有没有觉得你的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 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
}
}
}
看完后,有没有一个类就能完成整个应用的想法?今天,我们的目的就是使得AppDelegate这个类代码极限缩减。
如果大家有了解过微服务的话,大家就会知道,一个服务专职做一件事情,然后由网关来调度,这样的逻辑是非常清晰的,也非常便于维护,我们这次的改造也是源于这样的思路的。
按照上图,以后我们的AppDelegate只做网关对应的功能,其他具体业务,交由不同的服务去做,那么,我们应该如何实现这样的想法呢?
1.首先我们创建一个文件TDWApplicationDelegate.swift
里面的代码:
/// UIApplicationDelegate 协议扩展
public protocol TDWApplicationDelegate: UIApplicationDelegate {
}
这里定义了一个TDWApplicationDelegate,继承UIApplicationDelegate。这个协议是方便以后扩展用的。
2.我们再创建一个文件TDWAppDelegateService.swift
代码为:
import Foundation
open 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 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个服务,一个是初始化服务,一个是日志服务,他们都只做一件事件,打印相关的字符串。
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.最后,你就可以运行看结果了。
没错,服务按顺序执行对应的功能,也就是打印对应的字符串。
好了,以上就是本文要介绍的内容,欢迎评论反馈,谢谢!!