KEMBAR78
Advanced Swift Generics | PDF
Advanced Swift Generics
Перейдем на <T>
Max Sokolov - iOS dev @ Avito
masokolov@avito.ru
@max_sokolov
Agenda
• The problem
• Generics in Swift
• Protocols with associated types
• Declarative type-safe UIKit
• Generics in practise
2
🤔
class Matrix<T: Choiceable where T.T == T, T: Learnable, T: Fateable>
Generic code enables you to write flexible, reusable functions
and types that can work with any type, subject to requirements
that you define. You can write code that avoids duplication and
expresses its intent in a clear, abstracted manner.
Apple docs
What for?
4
😎
Segmentation fault: 11
Why?
• One team of 5 people
• 3 different apps with shared components (mostly Swift)
• 20 private CocoaPods
• Large code base (~100k)
8
[go back];
Typical api request
API Resource
/users
Raw Data
NSData?
Parsed Object
[String: String]?
Model
User?
Request Parsing
ObjectMapper
10
Implementation
@implementation DataProvider
- (void)fetch:(NSString *)resources
mappingClass:(Class)mappingClass
completion:(void(^)(NSArray<id<Mappable>> *results))completion {
// Load data...
// Parse data...
if ([mappingClass conformsToProtocol:@protocol(Mappable)]) {
id result = [mappingClass initWithDictionary:response];
completion(@[result]);
}
}
@end
11
The problem?
@implementation DataProvider
- (void)fetch:(NSString *)resources
mappingClass:(Class)mappingClass
completion:(void(^)(NSArray<id<Mappable>> *results))completion {
// Load data...
// Parse data...
if ([mappingClass conformsToProtocol:@protocol(Mappable)]) {
id result = [mappingClass initWithDictionary:response];
completion(@[result]);
}
}
@end
12
RUN TIME!
What run time means?
DataProvider *provider = [DataProvider new];
[provider fetch:@"/users"
mappingClass:[NSString class] // compiler doesn’t warn you here...
completion:^(NSArray<User *> *results) {
}];
14
You are doing it wrong with Swift
let dataProvider = DataProvider()
dataProvider.fetch("/resource", mappingClass: User.self) { result in
// code smell...
guard let users = result.values as? [User] else {
return
}
}
15
<T>
Demo
UIKit<T>?
Next generation table views
UITableView powered by generics
90% of our screens are table views
21
The problem with UITableView
• Boilerplate
• Painful when data sources are complex and dynamic
• Not type safe (dequeueReusableCellWithIdentifier etc.)
22
What table actually needs?
Model ViewModel
UITableViewCell
Deadline ;)
23
<DataType, CellType>
What if…
Line of code
let rowBuilder = TableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3"])
26
Demo
Tablet.swift
Open Source
https://github.com/maxsokolov/Tablet.swift
🚀
Tablet.swift
import Tablet
let row = TableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3"])
.action(.click) { (data) in
}
.action(.willDisplay) { (data) in
}
let section = TableSectionBuilder(headerView: nil, footerView: nil, rows: [row])
tableDirector += section
29
Generalization
NibLoadable
class MyView: UIView, NibLoadable {
}
31
let view = MyView.nibView() // view is MyView
NibLoadable / generic protocol
protocol NibLoadable {
associatedtype T: UIView
static func nibView() -> T?
}
32
extension NibLoadable where Self: UIView {
static func nibView() -> Self? {
return NSBundle(forClass: self)
.loadNibNamed(String(self), owner: nil, options: nil)
.first as? Self
}
}
NibLoadable / generic protocol
protocol NibLoadable {
associatedtype T: UIView
static func nibView() -> T?
}
33
extension NibLoadable where Self: UIView {
static func nibView() -> Self? {
return NSBundle(forClass: self)
.loadNibNamed(String(self), owner: nil, options: nil)
.first as? Self
}
}
NibLoadable / generic func
protocol NibLoadable {
static func nibView<T: UIView>(viewType type: T.Type) -> T?
}
34
extension NibLoadable where Self: UIView {
static func nibView<T: UIView>(viewType type: T.Type) -> T? {
return NSBundle.mainBundle()
.loadNibNamed(String(type), owner: nil, options: nil)
.first as? T
}
static func nibView() -> Self? {
return nibView(viewType: self)
}
}
NibLoadable / generic func
protocol NibLoadable {
static func nibView<T: UIView>(viewType type: T.Type) -> T?
}
35
extension NibLoadable where Self: UIView {
static func nibView<T: UIView>(viewType type: T.Type) -> T? {
return NSBundle.mainBundle()
.loadNibNamed(String(type), owner: nil, options: nil)
.first as! T
}
static func nibView() -> Self? {
return nibView(viewType: self)
}
}
Resume
• Currently, SDK/API’s are not generic
• Compiler is not perfect
• Can’t be used with Objective-C
• Generics help to know about potential issues earlier
• Compile-time type-safety makes your code better
36
Links
37
Covariance and Contravariance
https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html
Protocols with Associated Types, and How They Got That Way
http://www.slideshare.net/alexis_gallagher/protocols-with-associated-types-and-how-they-got-that-way
Why Associated Types?
http://www.russbishop.net/swift-why-associated-types
Tablet.swift
https://github.com/maxsokolov/Tablet.swift
THANK YOU!
QUESTIONS?
Max Sokolov - iOS dev @ Avito
@max_sokolov
https://github.com/maxsokolov

Advanced Swift Generics

  • 1.
    Advanced Swift Generics Перейдемна <T> Max Sokolov - iOS dev @ Avito masokolov@avito.ru @max_sokolov
  • 2.
    Agenda • The problem •Generics in Swift • Protocols with associated types • Declarative type-safe UIKit • Generics in practise 2
  • 3.
    🤔 class Matrix<T: Choiceablewhere T.T == T, T: Learnable, T: Fateable>
  • 4.
    Generic code enablesyou to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner. Apple docs What for? 4
  • 5.
  • 6.
  • 8.
    Why? • One teamof 5 people • 3 different apps with shared components (mostly Swift) • 20 private CocoaPods • Large code base (~100k) 8
  • 9.
  • 10.
    Typical api request APIResource /users Raw Data NSData? Parsed Object [String: String]? Model User? Request Parsing ObjectMapper 10
  • 11.
    Implementation @implementation DataProvider - (void)fetch:(NSString*)resources mappingClass:(Class)mappingClass completion:(void(^)(NSArray<id<Mappable>> *results))completion { // Load data... // Parse data... if ([mappingClass conformsToProtocol:@protocol(Mappable)]) { id result = [mappingClass initWithDictionary:response]; completion(@[result]); } } @end 11
  • 12.
    The problem? @implementation DataProvider -(void)fetch:(NSString *)resources mappingClass:(Class)mappingClass completion:(void(^)(NSArray<id<Mappable>> *results))completion { // Load data... // Parse data... if ([mappingClass conformsToProtocol:@protocol(Mappable)]) { id result = [mappingClass initWithDictionary:response]; completion(@[result]); } } @end 12
  • 13.
  • 14.
    What run timemeans? DataProvider *provider = [DataProvider new]; [provider fetch:@"/users" mappingClass:[NSString class] // compiler doesn’t warn you here... completion:^(NSArray<User *> *results) { }]; 14
  • 15.
    You are doingit wrong with Swift let dataProvider = DataProvider() dataProvider.fetch("/resource", mappingClass: User.self) { result in // code smell... guard let users = result.values as? [User] else { return } } 15
  • 16.
  • 17.
  • 18.
  • 19.
    Next generation tableviews UITableView powered by generics
  • 20.
    90% of ourscreens are table views
  • 21.
  • 22.
    The problem withUITableView • Boilerplate • Painful when data sources are complex and dynamic • Not type safe (dequeueReusableCellWithIdentifier etc.) 22
  • 23.
    What table actuallyneeds? Model ViewModel UITableViewCell Deadline ;) 23
  • 24.
  • 25.
  • 26.
    Line of code letrowBuilder = TableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3"]) 26
  • 27.
  • 28.
  • 29.
    Tablet.swift import Tablet let row= TableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3"]) .action(.click) { (data) in } .action(.willDisplay) { (data) in } let section = TableSectionBuilder(headerView: nil, footerView: nil, rows: [row]) tableDirector += section 29
  • 30.
  • 31.
    NibLoadable class MyView: UIView,NibLoadable { } 31 let view = MyView.nibView() // view is MyView
  • 32.
    NibLoadable / genericprotocol protocol NibLoadable { associatedtype T: UIView static func nibView() -> T? } 32 extension NibLoadable where Self: UIView { static func nibView() -> Self? { return NSBundle(forClass: self) .loadNibNamed(String(self), owner: nil, options: nil) .first as? Self } }
  • 33.
    NibLoadable / genericprotocol protocol NibLoadable { associatedtype T: UIView static func nibView() -> T? } 33 extension NibLoadable where Self: UIView { static func nibView() -> Self? { return NSBundle(forClass: self) .loadNibNamed(String(self), owner: nil, options: nil) .first as? Self } }
  • 34.
    NibLoadable / genericfunc protocol NibLoadable { static func nibView<T: UIView>(viewType type: T.Type) -> T? } 34 extension NibLoadable where Self: UIView { static func nibView<T: UIView>(viewType type: T.Type) -> T? { return NSBundle.mainBundle() .loadNibNamed(String(type), owner: nil, options: nil) .first as? T } static func nibView() -> Self? { return nibView(viewType: self) } }
  • 35.
    NibLoadable / genericfunc protocol NibLoadable { static func nibView<T: UIView>(viewType type: T.Type) -> T? } 35 extension NibLoadable where Self: UIView { static func nibView<T: UIView>(viewType type: T.Type) -> T? { return NSBundle.mainBundle() .loadNibNamed(String(type), owner: nil, options: nil) .first as! T } static func nibView() -> Self? { return nibView(viewType: self) } }
  • 36.
    Resume • Currently, SDK/API’sare not generic • Compiler is not perfect • Can’t be used with Objective-C • Generics help to know about potential issues earlier • Compile-time type-safety makes your code better 36
  • 37.
    Links 37 Covariance and Contravariance https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html Protocolswith Associated Types, and How They Got That Way http://www.slideshare.net/alexis_gallagher/protocols-with-associated-types-and-how-they-got-that-way Why Associated Types? http://www.russbishop.net/swift-why-associated-types Tablet.swift https://github.com/maxsokolov/Tablet.swift
  • 38.
  • 39.
    QUESTIONS? Max Sokolov -iOS dev @ Avito @max_sokolov https://github.com/maxsokolov