Slide 1

Slide 1 text

BUILD APPS

Slide 2

Slide 2 text

PEOPLE TAKE APPS FOR GRANTED

Slide 3

Slide 3 text

BUILD BETTER APPS FASTER PIERLUIGI CIFANI

Slide 4

Slide 4 text

SOFTWARE HAS ALWAYS BEEN ABOUT BUILDING ABSTRACTIONS

Slide 5

Slide 5 text

BIT -> BYTE BYTE -> ASSEMBLY ASSEMBLY -> C C -> OBJECTIVE C

Slide 6

Slide 6 text

OBJECTIVE C -> SWIFT

Slide 7

Slide 7 text

SWIFT ENABLES US TO BUILD ABSTRACTIONS THAT ARE CLOSER TO OUR PROBLEM DOMAIN

Slide 8

Slide 8 text

≈ 60% OF THE CODE WE WRITE IS "JUST" REST CLIENT CODE

Slide 9

Slide 9 text

WHAT WILL WE BUILD? 1. Generic UICollectionViewDataSource 2. Easy to reason networking

Slide 10

Slide 10 text

1. GENERIC UICollectionViewDataSource

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

GENERIC STATEFUL UICollectionViewDataSource

Slide 14

Slide 14 text

SWIFT == STATELESS !

Slide 15

Slide 15 text

IN SWIFT YOU CAN BE MORE EXPLICIT ABOUT THE STATE

Slide 16

Slide 16 text

IN ANY UITableViewDataSource - (void)requestWithCompletionBlock:(AAPLCompletionBlock)block { self.executingRequest = YES; [self showLoadingView]; [self requestUsersWithPage:self.currentPagination completionBlock:^(NSArray *array, NSError *error) { if (error) { [self showError]; } else { self.array = array; [self.tableView reloadData]; } self.executingRequest = NO; block(array, error); }]; }

Slide 17

Slide 17 text

public enum ListState { case Loading case Loaded(data: [T]) case Failure(error: ErrorType) }

Slide 18

Slide 18 text

private func fetchData() { collectionView.state = .Loading searchUserInteractor.fetchLocationAndUsers() { [weak self] result in guard let strongSelf = self else { return } switch result { case .Failure(let error): strongSelf.collectionView.state = .Failure(error: error) case .Success(let users): strongSelf.collectionView.state = .Loaded(data: users) } } }

Slide 19

Slide 19 text

However, last time I tried to do collectionView.state = .Loading this happened: error: value of type 'UICollectionView' has no member 'state' collectionView.state = .Loading ^~~~~~~~~~~~~~ ~~~~~ !!!!!!

Slide 20

Slide 20 text

APPLE PROVIDES 3 POINTS OF CUSTOMIZATION: 1. Subclassing 2. UICollectionViewFlowLayout 3. UICollectionViewDataSource

Slide 21

Slide 21 text

APPLE PROVIDES 3 POINTS OF CUSTOMIZATION: 1. Subclassing 2. UICollectionViewFlowLayout 3. UICollectionViewDataSource ✅

Slide 22

Slide 22 text

OBJECTIVES: 1. Enforce MVVM1 2. Map one model object to one cell kind at compile time 3. Support for ListState 4. Customization for the .Loading and .Error state 5. Independence from any Layout 1 The one good thing that came out of Microsoft.

Slide 23

Slide 23 text

ENFORCE MVVM public protocol ViewModelConfigurable { associatedtype VM func configureFor(viewModel viewModel: VM) -> Void } public protocol ViewModelReusable: ViewModelConfigurable { static var reuseType: ReuseType { get } static var reuseIdentifier: String { get } } public enum ReuseType { case NIB(UINib) case ClassReference(AnyClass) }

Slide 24

Slide 24 text

//MARK:- Extensions extension ViewModelReusable where Self: UICollectionViewCell { public static var reuseIdentifier: String { return NSStringFromClass(self) } public static var reuseType: ReuseType { return .ClassReference(Self) } }

Slide 25

Slide 25 text

MAP ONE MODEL OBJECT TO ONE CELL KIND AT COMPILE TIME

Slide 26

Slide 26 text

class CollectionViewStatefulDataSource { typealias ModelMapper = (Model) -> (Cell.VM) weak var collectionView: UICollectionView? let mapper: ModelMapper var state: ListState { didSet { collectionView?.reloadData() } } }

Slide 27

Slide 27 text

TO THE PLAYGROUND

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

POSSIBLE IMPROVEMENTS: > Pull to refresh > Inifite scrolling > Support for n sections

Slide 30

Slide 30 text

2. EASY TO REASON NETWORKING

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

ConfigurationLogic *logic = [ConfigurationLogic currentConfiguration]; [NetworkFetcher callService:@"/users" host:[logic baseURL] method:GET postData:nil bodyInJSON:NO hasCache:YES timeoutInterval:[logic servicesTimeOut] successHandler:successHandler failureHandler:failureHandler];

Slide 34

Slide 34 text

func lookForStockForSymbols (symbol : String, handler : FetchSymbolHandler?) -> StockFetcherOperation { let parameters = [ "input" : symbol ] let request = networkFetcher.request( .GET, "http://dev.markitondemand.com/Api/v2/Lookup/json", parameters:parameters , encoding:.URL ) request.responseJSON { (_, _, operationResult) -> Void in /** Some code */ } return StockFetcherOperation(request: request) }

Slide 35

Slide 35 text

OBJECTIVES: 1. Composability 2. Sensitive default values 3. Readability 4. Self documented

Slide 36

Slide 36 text

MORE PROTOCOLS

Slide 37

Slide 37 text

/** Protocol used to describe what is needed in order to send REST API requests. */ public protocol Endpoint { /// The path for the request var path: String { get } /// The HTTPMethod for the request var method: HTTPMethod { get } /// Optional parameters for the request var parameters: [String : AnyObject]? { get } /// How the parameters should be encoded var parameterEncoding: HTTPParameterEncoding { get } /// The HTTP headers to be sent var httpHeaderFields: [String : String]? { get } }

Slide 38

Slide 38 text

// This is the default implementation for Endpoint extension Endpoint { public var method: HTTPMethod { return .GET } var parameters: [String : AnyObject]? { return nil } public var parameterEncoding: HTTPParameterEncoding { return .URL } public var httpHeaderFields: [String : String]? { return nil } }

Slide 39

Slide 39 text

enum AuthenticationAPI: Endpoint { case LoginFacebook(token: String) }

Slide 40

Slide 40 text

extension AuthenticationAPI: Endpoint { var path: String { switch self { case .LoginFacebook(_): return "/authentication" } } var method: HTTPMethod { switch self { case .LoginFacebook(_): return .POST } } var parameters: [String : AnyObject]? { switch self { case .LoginFacebook(let token): return [ "token" : token ] } } }

Slide 41

Slide 41 text

enum UsersAPI { case SearchUsers(Filter) case UserDetails(UUID) case UserPictures(UUID) }

Slide 42

Slide 42 text

extension UsersAPI: Endpoint { public var path: String { switch self { case .SearchUsers(_): return "/users" case .UserDetails(let uuid): return "/users/\(uuid)" case .UserPictures(let uuid): return "/users/\(uuid)/pictures" } } public var parameters: [String : AnyObject]? { switch self { case .SearchUsers(let filter): var params = [String : AnyObject]() params["min_age"] = filter.minAge params["max_age"] = filter.maxAge params["min_dist"] = filter.minDistance params["max_dist"] = filter.maxDistance params["lat"] = filter.latitude params["long"] = filter.longitude return params default: return nil } } }

Slide 43

Slide 43 text

WHERE'S THE NSURLRequest?

Slide 44

Slide 44 text

"Apps interacting with web services are encouraged to have custom types conform to URLRequestConvertible as a way to ensure consistency of requested endpoints." -- @mattt2 2 Alamofire's Readme.

Slide 45

Slide 45 text

/** Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests. */ public protocol URLRequestConvertible { /// The URL request. var URLRequest: NSMutableURLRequest { get } } protocol Endpoint: URLRequestConvertible { /* The other stuff */ var URLRequest: NSMutableURLRequest { let request = NSMutableURLRequest(URL: NSURL(string: path)!) request.HTTPMethod = method.rawValue request.allHTTPHeaderFields = httpHeaderFields let requestTuple = parameterEncoding.encode(request, parameters: parameters) return requestTuple.0 } }

Slide 46

Slide 46 text

POSSIBLE IMPROVEMENTS: > Multiple environments > Authentication tokens > Response parsing

Slide 47

Slide 47 text

BUILD ABSTRACTIONS ON TOP OF THE PROBLEMS WE'RE SOLVING OVER AND OVER

Slide 48

Slide 48 text

MORE TIME FOR: > Animations > Eye-Candy > Performance > Iterating

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

CODE AVAILABLE AT IN GITHUB

Slide 51

Slide 51 text

AVAILABLE FOR: > Consulting > Auditing > Training

Slide 52

Slide 52 text

THANK YOU! @PIERCIFANI [email protected]