Share
Embed
Build Better Apps Faster
Pierluigi Cifani
May 30, 2016
Transcript
52 slides
-
BIT -> BYTE BYTE -> ASSEMBLY ASSEMBLY -> C C
-> OBJECTIVE C -
SWIFT ENABLES US TO BUILD ABSTRACTIONS THAT ARE CLOSER TO
OUR PROBLEM DOMAIN -
≈ 60% OF THE CODE WE WRITE IS "JUST" REST
CLIENT CODE -
WHAT WILL WE BUILD? 1. Generic UICollectionViewDataSource 2. Easy to
reason networking -
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); }]; } -
public enum ListState<T> { case Loading case Loaded(data: [T]) case
Failure(error: ErrorType) } -
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) } } } -
However, last time I tried to do collectionView.state = .Loading
this happened: error: value of type 'UICollectionView' has no member 'state' collectionView.state = .Loading ^~~~~~~~~~~~~~ ~~~~~ !!!!!! -
APPLE PROVIDES 3 POINTS OF CUSTOMIZATION: 1. Subclassing 2. UICollectionViewFlowLayout
3. UICollectionViewDataSource -
APPLE PROVIDES 3 POINTS OF CUSTOMIZATION: 1. Subclassing 2. UICollectionViewFlowLayout
3. UICollectionViewDataSource ✅ -
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. -
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) } -
//MARK:- Extensions extension ViewModelReusable where Self: UICollectionViewCell { public static
var reuseIdentifier: String { return NSStringFromClass(self) } public static var reuseType: ReuseType { return .ClassReference(Self) } } -
class CollectionViewStatefulDataSource <Model, Cell:ViewModelReusable where Cell:UICollectionViewCell > { typealias ModelMapper
= (Model) -> (Cell.VM) weak var collectionView: UICollectionView? let mapper: ModelMapper var state: ListState<Model> { didSet { collectionView?.reloadData() } } } -
POSSIBLE IMPROVEMENTS: > Pull to refresh > Inifite scrolling >
Support for n sections -
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]; -
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) } -
/** 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 } } -
// 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 } } -
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 ] } } } -
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 } } } -
"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. -
/** 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 } }