基于Protocol的log实现
Martin Lv1

写在前面
苹果在今年WWDC的时候称swift 2.0为面向协议的语言(Protocol-Oriented Programming)。Dave 用了45分钟的时间生动的阐述了什么是POP, 为什么要这么做。没看过的小伙伴建议去看下。

什么是协议(protocol)

协议这个概念在Objective-C中就存在了。所谓协议其实就是一系列可以调用方法的结合。在我们调用的时候就可以将注意力集中在方法本身而不是类的实现。苹果在swift 2.0里面给protocol赋予了更加强大的功能。protocol能够被直接扩展。这样prototol的使用更加灵活方便了。
正题来咯
基于protocol可以被直接扩展的特性,我们可以很方便的实现一些经常被复用的代码。比如上传事件到一些数据分析平台上去。我这里用google analytics作为例子。
让我们先来看下之前是怎么做的。
上传事件在程序中会被调用很多次,为了数据统计的准确性我们会在每个action里面都汇报到server上去。一般会有两种方法。
直接调用sdk的方法
这种一般都是比较简单的sdk,像Yahoo的Flurry,他的方法只有一行Flurry.LogEvent(“message”)对于这类的请求没有必要单独写一个类方法去实现。
写一个类方法,然后调用
有些统计的sdk的实现要稍微多一些,比如google analytics。他的代码看起来是这样的

1
2
3
4
5
Google analytics code
var tracker = GAI.sharedInstance().defaultTracker
tracker.set(kGAIScreenName, value: name)
var builder = GAIDictionaryBuilder.createScreenView()
tracker.send(builder.build() as [NSObject : AnyObject])

这个代码就有些多,或者我们想同时向几个分析平台发送事件的时候,一般都会写个类方法封装一下。然后用这样的方式在每个需要的地方调用。

Log.sendEvent("category", action: "action", label: "label", value: "value")

现在我们可以用更加优雅的方式实现
Swift 2.0 以后我们可以用另外一种方式来实现上面第二种方法。
定义一个protocol

MTLog {
1
2
3
4
5
     func logEvent(category: String, action: String, label: String?, value: NSNumber?)
}```

扩展protocol,实现默认实现

extension MTLog {
func logEvent(category: String, action: String, label: String? = nil, value: NSNumber? = nil ) {

//Google analytics code

let tracker = GAI.sharedInstance().defaultTracker
let builder = GAIDictionaryBuilder.createEventWithCategory(category, action: action, label: Label, value: Value)
tracker.send(builder.build() as [NSObject : AnyObject])

}
}

1
2
3
4
5
6
7
8
9
10
11
12

调用方法
现在我们可以用一种优雅的方式来调用了

```logEvent("category",action:"action",label:"label",value:1)```

如果不需要后面的参数也可以写成

```logEvent("category",action:"action")```

很多时候我们会将同一个view实现的事件作为同一个category来对待。为了避免错误,我们可以在定义protocol的时候定义一个变量作为一个view的默认category。代码可以修改为:

protocol MTLog {
var category: String {get}
func logEvent(action: String, label: String?, value: NSNumber?)
}
extension MTLog {
func logEvent(category: String, action: String, label: String? = nil, value: NSNumber? = nil ) {

//Google analytics code

let tracker = GAI.sharedInstance().defaultTracker
let builder = GAIDictionaryBuilder.createEventWithCategory(category, action: action, label: Label, value: Value)
tracker.send(builder.build() as [NSObject : AnyObject])

}
}

1
2
3
4
5
6
7

这样我们就不用担心因为typo的问题导致最终的统计结果出现偏差了。
更为强大来了
有人会说这样的修改并没有给我们开发带来多大的便利。那么下面这个特性才是protocol的核心了。
我们的程序会有很多popup的信息,错误信息,版本提示信息等等等等。这时候我们不得不在每个需要用到的viewcontroller中都实现一遍。想不重复代码? 可以! 要么我们写一个UIViewController的基类。要么将viewController作为参数传进函数中。
现在我们可以这么做:

extension MTLog where Self: UIViewController {
func errorHandle(error: String) {

  let alertController = UIAlertController(title: nil, message: error, preferredStyle: .Alert)
      let cancelAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
  alertController.addAction(cancelAction)

  self.presentViewController(alertController, animated: true, completion: nil)

}
}

```

这样就实现一个简单可复用的提示方法。而且这个方法只在继承与UIViewController的类才有效。这样就不需要实现一个难以控制的基类了。
这只是基于protocol的一个简单应用。还有其他的我们可以探讨。
代码我已经上传到github上了。有需要的小伙伴可以下来看看。