有沒有覺得你的AppDelegate雜亂無章静陈?代碼幾百行上千行燕雁?集成了無數(shù)的功能,如推送鲸拥、埋點(diǎn)贵白、日志統(tǒng)計、Crash統(tǒng)計等等崩泡,感覺AppDelegate無所不能。
來一段一般的AppDelegate代碼猬膨,來自網(wǎng)上一篇文章:
@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 Analytics
if !AppGlobal.userDefaults[.googleAnalyticsID].isEmpty {
GAI.sharedInstance().tracker(
withTrackingId: AppGlobal.userDefaults[.googleAnalyticsID])
}
// Declare data format from remote REST API
JSON.dateFormatter.dateFormat = ZamzamConstants.DateTime.JSON_FORMAT
// Initialize components
AppLogger.shared.setUp()
AppData.shared.setUp()
// Select home tab
(window?.rootViewController as? UITabBarController)?.selectedIndex = 2
setupTheme()
Log(info: "App finished launching.")
// Handle shortcut launch
if 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 = 0
case "search":
tabController.selectedIndex = 3
case "contact":
guard let url = URL(string: "mailto:\(AppGlobal.userDefaults[.email])") else { break }
UIApplication.shared.open(url)
default: break
}
completionHandler?(true)
}
}
// MARK: - User Notification Delegate
extension 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 interrupted
urlSessionManager = 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 exit
func deferred() {
// Launch notification
UNUserNotificationCenter.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 notification
guard let link = post.media?.thumbnailLink else { return deferred() }
let thread = Thread.current
UNNotificationAttachment.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 later
attachments.append(attachment)
}
}
}
}
// MARK: - Internal functions
private 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 bar
if 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 applicable
if AppGlobal.userDefaults[.darkMode] {
UINavigationBar.appearance().barStyle = .black
UITabBar.appearance().barStyle = .black
UICollectionView.appearance().backgroundColor = .black
UITableView.appearance().backgroundColor = .black
UITableViewCell.appearance().backgroundColor = .clear
}
}
}
看完后角撞,有沒有一個類就能完成整個應(yīng)用的想法?今天勃痴,我們的目的就是使得AppDelegate這個類代碼極限縮減谒所。
如果大家有了解過微服務(wù)的話,大家就會知道沛申,一個服務(wù)專職做一件事情劣领,然后由網(wǎng)關(guān)來調(diào)度,這樣的邏輯是非常清晰的铁材,也非常便于維護(hù)尖淘,我們這次的改造也是源于這樣的思路的。
按照上圖著觉,以后我們的AppDelegate只做網(wǎng)關(guān)對應(yīng)的功能村生,其他具體業(yè)務(wù),交由不同的服務(wù)去做饼丘,那么趁桃,我們應(yīng)該如何實現(xiàn)這樣的想法呢?
1.首先我們創(chuàng)建一個文件TDWApplicationDelegate.swift
里面的代碼:
/// UIApplicationDelegate 協(xié)議擴(kuò)展
public protocol TDWApplicationDelegate: UIApplicationDelegate {
}
這里定義了一個TDWApplicationDelegate,繼承UIApplicationDelegate卫病。這個協(xié)議是方便以后擴(kuò)展用的油啤。
2.我們再創(chuàng)建一個文件TDWAppDelegateService.swift
代碼為:
import Foundation
open class TDWAppDelegateService: UIResponder, TDWApplicationDelegate {
/// 啟動服務(wù)的數(shù)組
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: - 其他應(yīng)用喚起
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: - 推送相關(guān)
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相關(guān)
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.定義了一個服務(wù)數(shù)組益咬,把服務(wù)都統(tǒng)一管理。
2.在extension里面實現(xiàn)常用的AppDelegate生命周期的協(xié)議屹逛。因為__services里面的服務(wù)都是繼承于TDWApplicationDelegate础废,所以,沒有服務(wù)罕模,其實能實現(xiàn)AppDelegate生命周期评腺。所以,在這個TDWAppDelegateService上淑掌,我在他所有的生命周期里同步遍歷調(diào)用所有服務(wù)__services的對等生命周期蒿讥,這樣,就變相于我收到系統(tǒng)的信息后抛腕,會同步給各個服務(wù)芋绸,讓他們自己處理了。
這樣担敌,我們就完成了整個服務(wù)的框架了摔敛。那么,我們?nèi)绾问褂媚兀?br> 這里全封,我以2個服務(wù)作為例子马昙,當(dāng)然,你可以構(gòu)建10個刹悴,只要你喜歡行楞。
import TDWAppDelegateService
class 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個服務(wù),一個是初始化服務(wù)土匀,一個是日志服務(wù)子房,他們都只做一件事件,打印相關(guān)的字符串就轧。
ok证杭,下面我們構(gòu)建下我們的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方法躯砰,把服務(wù)實例放到__services數(shù)組就可以了。
3.最后携丁,你就可以運(yùn)行看結(jié)果了琢歇。
沒錯兰怠,服務(wù)按順序執(zhí)行對應(yīng)的功能,也就是打印對應(yīng)的字符串李茫。
好了揭保,以上就是本文要介紹的內(nèi)容,歡迎評論反饋魄宏,謝謝=章隆!