为什么要抽象任何iOS第三方库

如果您有iOS应用,则可能已集成了外部库和工具来帮助您更快地准备好产品。 但是,您的iOS体系结构和快速代码不应依赖于这些库。

与许多iOS开发人员一样,您已经为应用程序实现了外部服务。 它可能是诸如Stripe之类的支付系统,诸如NewRelic或Crashlytics之类的监控工具,也可能是诸如Google Analytics(分析)之类的跟踪平台。

这些工具很棒,但是,现在您必须切换到新服务。 这是否意味着您有数小时来重构方法? 您是否需要重写数百行代码? 如果是,那么这就是我们应该始终为外部服务抽象您的实现的原因。

在此演示中,我将展示如何通过Firebase集成将其切换到Fabric来记录事件。 这种方法包括如何抽象化实现以将第三方库包含到我们的代码中,并使其更易于迁移。

为了准备用于迁移的代码,我们需要抽象一些元素:要发送的事件,记录器,以及可能还需要经理来使所有内容保持单一状态。

假设我们有3个经典事件,分别登陆首页,注册和登录。我的视图可能如下所示

import UIKit 
import Firebase
import UIKit
import Firebase
class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
}
class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
}
class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
FirebaseApp.configure()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Analytics.logEvent("homepage", parameters: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Analytics.logEvent("homepage", parameters: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Analytics.logEvent("homepage", parameters: nil)
}
func didLogin() {
Analytics.logEvent("login", parameters: ["userID" : "1234567890ABC"])
}
func didLogin() {
Analytics.logEvent("login", parameters: ["userID" : "1234567890ABC"])
}
func didLogin() {
Analytics.logEvent("login", parameters: ["userID" : "1234567890ABC"])
}
func didSignUp() {
Analytics.logEvent("signup", parameters: ["userID" : "1234567890ABC"])
}
}
func didSignUp() {
Analytics.logEvent("signup", parameters: ["userID" : "1234567890ABC"])
}
}

这就是我们要避免的事情。 在这里,如果我们换了新工具,我们要做很多工作,每页可能使用相同的方法。

让我们抽象一下Firebase实现

 enum Events : String { 
case login
case home
case sign_up
}
enum Events : String {
case login
case home
case sign_up
}
protocol LoggerProtocol {
func setup()
func log(event: Events, parameters: [String: Any]?)
}
protocol LoggerProtocol {
func setup()
func log(event: Events, parameters: [String: Any]?)
}

在这里,我只是设计了一些我想识别的事件,以及一个使它在整个应用程序中通用的协议。 我准备以正确的方式重新实现Firebase。

 import Firebase 
struct FirebaseLogger : LoggerProtocol {
func setup() {
FirebaseApp.configure()
}
import Firebase
struct FirebaseLogger : LoggerProtocol {
func setup() {
FirebaseApp.configure()
}
func log(event: Events, parameters: [String : Any]? = nil) {
Analytics.logEvent(event.rawValue, parameters: parameters)
}
}
func log(event: Events, parameters: [String : Any]? = nil) {
Analytics.logEvent(event.rawValue, parameters: parameters)
}
}

我还创建了一个LoggerManager,以使每个设置和记录器都处于同一保护之下,并使用单例来访问它。

 class LoggerManager : LoggerProtocol { 
static let shared : LoggerManager = {
var manager = LoggerManager()
manager.loggers.append(FirebaseLogger())
manager.setup() return manager
}()
class LoggerManager : LoggerProtocol {
static let shared : LoggerManager = {
var manager = LoggerManager()
manager.loggers.append(FirebaseLogger())
manager.setup() return manager
}()
private var loggers : [LoggerProtocol] = []
internal func setup() {
self.loggers.forEach({ $0.setup() })
}
private var loggers : [LoggerProtocol] = []
internal func setup() {
self.loggers.forEach({ $0.setup() })
}
private var loggers : [LoggerProtocol] = []
internal func setup() {
self.loggers.forEach({ $0.setup() })
}
func log(event: Events, parameters: [String : Any]? = nil) {
self.loggers.forEach({ $0.log(event: event, parameters: parameters) })
}
}
func log(event: Events, parameters: [String : Any]? = nil) {
self.loggers.forEach({ $0.log(event: event, parameters: parameters) })
}
}

现在,让我们更新视图,从工具本身中删除尽可能多的依赖项。

 class ViewController: UIViewController { 
override func viewDidLoad() {
super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib.
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
LoggerManager.shared.log(event: .home)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
LoggerManager.shared.log(event: .home)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
LoggerManager.shared.log(event: .home)
}
func didLogin() {
LoggerManager.shared.log(event: .login, parameters: ["userID" : "1234567890ABC"])
}
func didLogin() {
LoggerManager.shared.log(event: .login, parameters: ["userID" : "1234567890ABC"])
}
func didLogin() {
LoggerManager.shared.log(event: .login, parameters: ["userID" : "1234567890ABC"])
}
func didSignUp() {
LoggerManager.shared.log(event: .signup, parameters: ["userID" : "1234567890ABC"])
}
}
func didSignUp() {
LoggerManager.shared.log(event: .signup, parameters: ["userID" : "1234567890ABC"])
}
}

看起来我们很高兴,在我们看来不再依赖Firebase。

我们很高兴迁移到任何新工具。

这是我的面料记录器

 import Fabric 
import Answers
import Fabric
import Answers
struct FabricLogger : LoggerProtocol {
func setup() {
Fabric.with([Answers.self])
}
struct FabricLogger : LoggerProtocol {
func setup() {
Fabric.with([Answers.self])
}
struct FabricLogger : LoggerProtocol {
func setup() {
Fabric.with([Answers.self])
}
func log(event: Events, parameters: [String : Any]?) {
Answers.logCustomEvent(withName: event.rawValue, customAttributes: parameters)
}
}
func log(event: Events, parameters: [String : Any]?) {
Answers.logCustomEvent(withName: event.rawValue, customAttributes: parameters)
}
}

不要忘了更新LoggerManager,删除Fabric的Firebase One。

 static let shared : LoggerManager = { 
var manager = LoggerManager()
manager.loggers.append(FabricLogger())
manager.setup() return manager
}()

如您所见,代码像以前一样工作,我们不必更新视图端。

总之 ,我们设法抽象化第三方库集成,以使其易于维护和重用。 实际上,我们可以将Firebase和Fabric保持在一起,如果有特定的方法,可以限制它们的日志记录。

我的主要目的是向您展示如何避免iOS项目和快速代码依赖于其他工具。 如果不能说服,另一个很好的理由是节省时间。 您不想每年花费几天来重新实现新工具,具体取决于市场上的新事物。


最初发布在 http://benoitpasquier.com/abstract-ios-third-party-libraries/上