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

Avatar for Quinn McHenry

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