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.mzph.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;…