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

Swift Solutions

Swift Solutions

In this talk we start by looking at where we came from with Objective-C, and how we feel about Swift. This presentation is about trying to discover what idiomatic Swift is by looking at a series of examples with "Swift" solutions.

023a6a37e8177cb2f84a236bbce643cf?s=128

Ben Scheirman

April 10, 2015
Tweet

Transcript

  1. None
  2. @subdigital

  3. None
  4. None
  5. Objec&ve(C

  6. [ ]

  7. 2009

  8. None
  9. None
  10. C#

  11. BCSItem *item = [[BCSItem alloc] initWithIdentifier:@"id1"]; [item favorite];

  12. °□°#

  13. NSFileManager *fm = [NSFileManager defaultManager]; NSError *error = nil; BOOL

    result = [fm removeItemAtPath:path error:&error]; if (!result) { NSLog(“The error was: %@“, [error localizedDescription]); }
  14. !

  15. Stockholm)Syndrome?

  16. Objec&ve(C*has*some*really*great*features.

  17. None
  18. 2014

  19. None
  20. Swi$%is%different.

  21. Swi$%is • clean'&'concise • memory'managed • safe • func3onal'(?)

  22. Why$do$we$need$a$safe$language?

  23. remember%goto%fail?

  24. no?

  25. hashOut.data = hashes + SSL_MD5_DIGEST_LEN; hashOut.length = SSL_SHA1_DIGEST_LEN; if ((err

    = SSLFreeBuffer(&hashCtx)) != 0) goto fail; if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; err = sslRawVerify(...); . . .
  26. hashOut.data = hashes + SSL_MD5_DIGEST_LEN; hashOut.length = SSL_SHA1_DIGEST_LEN; if ((err

    = SSLFreeBuffer(&hashCtx)) != 0) goto fail; if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; <---------- if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; err = sslRawVerify(...); . . .
  27. Hidden&backdoor&to root$in$OS$X !! !!!h#ps:/ /truesecdev.wordpress.com/2015/04/09/hidden;backdoor;api;to;root;privileges;in;apple;os;x/

  28. ! !!h#ps:/ /twi#er.com/tlrobinson/status/586202234582507520

  29. “Swi%&Sucks”

  30. “What&the&hell&were&they&thinking?”

  31. "Let’s'implement'our'own'open1 source'Swi4"

  32. None
  33. Swi$%is%unique

  34. Idioma'c)Swi,

  35. None
  36. We#are#s'll#learning#what#idioma'c# Swi4#is.

  37. So#let’s#take#a#look#at#some#cases#from#a#Swi1# viewpoint.

  38. Lazy%Proper+es%with%Closures

  39. func viewDidLoad() { super.viewDidLoad() let config = NSURLSessionConfiguration.defaultSessionConfiguration() session =

    NSURLSession( configuration: config, delegate: self, delegateQueue: nil) navigationItem.leftBarButtonItem = UIBarButtonItem( barButtonSystemItem: .Bookmarks, target: self, action: "bookmarks:") navigationItem.rightBarButtonItem = UIBarButtonItem( barButtonSystemItem: .Add, target: self, action: "add:") ... }
  40. @interface MyViewController () @property (nonatomic, strong) NSURLSessionConfiguration *configuration; @end -

    (NSURLSessionConfiguration *)configuration { if (_configuration == nil) { _configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; } return _configuration; }
  41. lazy var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

  42. lazy var configuration = NSURLSessionConfiguration.defaultSessionConfiguration() lazy var session = ...

  43. lazy var configuration = NSURLSessionConfiguration.defaultSessionConfiguration() lazy var session: NSURLSession =

    { }()
  44. lazy var configuration = NSURLSessionConfiguration.defaultSessionConfiguration() lazy var session: NSURLSession =

    { return NSURLSession(configuration: self.configuration, delegate: self, delegateQueue: nil) }()
  45. lazy var configuration = NSURLSessionConfiguration.defaultSessionConfiguration() lazy var session: NSURLSession =

    { return NSURLSession(configuration: self.configuration, delegate: self, delegateQueue: nil) }() lazy var leftBarButtonItem: UIBarButtonItem = { return UIBarButtonItem( barButtonSystemItem: .Bookmarks, target: self, action: "bookmarks:") }() lazy var rightBarButtonItem: UIBarButtonItem = { return UIBarButtonItem( barButtonSystemItem: .Add, target: self, action: "add:") }()
  46. func viewDidLoad() { super.viewDidLoad() let config = NSURLSessionConfiguration.defaultSessionConfiguration() session =

    NSURLSession( configuration: config, delegate: self, delegateQueue: nil) navigationItem.leftBarButtonItem = UIBarButtonItem( barButtonSystemItem: .Bookmarks, target: self, action: "bookmarks:") navigationItem.rightBarButtonItem = UIBarButtonItem( barButtonSystemItem: .Add, target: self, action: "add:") ... }
  47. func viewDidLoad() { super.viewDidLoad() navigationItem.leftBarButtonItem = leftBarButtonItem navigationItem.rightBarButtonItem = rightBarButtonItem

    }
  48. Returning)errors

  49. var error: NSError? = nil let object = NSJSONSerialization.JSONObjectWithData(data, options:

    nil, error: &error )
  50. class func JSONObjectWithData(data: NSData, options: NSJSONReadingOptions, error: NSErrorPointer) -> AnyObject

  51. func JSONObjectWithData( data: NSData, options: NSJSONReadingOptions = nil, ) ->

    (AnyObject?, NSError?)
  52. let result = JSONObjectWithData(data) result.0 // object? result.1 // error?

  53. let (object, error) = JSONObjectWithData(data) if let someObject = object

    { // can work with someObject } else { // error }
  54. func JSONObjectWithData( data: NSData, options: NSJSONReadingOptions = nil, ) ->

    (obj: AnyObject?, error: NSError?)
  55. let result = JSONObjectWithData(data) result.obj // object? result.error // error?

  56. Some%mes'if let'sucks

  57. Swi$%Pa(ern%Matching

  58. switch(result) { case (.Some(let x), nil): println("x's type is AnyObject")

    case (nil, .Some(let e)): println("e's type is NSError") default: }
  59. A"more"general"solu-on

  60. enum Result<T> { case Value(T) case Error(NSError) }

  61. func JSONObjectWithData( data: NSData, options: NSJSONReadingOptions = nil ) ->

    Result<AnyObject>
  62. func JSONObjectWithData( data: NSData, options: NSJSONReadingOptions = nil ) ->

    Result<AnyObject> { .... if let error = parserError { return .Error(error) } else { return .Value(parsedObject) } }
  63. let result = JSONObjectWithData(data) switch(result) { case .Value(let obj): println("we

    parsed the object: \(obj)") case .Error(let error): println("error parsing json: \(error)") }
  64. (Side&Note:&Boxing&currently&required) Hopefully)Apple)will)fix)this)soon,)but)currently)you)need)to)box)the) T)value)for)this)to)work: class Box<T> { var unbox: T init(_

    value: T) { unbox = value } }
  65. enum Result<T> { case Value(Box<T>) case Error(NSError) } switch result

    { case .Value(let box): let val = box.unbox ... }
  66. More%on%Result<T>%later...

  67. Map$/$Reduce

  68. None
  69. struct Item { let name: String let price: Double let

    taxable: Bool }
  70. Given&a&Shopping&cart&full&of&items... let shoppingCart = [ Item(name: "Apples", price: 0.67, taxable:

    false), Item(name: "Bananas", price: 1.87, taxable: false), Item(name: "Beer", price: 6.99, taxable: true), Item(name: "Candy", price: 4.00, taxable: true), Item(name: "Ice Cream", price: 6.00, taxable: true) ]
  71. Compute(the(amount(of(tax(you( have(to(pay...

  72. let TaxRate = 0.0825

  73. let TaxRate = 0.0825 var tax = 0.0

  74. let TaxRate = 0.0825 var tax = 0.0 for item

    in shoppingCart { } println(tax)
  75. let TaxRate = 0.0825 var tax = 0.0 for item

    in shoppingCart { if item.taxable { } } println(tax)
  76. let TaxRate = 0.0825 var tax = 0.0 for item

    in shoppingCart { if item.taxable { tax += item.price * TaxRate } } println(tax)
  77. How$can$we$express$this$in$terms$of$ map,$filter,$and$reduce?

  78. map

  79. func map<C : CollectionType, T>( source: C, transform: (C.Generator.Element) ->

    T) -> [T]
  80. func map<S : SequenceType, T>( source: S, transform: (S.Generator.Element) ->

    T) -> [T]
  81. let items = [1, 2, 3] map(items, { (item: Int)

    -> Int in return item * 2 })
  82. let items = [1, 2, 3] map(items, { (item) ->

    Int in return item * 2 })
  83. let items = [1, 2, 3] map(items, { item in

    return item * 2 })
  84. map(items, { return $0 * 2 })

  85. map(items, { $0 * 2 })

  86. map(items) { $0 * 2 }

  87. [1,2,3].map { $0 * 2 }

  88. [1,2,3].map { $0 * 2 } // => [2, 4,

    6]
  89. filter

  90. func filter<S : SequenceType>( source: S, includeElement: (S.Generator.Element) -> Bool)

    -> [S.Generator.Element]
  91. [1,2,3].filter { $0 % 2 == 0 }

  92. [1,2,3].filter { $0 % 2 == 0 } // =>

    [2]
  93. reduce

  94. func reduce<S : SequenceType, U>( sequence: S, initial: U, combine:

    @noescape (U, S.Generator.Element) -> U) -> U
  95. [1,2,3].reduce(0) { (sum, n) in return sum + n }

  96. All#set?

  97. First&get&the&taxable&items let taxable = shoppingCart.filter { $0.taxable }

  98. Map$items$to$their$prices let taxable = shoppingCart.filter { $0.taxable } let taxableAmounts

    = taxable.map { $0.price }
  99. Reduce&to&a&single&value&(the&amount&of&tax) let taxable = shoppingCart.filter { $0.taxable } let taxableAmounts

    = taxable.map { $0.price } let tax = taxableAmounts.reduce(0) { (tax, itemPrice) in return tax + (itemPrice * TaxRate) }
  100. Can$simplify$the$reduce$block... let taxable = shoppingCart.filter { $0.taxable } let taxableAmounts

    = taxable.map { $0.price } let itemTaxes = taxable.map { $0 * TaxRate } let tax = itemTaxes.reduce(0) { (tax, itemTax) in return tax + (itemTax) }
  101. ...into&an&exis+ng&func+on

  102. func +(lhs: Int, rhs: Int) -> Int { return lhs

    + rhs }
  103. Look$at$the$Shape$of$the$func/on... (T,T) -> U

  104. Look$at$the$Shape$of$the$func/on... !-> T

  105. (T,T)-> T

  106. (U,T)-> U

  107. Can$just$use$the$+$func-on... let taxable = shoppingCart.filter { $0.taxable } let taxableAmounts

    = taxable.map { $0.price } let itemTaxes = taxable.map { $0 * TaxRate } let tax = itemTaxes.reduce(0, combine: +)
  108. ..."and"chain"them"together... let tax = shoppingCart .filter { $0.taxable } .map

    { $0.price } .map { $0 * TaxRate } .reduce(0, combine: +) println(tax) // 1.40
  109. Func%ons (no$interim$state,$no$loops,$no$condi1onals)

  110. func taxFunction(rate: Double)

  111. func taxFunction(rate: Double) -> ([Item]) -> Double {

  112. func taxFunction(rate: Double) -> ([Item]) -> Double { return {

    (items) in return items .filter { $0.taxable } .map { $0.price } .map { $0 * rate } .reduce(0, combine: +) } }
  113. let texasTax = taxFunction(0.0825) let tax = texasTax(items)

  114. Flat%Map

  115. We#know#what#map#is,#what#is#fla1en? flatten([[5, 6], [4], [7, 8]]) -> [5,6,4,7,8]

  116. it#is#o&en#handy#to#fla.en#before# mapping

  117. (flat,&map)

  118. struct Person { var name: String var emails: [String] }

  119. "Give&me&a&list&of&all&email& addresses"

  120. people.map { $0.emails } ???

  121. There%is%no%fla,en%func0on,%but%we%could% write%one... func flatten<T>(array: [[T]) -> [T] { var result

    = [T]() for item in array { for subItem in item { result.append(subItem) } } return result }
  122. ...ore%more%succinctly,%with%reduce: func flatten<T>(array: [[T]) -> [T] { return array.reduce([], combine:

    +) }
  123. Yay!%Swi)%1.2%has%a%flatMap% func3on%:)

  124. [[1, 2], [3], [4, 5]].flatMap { $0 } // ->

    [1, 2, 3, 4, 5]
  125. people.flatMap { $0.emails }

  126. flatMap&for&Result<T>&?

  127. enum Result<T> { case Value(Box<T>) case Error(NSError) func flatMap<U>(next: T

    -> Result<U>) -> Result<U> { switch self { case .Value(let box): let val = box.unbox return next(val) case .Error(let err): return .Error(err) } } }
  128. func getValue() -> Result<Int> func convertToFloat(i: Int) -> Result<Float> func

    squareRoot(x: Float) -> Result<Float>
  129. getValue().flatMap(convertToFloat).flatMap(squareRoot)

  130. Dealing(with(Index(Paths

  131. Remember&sta*c&table&views&before& Storyboards?

  132. func tableView(tableView: UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell { if indexPath.section

    == 0 { if indexPath.row == 0 { // } else { // ... } } else if indexPath.section == numberOfSectionsInTableView(tableView) - 1 { // ... } else { // ... } }
  133. func tableView(tableView: UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell { switch (indexPath)

    { case (0, 0): ... case (0, let row): ... case (let section, let row) where isLastSection(section): ... case (let section, let row): ... } }
  134. Enums&for&API&Endpoints

  135. Consider)an)API)with)some)paths: • /notifications • /users/:username • /catgories/:category_id/products/:product_id

  136. enum ApiEndpoints { case Notifications case UserProfile(String) case ProductDetail(Int, Int)

    }
  137. enum ApiEndpoints { case Notifications case UserProfile(String) case ProductDetail(Int, Int)

    var path: String { switch(self) { case .Notifications: return "/notifications" case .UserProfile(let username): return "/users/\(username.stringByAddingPercentEscapes...)" case .ProductDetail(let categoryId, let productId): return "/categories/\(categoryId)/products/\(productId)" } } }
  138. ApiClient.request(.UserProfile("subdigital")) { ... }

  139. Swi$%is%s'll%new

  140. !

  141. "When&we&learn&a&new&programming& language,&we&may&need&[to]&discard& what&we&thought&it&should&be,&and& learn&what&it&will&be." —"a"Swi'"student

  142. thanks! @subdigital+•+@nsscreencast