Upload
keith-moon
View
58
Download
1
Embed Size (px)
Citation preview
Keith Moon - Senior iOS Developer
Thinking In Swift
MBLT.dev - MoscowNovember 2016
• 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
• 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
• Australia• Brazil• Canada• Denmark• France• Ireland• Italy• Mexico
What is Just Eat?
Global Business
• New Zealand• Norway• Spain• Switzerland• UK
• 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
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
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”?
• 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
The right tool for the job
Enums
• Integer based
• Typedef
• …and that’s it.
typedef enum : NSUInteger { JEPaymentOptionCash, JEPaymentOptionSavedCard, JEPaymentOptionApplePay, JEPaymentOptionAccountCredit } JEPaymentOption;
- (void)payByOption:(JEPaymentOption)option { // Pay ...}
The right tool for the job
Enums
• Based on any RawRepresentableenum PaymentOption: String { case cash case savedCard case applePay case accountCredit}
The right tool for the job
Enums
• Based on any RawRepresentable
• … or not.
enum PaymentOption { case cash case savedCard case applePay case accountCredit}
The right tool for the job
Enums
• Based on any RawRepresentable
• … or not.
• Associated Types
enum PaymentOption { case cash case savedCard(SavedCard) case applePay case accountCredit}
The right tool for the job
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 { //... }}
The right tool for the job
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
Value Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
S
Value Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
SSearchFilter
postcode: BR13HPcuisine: nil
S
Value Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
SSearchFilter
postcode: BR13HPcuisine: Chicken
S
Value Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
S
Reference Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
C
Reference Type Semantics
SearchFilterpostcode: BR13HP
cuisine: nil
C
Reference Type Semantics
SearchFilterpostcode: BR13HPcuisine: Chicken
C
Reference Type Semantics
SearchFilterpostcode: BR13HPcuisine: 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"] }}
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) }}
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
Margherita PizzaC
Vegetarian PizzaC
Coca-ColaC
SpriteC
Reference Type Semantics
Margherita PizzaC
Vegetarian PizzaC
Coca-ColaC
SpriteC
Discount
Reference Type Semantics
Margherita PizzaC
Vegetarian PizzaC
Coca-ColaC
SpriteC
Discount
Value Type Semantics
Margherita PizzaS
Vegetarian PizzaS
Vegetarian PizzaS
Coca-ColaS
Coca-ColaS
SpriteS
Value Type Semantics
Margherita PizzaS
Vegetarian PizzaS
Vegetarian PizzaS
Coca-ColaS
Coca-ColaS
SpriteS
Discount
Value Type Semantics
Margherita PizzaS
Vegetarian PizzaS
Vegetarian PizzaS
Coca-ColaS
Coca-ColaS
SpriteS
Discount
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
Thanks! @[email protected]
keefmoon