Adopting Swift Generics
Перейдем на <T>
Max Sokolov - iOS dev @ Avito
masokolov@avito.ru
max_sokolov
2
3
World’s 3rd
Biggest
Classifieds Site
35m
users
per month
500k
items
per day
100k
deals
Agenda
• Dynamic type-casting problem
• Generics in Swift
• Protocols with associated types
• Declarative type-safe UIKit
• Livecoding!
4
🤔
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?
6
😎
Segmentation fault: 11
Why?
• One team of 5 people
• 3 different apps with shared components (mostly Swift)
• 20 private CocoaPods
• Large code base (~100k)
10
[go back];
Typical api request
API Resource
/users
Raw Data
NSData?
Parsed Object
[String: String]?
Model
User?
Request Parsing
ObjectMapper
12
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
13
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
14
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) {
}];
16
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
}
}
17
<T>
Demo
UIKit<T>?
UITableView powered by generics
90% of our screens are table views
23
Problems with UITableView
• Boilerplate
• Painful when data sources are complex and dynamic
• Not type safe (dequeueReusableCellWithIdentifier etc.)
24
What table view actually needs?
Model ViewModel
UITableViewCell
Deadline ;)
25
<ItemType, CellType>
What if…
Line of code
let row = TableRow<String, MyTableViewCell>(item: "string")
28
Demo
TableKit
Open Source
https://github.com/maxsokolov/TableKit
🚀
TableKit
import TableKit
let clickAction = TableRowAction<String, MyTableViewCell>(.click) { (data) in
}
let row = TableRow<String, MyTableViewCell>(item: "string", actions: [clickAction])
tableDirector += TableSection(rows: [row])
31
Functional programming
The power of declarative approach
let rows: [Row] = strings.map { TableRow<String, MyTableViewCell>(item: $0) }
The view layer is simply
a mapping of our data layer
let rows: [Row] = users
.filter({ $0.active })
.map({ UserViewModel(user: $0) })
.map({ TableRow<UserViewModel, UserTableViewCell>(item: $0) })
Resume
• Currently, SDK/API’s are not generic
• Compiler is not perfect
• Can’t be used with Objective-C
• Compile-time type-safety help to know about potential
issues earlier
• We could build great, flexible, easy to use API’s
35
Links
36
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
TableKit
https://github.com/maxsokolov/TableKit
AvitoTech
https://twitter.com/avitotech
THANK YOU!
QUESTIONS?
Max Sokolov - iOS dev @ Avito
max_sokolov
https://github.com/maxsokolov

Adopting Swift Generics