Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Swift Bits v1 - Protocol Oriented Programming

Swift Bits v1 - Protocol Oriented Programming

Simplifying NSNotifications and Flurry Analytics through Protocol Oriented Programming

Quinn McHenry

September 23, 2015
Tweet

More Decks by Quinn McHenry

Other Decks in Programming

Transcript

  1. • Literal String names • Literal String keys in userInfo

    dictionary • Potential for mismatched strings NSNotification
  2. • Old school approach • Literal Strings everywhere • Hope

    for the best 1 // Old school approach 2 3 4 5 NSNotificationCenter.defaultCenter().addObserver(self, name:"SomeImportantNote", m selector:Selector("functionName"), object: scopeObj) 6 7 // Elsewhere in code 8 9 NSNotificationCenter.defaultCenter().postNotificationName("SomeimportantNote", object: scopeObj, userInfo: nil) NSNotification
  3. • A little better • Constant Strings for keys •

    Can we do better? 1 // Slightly less old school approach 2 3 let importantNoteName = "SomeImportantNote" 4 5 NSNotificationCenter.defaultCenter().addObserver(self, name:importantNoteName, m selector:Selector("functionName"), object: scopeObj) 6 7 // Elsewhere in code 8 9 NSNotificationCenter.defaultCenter().postNotificationName(importantNoteName, object: scopeObj, userInfo: nil) NSNotification
  4. Notification 1 enum Notification: String { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 } 1 // Old school 2 NSNotificationCenter.defaultCenter(). addObserver(self, name:"SomeImportantNote", selector:Selector("functionName"), object: scopeObj)
  5. Notification 1 enum Notification: String { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 } 1 // Old school 2 NSNotificationCenter.defaultCenter(). addObserver(self, name:"SomeImportantNote", selector:Selector("functionName"), object: scopeObj) 3 4 // Slightly less old school 5 NSNotificationCenter.defaultCenter(). addObserver(self, name:Notification.SomeImportantNote.rawValue, selector:Selector("functionName"), object: scopeObj)
  6. 1 // Old school 2 NSNotificationCenter.defaultCenter(). addObserver(self, name:"SomeImportantNote", selector:Selector("functionName"), object:

    scopeObj) 3 4 // Slightly less old school 5 NSNotificationCenter.defaultCenter(). addObserver(self, name:Notification.SomeImportantNote.rawValue, selector:Selector("functionName"), object: scopeObj) Is this an improvement?! Notification 1 enum Notification: String { 2 case SomeImportantNote 3 case SystemTimeChanged 4 }
  7. Notification 1 enum Notification: String { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 12 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 13 } 14 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 15 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 16 } 17 }
  8. Notification 1 // Usage 2 Notification.SomeImportantNote.observe(self, selector: Selector("functionName"), object: scopeObj)

    3 Notification.SomeImportantNote.post(object: scopeObj) 4 Notification.SomeImportantNote.post() 1 enum Notification: String { 2 case SomeImportantNote 3 case SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 12 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 13 } 14 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 15 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 16 } 17 }
  9. 1 enum Notification: String { 2 case SomeImportantNote 3 case

    SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 } 13 public protocol NotificationHandler { 14 var name: String { get } 15 func post(object: AnyObject?, userInfo: [NSObject : AnyObject]?) 16 func observe(observer: AnyObject, selector: Selector, object: AnyObject?) 17 } 19 public extension NotificationHandler { 20 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 21 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 22 } 23 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 24 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 25 } 26 } NotificationHandler
  10. 1 enum Notification: String { 2 case SomeImportantNote 3 case

    SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 } 13 public protocol NotificationHandler { 14 var name: String { get } 15 func post(object: AnyObject?, userInfo: [NSObject : AnyObject]?) 16 func observe(observer: AnyObject, selector: Selector, object: AnyObject?) 17 } 19 public extension NotificationHandler { 20 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 21 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 22 } 23 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 24 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 25 } 26 } NotificationHandler
  11. 1 enum Notification: String, NotificationHandler { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 } 13 public protocol NotificationHandler { 14 var name: String { get } 15 func post(object: AnyObject?, userInfo: [NSObject : AnyObject]?) 16 func observe(observer: AnyObject, selector: Selector, object: AnyObject?) 17 } 19 public extension NotificationHandler { 20 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 21 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 22 } 23 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 24 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 25 } 26 } NotificationHandler
  12. 1 enum Notification: String, NotificationHandler { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 } 13 public protocol NotificationHandler { 14 var name: String { get } 15 func post(object: AnyObject?, userInfo: [NSObject : AnyObject]?) 16 func observe(observer: AnyObject, selector: Selector, object: AnyObject?) 17 } 19 public extension NotificationHandler { 20 func post(object: AnyObject? = nil, userInfo: [NSObject : AnyObject]? = nil) { 21 NSNotificationCenter.defaultCenter().postNotificationName(name, object: object, userInfo: userInfo) 22 } 23 func observe(observer: AnyObject, selector: Selector, object: AnyObject? = nil) { 24 NSNotificationCenter.defaultCenter().addObserver(observer, selector: selector, name: name, object: object) 25 } 26 } NotificationHandler To PlanetSwift }
  13. 1 enum Notification: String, NotificationHandler { 2 case SomeImportantNote 3

    case SystemTimeChanged 4 5 var name: String { 6 switch self { 7 case .SystemTimeChanged: return NSSystemClockDidChangeNotification 8 default: return rawValue 9 } 10 } 11 } NotificationHandler 1 // Usage 2 Notification.SomeImportantNote.observe(self, selector: Selector("functionName"), object: scopeObj) 3 Notification.SomeImportantNote.post(object: scopeObj) 4 Notification.SomeImportantNote.post()
  14. Why POP? vs OOP 1 class NotificationHandler { 2 var

    name: String { 3 return "Override in subclass" 4 } 5 func post() { ... } 6 func observe() { ... } 7 } 8 9 class Notification: NotificationHandler { 10 override var name: String { 11 return generateName() 12 } 13 func generateName() -> String { ... } 14 }
  15. 1 class NotificationHandler { 2 var name: String { 3

    return "Override in subclass" 4 } 5 func post() { ... } 6 func observe() { ... } 7 } 8 9 class Notification: NotificationHandler { 10 override var name: String { 11 return generateName() 12 } 13 func generateName() -> String { ... } 14 } Why POP? vs OOP 1 protocol NotificationHandler { 2 var name: String { get } 3 func post() 4 func observe() 5 } 6 7 extension NotificaitonHandler { ... } 8 9 class Notification: NotificationHandler { 10 var name: String { 11 return generateName() 12 } 13 func generateName() -> String { ... } 14 }
  16. 1 protocol NotificationHandler { 2 var name: String { get

    } 3 func post() 4 func observe() 5 } 6 7 extension NotificaitonHandler { ... } 8 9 class Notification: NotificationHandler { } 10 11 enum Notification: NotificationHandler { } 12 13 struct Notification: NotificationHandler { } 1 class NotificationHandler { 2 var name: String { 3 return "Override in subclass" 4 } 5 func post() { ... } 6 func observe() { ... } 7 } 8 9 class Notification: NotificationHandler { 10 override var name: String { 11 return generateName() 12 } 13 func generateName() -> String { ... } 14 } Why POP? vs OOP
  17. Why POP? 1 extension String: NotificationHandler { 2 public var

    name: String { 3 return self 4 } 5 } 6 7 "note_this!".post() ACTUALLY WORKS!
  18. Flurry Analytics 1 protocol FlurryAnalytics { 2 static var apiKey:

    String { get } 3 var eventName: String { get } 4 var parameters: [NSObject: AnyObject] { get } 5 func logEvent(timed: Bool) 6 func endTimedEvent() 7 static func startSession() 8 }
  19. Flurry Analytics 1 protocol FlurryAnalytics { 2 static var apiKey:

    String { get } 3 var eventName: String { get } 4 var parameters: [NSObject: AnyObject] { get } 5 func logEvent(timed: Bool) 6 func endTimedEvent() 7 static func startSession() 8 } 9 10 extension FlurryAnalytics { 11 func logEvent(timed: Bool = false) { 12 Flurry.logEvent(eventName, withParameters: parameters, timed: timed) 13 } 15 func endTimedEvent() { 16 Flurry.endTimedEvent(eventName, withParameters: nil) 17 } 19 static func startSession() { 20 Flurry.startSession(apiKey) 21 Flurry.setDebugLogEnabled(false) 22 } 23 }
  20. Flurry Analytics 1 protocol FlurryAnalytics { 2 static var apiKey:

    String { get } 3 var eventName: String { get } 4 var parameters: [NSObject: AnyObject] { get } 5 func logEvent(timed: Bool) 6 func endTimedEvent() 7 static func startSession() 8 } 9 10 extension FlurryAnalytics { 11 func logEvent(timed: Bool = false) { 12 Flurry.logEvent(eventName, withParameters: parameters, timed: timed) 13 } 15 func endTimedEvent() { 16 Flurry.endTimedEvent(eventName, withParameters: nil) 17 } 19 static func startSession() { 20 Flurry.startSession(apiKey) 21 Flurry.setDebugLogEnabled(false) 22 } 23 } ← Handled by extension
  21. 1 enum Analytics: FlurryAnalytics { 2 static let apiKey =

    "QKNJDNXR3C4NQSNZT7Q5" 3 4 case DayView 5 case Renewable(type: String, taken: Bool) 6 case ResourcesView(resourceKey: String?) 7 8 var eventName: String { 9 switch self { 10 case DayView: return "Day view" 11 case Renewable(_,_): return "Renewable" 12 case ResourcesView: return "Resources view" 13 } 14 } 15 16 var parameters: [NSObject: AnyObject] { 17 switch self { 18 case Renewable(let type, let taken): 19 return ["Type":type, "State": taken ? "Taken" : "Missed"] 20 case ResourcesView(let resourceKey): 21 guard let resourceKey = resourceKey else { return [:] } 22 return ["key":resourceKey] 23 default: 24 return [:] 25 } 26 } 27 } Flurry Analytics 1 protocol FlurryAnalytics { 2 static var apiKey: String { get } 3 var eventName: String { get } 4 var parameters: [NSObject: AnyObject] { get }
  22. Flurry Analytics 1 enum Analytics: FlurryAnalytics { 2 static let

    apiKey = "QKNJDNXR3C4NQSNZT7Q5" 3 4 case DayView 5 case Renewable(type: String, taken: Bool) 6 case ResourcesView(resourceKey: String?) 7 8 var eventName: String { 9 switch self { 10 case DayView: return "Day view" 11 case Renewable(_,_): return "Renewable" 12 case ResourcesView: return "Resources view" 13 } 14 } 15 ... 1 Analytics.startSession() 2 Analytics.DayView.logEvent() 3 Analytics.Renewable(type: "Pill", taken: false).logEvent() 4 Analytics.ResourcesView.logEvent() 5 Analytics.ResourcesView("WhatTypeOfBC").logEvent(timed: true) 6 Analytics.ResourcesView("WhatTypeOfBC").endTimedEvent() #POP4EVA