Keith Moon - Senior iOS Developer
Thinking In Swift
MBLT.dev - Moscow
November 2016
2
• iOS Developer since 2010
• Worked with BBC News, Hotels.com and Travelex
• Working in Swift since it’s release
• Built 2 apps end to end in Swift
• “Swift 3 Cookbook” to be published by Pakt
Who am I?
@keefmoon
3
• Help users discover great local food
• Make it quick and easy to order from a wide variety of takeaways
• Available on:
–Web
–iOS
–Android
–Amazon Echo
What is Just Eat?
3
4
• Australia
• Brazil
• Canada
• Denmark
• France
• Ireland
• Italy
• Mexico
What is Just Eat?
Global Business
• New Zealand
• Norway
• Spain
• Switzerland
• UK
5
• 12 iOS Developers
• Organised around feature teams
• Mixture of Objective-C and Swift
• Regular releases
• Multi-Variant Testing
• Research team investigating new technology
What is Just Eat?
5
6
What is Just Eat?
• 12 iOS Developers
• Organised around feature teams
• Mixture of Objective-C and Swift
• Regular releases
• Multi-Variant Testing
• Research team investigating new technology
7
Purpose of this talk?
• Swift is fundamentally different to Objective-C
• Not just different syntax
• Swift has more “tools in the toolbox”
How do we “think in Swift”?
How do we “think in Swift”?
8
• Use constructs that more closely match the model
• Write code that is hard to use wrong
• Remove the need for trivial tests
• Consider a Protocol orientated approach
Structs
The right tool for the job
Class Objects
Enums
Protocols
+ Extensions
Constrained
Extensions
Tuples
Enums
• Integer based
• Typedef
• …and that’s it.
typedef enum : NSUInteger {
JEPaymentOptionCash,
JEPaymentOptionSavedCard,
JEPaymentOptionApplePay,
JEPaymentOptionAccountCredit
} JEPaymentOption;
- (void)payByOption:(JEPaymentOption)option {
// Pay ...
}
Enums
• Based on any RawRepresentable
enum PaymentOption: String {
case cash
case savedCard
case applePay
case accountCredit
}
Enums
• Based on any RawRepresentable
• … or not.
enum PaymentOption {
case cash
case savedCard
case applePay
case accountCredit
}
Enums
• Based on any RawRepresentable
• … or not.
• Associated Types
enum PaymentOption {
case cash
case savedCard(SavedCard)
case applePay
case accountCredit
}
Enums
• Based on any RawRepresentable
• … or not.
• Associated Types
• Methods
enum PaymentOption {
case cash
case savedCard(SavedCard)
case applePay
case accountCredit
func canPay(at rest: Restaurant) -> Bool {
//...
}
}
Enums
• Based on any RawRepresentable
• … or not.
• Associated Types
• Methods
• Computed variables
enum PaymentOption {
case cash
case savedCard(SavedCard)
case applePay
case accountCredit
func canPay(at rest: Restaurant) -> Bool {
//...
}
var isDeviceSupported: Bool {
//...
}
}
Example App
16
Value Type Semantics
17
SearchFilter
postcode: BR13HP
cuisine: nil
S
Value Type Semantics
18
SearchFilter
postcode: BR13HP
cuisine: nil
S
SearchFilter
postcode: BR13HP
cuisine: nil
S
Value Type Semantics
19
SearchFilter
postcode: BR13HP
cuisine: nil
S
SearchFilter
postcode: BR13HP
cuisine: Chicken
S
Value Type Semantics
20
SearchFilter
postcode: BR13HP
cuisine: nil
S
Reference Type Semantics
21
SearchFilter
postcode: BR13HP
cuisine: nil
C
Reference Type Semantics
22
SearchFilter
postcode: BR13HP
cuisine: nil
C
Reference Type Semantics
23
SearchFilter
postcode: BR13HP
cuisine: Chicken
C
Reference Type Semantics
24
SearchFilter
postcode: BR13HP
cuisine: Chicken
C
enum Cuisine: Int {
case american
case bangladeshi
//...
case thai
case turkish
init?(string: String) {
guard let index = Cuisine.stringValues.index(of: string),
let cuisine = Cuisine(rawValue: index) else {
return nil
}
self = cuisine
}
var displayString: String {
return Cuisine.stringValues[rawValue]
}
private static var stringValues: [String] {
return ["American",
"Bangladeshi",
//...
"Thai",
"Turkish"]
}
}
25
enum Cuisine: Int, CaseCountable {
case american
case bangladeshi
//...
case thai
case turkish
init?(string: String) {
for index in 0..<Cuisine.caseCount {
if let cuisine = Cuisine(rawValue: index),
cuisine.displayString == string {
self = cuisine
return
}
}
return nil
}
var displayString: String {
return String(describing: self).capitalized
}
}
Cuisine Enum -
NewOld
restaurantService.fetchRestaurants(for: postcode) { [weak self] (fetchedRestaurants, error) in
if let fetchedRestaurants = fetchedRestaurants {
self?.allRestaurants = fetchedRestaurants
self?.visibleRestaurants = fetchedRestaurants
self?.tableView.reloadData()
} else if let error = error {
// Handle Error
print(error)
}
}
26
enum RestaurantResult {
case success([Restaurant])
case failure(Error)
}
restaurantService.fetchRestaurants(for: postcode) { [weak self] result
in
switch result {
case .success(let fetchedRestaurants):
self?.allRestaurants = fetchedRestaurants
self?.visibleRestaurants = fetchedRestaurants
self?.tableView.reloadData()
case .failure(let error):
// Handle Error
print(error)
}
}
Networking Result -
New
Old
Reference Type Semantics
27
Margherita Pizza
C
Vegetarian Pizza
C
Coca-Cola
C
Sprite
C
Reference Type Semantics
28
Margherita Pizza
C
Vegetarian Pizza
C
Coca-Cola
C
Sprite
C
Reference Type Semantics
29
Margherita Pizza
C
Vegetarian Pizza
C
Coca-Cola
C
Sprite
C
Value Type Semantics
30
Margherita Pizza
S
Vegetarian Pizza
S
Vegetarian Pizza
S
Coca-Cola
S
Coca-Cola
S
Sprite
S
Value Type Semantics
31
Margherita Pizza
S
Vegetarian Pizza
S
Vegetarian Pizza
S
Coca-Cola
S
Coca-Cola
S
Sprite
S
Value Type Semantics
32
Margherita Pizza
S
Vegetarian Pizza
S
Vegetarian Pizza
S
Coca-Cola
S
Coca-Cola
S
Sprite
S
Summary
● Pick the appropriate Swift type for the concept being modelled
● Consider the Type semantics
● Defining behaviours as protocols can produce expressive code
● Avoid “Stringly” typed implementations
● Try to anticipate and prevent future developer errors
33
Thanks! @keefmoon
keith.moon@just-eat.com
keefmoon

Thinking in swift ppt

  • 1.
    Keith Moon -Senior iOS Developer Thinking In Swift MBLT.dev - Moscow November 2016
  • 2.
    2 • iOS Developersince 2010 • Worked with BBC News, Hotels.com and Travelex • Working in Swift since it’s release • Built 2 apps end to end in Swift • “Swift 3 Cookbook” to be published by Pakt Who am I? @keefmoon
  • 3.
    3 • Help usersdiscover great local food • Make it quick and easy to order from a wide variety of takeaways • Available on: –Web –iOS –Android –Amazon Echo What is Just Eat? 3
  • 4.
    4 • Australia • Brazil •Canada • Denmark • France • Ireland • Italy • Mexico What is Just Eat? Global Business • New Zealand • Norway • Spain • Switzerland • UK
  • 5.
    5 • 12 iOSDevelopers • Organised around feature teams • Mixture of Objective-C and Swift • Regular releases • Multi-Variant Testing • Research team investigating new technology What is Just Eat? 5
  • 6.
    6 What is JustEat? • 12 iOS Developers • Organised around feature teams • Mixture of Objective-C and Swift • Regular releases • Multi-Variant Testing • Research team investigating new technology
  • 7.
    7 Purpose of thistalk? • Swift is fundamentally different to Objective-C • Not just different syntax • Swift has more “tools in the toolbox” How do we “think in Swift”?
  • 8.
    How do we“think in Swift”? 8 • Use constructs that more closely match the model • Write code that is hard to use wrong • Remove the need for trivial tests • Consider a Protocol orientated approach
  • 9.
    Structs The right toolfor the job Class Objects Enums Protocols + Extensions Constrained Extensions Tuples
  • 10.
    Enums • Integer based •Typedef • …and that’s it. typedef enum : NSUInteger { JEPaymentOptionCash, JEPaymentOptionSavedCard, JEPaymentOptionApplePay, JEPaymentOptionAccountCredit } JEPaymentOption; - (void)payByOption:(JEPaymentOption)option { // Pay ... }
  • 11.
    Enums • Based onany RawRepresentable enum PaymentOption: String { case cash case savedCard case applePay case accountCredit }
  • 12.
    Enums • Based onany RawRepresentable • … or not. enum PaymentOption { case cash case savedCard case applePay case accountCredit }
  • 13.
    Enums • Based onany RawRepresentable • … or not. • Associated Types enum PaymentOption { case cash case savedCard(SavedCard) case applePay case accountCredit }
  • 14.
    Enums • Based onany RawRepresentable • … or not. • Associated Types • Methods enum PaymentOption { case cash case savedCard(SavedCard) case applePay case accountCredit func canPay(at rest: Restaurant) -> Bool { //... } }
  • 15.
    Enums • Based onany RawRepresentable • … or not. • Associated Types • Methods • Computed variables enum PaymentOption { case cash case savedCard(SavedCard) case applePay case accountCredit func canPay(at rest: Restaurant) -> Bool { //... } var isDeviceSupported: Bool { //... } }
  • 16.
  • 17.
  • 18.
    Value Type Semantics 18 SearchFilter postcode:BR13HP cuisine: nil S SearchFilter postcode: BR13HP cuisine: nil S
  • 19.
    Value Type Semantics 19 SearchFilter postcode:BR13HP cuisine: nil S SearchFilter postcode: BR13HP cuisine: Chicken S
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
    enum Cuisine: Int{ case american case bangladeshi //... case thai case turkish init?(string: String) { guard let index = Cuisine.stringValues.index(of: string), let cuisine = Cuisine(rawValue: index) else { return nil } self = cuisine } var displayString: String { return Cuisine.stringValues[rawValue] } private static var stringValues: [String] { return ["American", "Bangladeshi", //... "Thai", "Turkish"] } } 25 enum Cuisine: Int, CaseCountable { case american case bangladeshi //... case thai case turkish init?(string: String) { for index in 0..<Cuisine.caseCount { if let cuisine = Cuisine(rawValue: index), cuisine.displayString == string { self = cuisine return } } return nil } var displayString: String { return String(describing: self).capitalized } } Cuisine Enum - NewOld
  • 26.
    restaurantService.fetchRestaurants(for: postcode) {[weak self] (fetchedRestaurants, error) in if let fetchedRestaurants = fetchedRestaurants { self?.allRestaurants = fetchedRestaurants self?.visibleRestaurants = fetchedRestaurants self?.tableView.reloadData() } else if let error = error { // Handle Error print(error) } } 26 enum RestaurantResult { case success([Restaurant]) case failure(Error) } restaurantService.fetchRestaurants(for: postcode) { [weak self] result in switch result { case .success(let fetchedRestaurants): self?.allRestaurants = fetchedRestaurants self?.visibleRestaurants = fetchedRestaurants self?.tableView.reloadData() case .failure(let error): // Handle Error print(error) } } Networking Result - New Old
  • 27.
    Reference Type Semantics 27 MargheritaPizza C Vegetarian Pizza C Coca-Cola C Sprite C
  • 28.
    Reference Type Semantics 28 MargheritaPizza C Vegetarian Pizza C Coca-Cola C Sprite C
  • 29.
    Reference Type Semantics 29 MargheritaPizza C Vegetarian Pizza C Coca-Cola C Sprite C
  • 30.
    Value Type Semantics 30 MargheritaPizza S Vegetarian Pizza S Vegetarian Pizza S Coca-Cola S Coca-Cola S Sprite S
  • 31.
    Value Type Semantics 31 MargheritaPizza S Vegetarian Pizza S Vegetarian Pizza S Coca-Cola S Coca-Cola S Sprite S
  • 32.
    Value Type Semantics 32 MargheritaPizza S Vegetarian Pizza S Vegetarian Pizza S Coca-Cola S Coca-Cola S Sprite S
  • 33.
    Summary ● Pick theappropriate Swift type for the concept being modelled ● Consider the Type semantics ● Defining behaviours as protocols can produce expressive code ● Avoid “Stringly” typed implementations ● Try to anticipate and prevent future developer errors 33
  • 34.