AppDelegate瘦身之服務化

有沒有覺得你的AppDelegate雜亂無章?代碼幾百行上千行?集成了無數的功能,如推送、埋點、日志統計、Crash統計等等,感覺AppDelegate無所不能。

來一段一般的AppDelegate代碼,來自網上一篇文章:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {var window: UIWindow?func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {Log(info: "AppDelegate.didFinishLaunchingSite started.")application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)UNUserNotificationCenter.current().register(delegate: self,actions: [UNNotificationAction(identifier: "favorite", title: .localized(.favorite))])// Initialize Google Analyticsif !AppGlobal.userDefaults[.googleAnalyticsID].isEmpty {GAI.sharedInstance().tracker(withTrackingId: AppGlobal.userDefaults[.googleAnalyticsID])}// Declare data format from remote REST APIJSON.dateFormatter.dateFormat = ZamzamConstants.DateTime.JSON_FORMAT// Initialize componentsAppLogger.shared.setUp()AppData.shared.setUp()// Select home tab(window?.rootViewController as? UITabBarController)?.selectedIndex = 2setupTheme()Log(info: "App finished launching.")// Handle shortcut launchif let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem {performActionForShortcutItem(application, shortcutItem: shortcutItem)return false}return true}func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let webpageURL = userActivity.webpageURL else { return false }Log(info: "AppDelegate.continueUserActivity for URL: \(webpageURL.absoluteString).")return navigateByURL(webpageURL)}func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {Log(info: "AppDelegate.performFetch started.")scheduleUserNotifications(completionHandler: completionHandler)}func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {window?.rootViewController?.dismiss(animated: false, completion: nil)guard let tabController = window?.rootViewController as? UITabBarController else { completionHandler?(false); return }switch shortcutItem.type {case "favorites":tabController.selectedIndex = 0case "search":tabController.selectedIndex = 3case "contact":guard let url = URL(string: "mailto:\(AppGlobal.userDefaults[.email])") else { break }UIApplication.shared.open(url)default: break}completionHandler?(true)}
}// MARK: - User Notification Delegateextension AppDelegate {func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {guard let id = response.notification.request.content.userInfo["id"] as? Int,let link = response.notification.request.content.userInfo["link"] as? String,let url = try? link.asURL()else { return }switch response.actionIdentifier {case UNNotificationDefaultActionIdentifier: _ = navigateByURL(url)case "favorite": PostService().addFavorite(id)case "share": _ = navigateByURL(url)default: break}completionHandler()}private func scheduleUserNotifications(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {// Get latest posts from server// Persist network manager instance to ensure lifespan is not interruptedurlSessionManager = PostService().updateFromRemote {guard case .success(let results) = $0 else { return completionHandler(.failed) }guard let id = results.created.first,let post = (try? Realm())?.object(ofType: Post.self, forPrimaryKey: id)else { return completionHandler(.noData) }var attachments = [UNNotificationAttachment]()// Completion process on exitfunc deferred() {// Launch notificationUNUserNotificationCenter.current().add(body: post.previewContent,title: post.title,attachments: attachments,timeInterval: 5,userInfo: ["id": post.id,"link": post.link],completion: {guard $0 == nil else { return Log(error: "Could not schedule the notification for the post: \($0.debugDescription).") }Log(debug: "Scheduled notification for post during background fetch.")})completionHandler(.newData)}// Get remote media to attach to notificationguard let link = post.media?.thumbnailLink else { return deferred() }let thread = Thread.currentUNNotificationAttachment.download(from: link) {defer { thread.async { deferred() } }guard $0.isSuccess, let attachment = $0.value else {return Log(error: "Could not download the post thumbnail (\(link)): \($0.error.debugDescription).")}// Store attachment to schedule notification laterattachments.append(attachment)}}}
}// MARK: - Internal functionsprivate extension AppDelegate {func setupTheme() {window?.tintColor = UIColor(rgb: AppGlobal.userDefaults[.tintColor])if !AppGlobal.userDefaults[.titleColor].isEmpty {UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor(rgb: AppGlobal.userDefaults[.titleColor])]}// Configure tab barif let controller = window?.rootViewController as? UITabBarController {controller.tabBar.items?.get(1)?.image = UIImage(named: "top-charts", inBundle: AppConstants.bundle)controller.tabBar.items?.get(1)?.selectedImage = UIImage(named: "top-charts-filled", inBundle: AppConstants.bundle)controller.tabBar.items?.get(2)?.image = UIImage(named: "explore", inBundle: AppConstants.bundle)controller.tabBar.items?.get(2)?.selectedImage = UIImage(named: "explore-filled", inBundle: AppConstants.bundle)if !AppGlobal.userDefaults[.tabTitleColor].isEmpty {UITabBarItem.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor(rgb: AppGlobal.userDefaults[.tabTitleColor])], for: .selected)}}// Configure dark mode if applicableif AppGlobal.userDefaults[.darkMode] {UINavigationBar.appearance().barStyle = .blackUITabBar.appearance().barStyle = .blackUICollectionView.appearance().backgroundColor = .blackUITableView.appearance().backgroundColor = .blackUITableViewCell.appearance().backgroundColor = .clear}}
}
復制代碼

看完后,有沒有一個類就能完成整個應用的想法?今天,我們的目的就是使得AppDelegate這個類代碼極限縮減。

如果大家有了解過微服務的話,大家就會知道,一個服務專職做一件事情,然后由網關來調度,這樣的邏輯是非常清晰的,也非常便于維護,我們這次的改造也是源于這樣的思路的。

按照上圖,以后我們的AppDelegate只做網關對應的功能,其他具體業務,交由不同的服務去做,那么,我們應該如何實現這樣的想法呢?

1.首先我們創建一個文件TDWApplicationDelegate.swift 里面的代碼:

/// UIApplicationDelegate 協議擴展
public protocol TDWApplicationDelegate: UIApplicationDelegate {}
復制代碼

這里定義了一個***TDWApplicationDelegate***,繼承***UIApplicationDelegate***。這個協議是方便以后擴展用的。

2.我們再創建一個文件TDWAppDelegateService.swift 代碼為:

import Foundationopen class TDWAppDelegateService: UIResponder, TDWApplicationDelegate {/// 啟動服務的數組open var __services:[TDWApplicationDelegate] = []
}// MARK: - 啟動
extension TDWAppDelegateService {open func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {__services.forEach {_ = $0.application?(application, didFinishLaunchingWithOptions: launchOptions)}return true}
}// MARK: - 其他應用喚起
extension TDWAppDelegateService {// iOS 9.0 及以下open func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {__services.forEach {_ = $0.application?(application, open: url, sourceApplication: sourceApplication, annotation: annotation)}return true}// iOS 9.0 以上open func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {if #available(iOS 9.0, *) {__services.forEach {_ = $0.application?(app, open: url, options: options)}return true}else {return false}}
}// MARK: - 前后臺
extension TDWAppDelegateService {open func applicationWillEnterForeground(_ application: UIApplication) {__services.forEach { $0.applicationWillEnterForeground?(application) }}open func applicationDidEnterBackground(_ application: UIApplication) {__services.forEach { $0.applicationDidEnterBackground?(application) }}open func applicationDidBecomeActive(_ application: UIApplication) {__services.forEach { $0.applicationDidBecomeActive?(application) }}open func applicationWillResignActive(_ application: UIApplication) {__services.forEach { $0.applicationWillResignActive?(application) }}open func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {__services.forEach{ $0.application?(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)}}
}// MARK: - 退出
extension TDWAppDelegateService {open func applicationWillTerminate(_ application: UIApplication) {__services.forEach { $0.applicationWillTerminate?(application) }}open func applicationDidReceiveMemoryWarning(_ application: UIApplication) {__services.forEach { $0.applicationDidReceiveMemoryWarning?(application) }}
}// MARK: - 推送相關
extension TDWAppDelegateService {open func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {__services.forEach { $0.application?(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken) }}open func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {__services.forEach { $0.application?(application, didFailToRegisterForRemoteNotificationsWithError: error) }}// NS_AVAILABLE_IOS(7_0);open func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {__services.forEach { $0.application?(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)}}
}// MARK: - 3DTouch相關
extension TDWAppDelegateService {@available(iOS 9.0, *)open func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {__services.forEach { $0.application?(application, performActionFor: shortcutItem, completionHandler: completionHandler) }}
}
復制代碼

這個是本文的核心類,他主要做了些什么事情呢?

1.定義了一個服務數組,把服務都統一管理。 2.在extension里面實現常用的AppDelegate生命周期的協議。因為***__services***里面的服務都是繼承于***TDWApplicationDelegate***,所以,沒有服務,其實能實現AppDelegate生命周期。所以,在這個***TDWAppDelegateService***上,我在他所有的生命周期里同步遍歷調用所有服務***__services***的對等生命周期,這樣,就變相于我收到系統的信息后,會同步給各個服務,讓他們自己處理了。

這樣,我們就完成了整個服務的框架了。那么,我們如何使用呢? 這里,我以2個服務作為例子,當然,你可以構建10個,只要你喜歡。

import TDWAppDelegateServiceclass TDWInitializeService: NSObject, TDWApplicationDelegate {func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {print("TDWInitializeService")return true}
}class TDWLogService: NSObject, TDWApplicationDelegate {func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {print("TDWLogService")return true}
}
復制代碼

這里有2個服務,一個是初始化服務,一個是日志服務,他們都只做一件事件,打印相關的字符串。

ok,下面我們構建下我們的AppDelegate:

import UIKit
import TDWAppDelegateService@UIApplicationMain
class AppDelegate: TDWAppDelegateService {var window: UIWindow?override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {// Override point for customization after application launch.__services = [TDWInitializeService(), TDWLogService()]return super.application(application, didFinishLaunchingWithOptions: launchOptions)}}
復制代碼

AppDelegate非常簡潔,他只有短短幾句代碼。 1.首先AppDelegate繼承于***TDWAppDelegateService*** 2.然后重載***didFinishLaunchingWithOptions***方法,把服務實例放到***__services***數組就可以了。 3.最后,你就可以運行看結果了。

沒錯,服務按順序執行對應的功能,也就是打印對應的字符串。

好了,以上就是本文要介紹的內容,歡迎評論反饋,謝謝!!

轉載于:https://juejin.im/post/5cb97755518825328e00aac2

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251897.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251897.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251897.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第四章:手機平板要兼顧-探究碎片

碎片是什么? 碎片(Fragment)是一種可以嵌入在活動(Activity)中的 UI 片段,它能讓程序更加合理和充分的利用大屏幕的空間,因而在平板上應用的非常廣泛。 碎片的使用方式 靜態嵌入動態加載碎片和活…

Android Studio 3.4增可視化資源管理工具 可管理和預覽項目資源

經過6個月的開發時間,網絡大廠17日發布了最新版的App開發IDE Android Studio 3.4,現在就能夠下載使用,除了有超過300個錯誤修護和穩定度增強之外,在開發、建置和測試App階段,都推出了一些小的新功能和工具,…

Python安裝、使用MySQL數據庫

本機安裝的python版本為Python 2.7(win32 bit) 從http://www.codegood.com/archives/129下載MySQL-python-1.2.3.win32-py2.7.exe,點擊安裝 如果是win版還需要下載:libguide40.dll 和 libmmd.dll這兩個文件,下載后放入到到C:\WINDOWS/syste…

pytorch 安裝

安裝pytorch時,官網不能選擇版本。原以為是瀏覽器問題,換了幾個瀏覽器都不行。 后來FQ之后,就能選擇版本了。 sudo pip install torch torchvision轉載于:https://www.cnblogs.com/rabitvision/p/8908757.html

《JavaScript 高級程序設計》精讀筆記

本系列讀書筆記是我通過學習《Javascript 高級程序設計》第3版時結合自己的理解、概括、精煉然后加以一定的拓展,總結而來的,非常適合具有一定基礎,同時又想把 JS 基礎學更好的童鞋,當然更希望得到大家的反饋于建議,比…

struts2實現文件查看、下載

CreateTime--2017年9月7日10:25:33 Author:Marydon struts2實現文件查看、下載 1.界面展示 <a style"color: #199ED8;" target"_blank" href"<c:url value"/telemedicine/reseCons/viewFile.do?fileName201516529IO.jpg"/>"…

css文本設置

常用的應用文本的css樣式&#xff1a; color 設置文字的顏色&#xff0c;如&#xff1a; color:red; font-size 設置文字的大小&#xff0c;如&#xff1a;font-size:12px; font-family 設置文字的字體&#xff0c;如&#xff1a;font-family:微軟雅黑; font-style 設置字體…

關鍵字static

原文出處&#xff1a;http://cmsblogs.com/ 『chenssy』 一、 static代表著什么 在Java中并不存在全局變量的概念&#xff0c;但是我們可以通過static來實現一個“偽全局”的概念&#xff0c;在Java中static表示“全局”或者“靜態”的意思&#xff0c;用來修飾成員變量和成員方…

[IoC容器Unity]第三回:依賴注入

上節介紹了&#xff0c;Unity的Lifetime Managers生命周期&#xff0c;Unity具體實現依賴注入包含構造函數注入、屬性注入、方法注入&#xff0c;所謂注入相當賦值&#xff0c;下面一個一個來介紹。 2.構造函數注入 Unity利用Resolve方法解析一個對象&#xff0c;都是調用注冊類…

Apache CarbonData 1.5.0編譯及安裝

2019獨角獸企業重金招聘Python工程師標準>>> 一、編譯環境描述 OpenStack創建五個虛擬機&#xff0c;其中1個主節點&#xff08;hostname為bigdatamaster&#xff09;&#xff0c;4個從節點&#xff08;hostname分別為&#xff0c;bigdataslave1、bigdataslave2、bi…

JS控制網頁全屏

在谷歌&#xff0c;IE等瀏覽器中&#xff0c;點擊F11按鍵會進入網頁全屏模式&#xff0c;如同看電影的劇場模式&#xff0c;這個在代碼中可以通過JS來實現&#xff0c;簡單說下在實現這個需求后的個人總結&#xff1a; 底層網頁是已經加載完畢的&#xff0c;這時我們需要的全屏…

HDU 3966-Aragorn's Story 樹鏈剖分+樹狀數組

題目鏈接 題意&#xff1a;有一棵樹&#xff0c;每個節點有權值 有三種操作&#xff1a; I c1 c2 k 從節點c1到節點c2的路徑上每個節點權值增加kD c1 c2 k 從節點c1到節點c2的路徑上每個節點權值減少kQ i 查詢節點i的權值是多少思路&#xff1a; 樹鏈剖分處理出來的鏈放在數組中…

Filter介紹

Filter 可認為是 Servlet的一種 “ 加強版 ”&#xff0c;它主要用于對用戶請求進行預處理&#xff0c; 也可以對HttpServletResponse 進行后處理&#xff0c;是個典型的處理鏈。Filter 也可對用戶請求生成響應&#xff0c;這一 點與Servlet 相同&#xff0c; 但實際上很少會使…

LeetCode算法題-Jewels and Stones(Java實現)

這是悅樂書的第313次更新&#xff0c;第334篇原創 01 看題和準備 今天介紹的是LeetCode算法題中Easy級別的第182題&#xff08;順位題號是771&#xff09;。字符串J代表珠寶&#xff0c;S代表你擁有的石頭。S中的每個字符都是你擁有的一種石頭。計算S中有多少石頭也是珠寶。J中…

python --- 二分查找算法

二分查找法&#xff1a;在我的理解中這個查找方法為什么會叫二分呢&#xff0c;我認為是將要查詢的一個列表分成了兩份&#xff0c;然后在利用某個值來進行比較&#xff0c;在一個不斷循環的過程中來找出我們要找的某一個值。 廢話不多說&#xff0c;先上代碼&#xff1a; 1 de…

面試題

1. block 的作用由來&#xff0c;跟delegate的區別。 2. swift 的枚舉。 3. iOS保存一個對象。轉載于:https://www.cnblogs.com/studyNT/p/7499779.html

ssm框架下文件上傳

springmvc實現文件上傳的步驟&#xff1a; 1.頁面上&#xff0c;通過input來準備file組件&#xff0c;該標簽&#xff0c;必須給定name屬性值 同時&#xff0c;要求form表單必須給定一個屬性&#xff1a;enctype"multipart/form-data" 2.在pom.xml文件中&#xff0c;…

MySQL via EF6 的試用報告

MySQL via EF6 的試用報告1、如何通過 EF6 來連接 MySQL&#xff1f;2、如何通過 EF6 來實現 CRUD&#xff1f;2.1、Create 添加2.2、Retrieve 查詢2.3、Update 修改2.4、Delete 刪除3、如何更好的運用 EF6 來完成工作&#xff1f;3.1、傳說中 EF 的三種模式3.2、EF6 執行原生 …

Java暑假作業

一.《大護法》觀影有感 ... 從預告開始就期待著這部影片&#xff0c;在看過一遍后又忍不住二刷&#xff0c;影片觀看至第二遍后&#xff0c;對于全片的脈絡也更清晰了一點&#xff0c;雖然打著暴力美學的旗子&#xff0c;但《大護法》偏偏更文藝一些。文藝片是沒有對錯的&a…

使用EasyNetQ組件操作RabbitMQ消息隊列服務

RabbitMQ是一個由erlang開發的AMQP(Advanved Message Queue)的開源實現&#xff0c;是實現消息隊列應用的一個中間件&#xff0c;消息隊列中間件是分布式系統中重要的組件&#xff0c;主要解決應用耦合&#xff0c;異步消息&#xff0c;流量削鋒等問題。實現高性能&#xff0c;…