Slide 1

Slide 1 text

Inside Parse SDK Nikita Lutsenko @nlutsenko Facebook, Parse

Slide 2

Slide 2 text

Hi » Nikita Lutsenko » @nlutsenko » Facebook » Slingshot » Parse

Slide 3

Slide 3 text

Inside Parse SDK » ~730 Source Files » ~51000 Lines of Code » ~150 Classes+Protocols » 767 Unit Tests » Support for iOS, OS X » 2 Maintainers

Slide 4

Slide 4 text

That's all folks. Thank you. !

Slide 5

Slide 5 text

Not that simple... !

Slide 6

Slide 6 text

Parse SDK » Object & File Storage » Query Engine » Local Datastore » User Authentication » Global Configuration » Analytics & Push Notifications » Much more...

Slide 7

Slide 7 text

Parse SDKs now power 800 million active app-device pairs per month

Slide 8

Slide 8 text

Inside Parse SDK » Promises for Asynchronous Operations » Instance/Controller/State Architecture » Lazy-loaded Dependency Injection » Written in ObjC, works in Swift » ??? » PROFIT!!!

Slide 9

Slide 9 text

Promise everything! » Perform asynchronous work » Serially or in Parallel » Errors, cancellation, chaining » Unified across ObjC/Java/.NET » Light-weight and Extendable

Slide 10

Slide 10 text

! Bolts.framework » Composable Promise Framework » Tasks, Executors, Cancellation Tokens » Avaialble for ObjC, Java, .NET » Open Source » github.com/BoltsFramework » Coming to Swift!

Slide 11

Slide 11 text

- (NSData *)dataWithContentsOfFile:(NSString *)file { NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; return data; }

Slide 12

Slide 12 text

- (NSData *)dataWithContentsOfFile:(NSString *)file error:(NSError **)error { NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; return data; }

Slide 13

Slide 13 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; completion(data); }); }

Slide 14

Slide 14 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; completion(data, error); }); }

Slide 15

Slide 15 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { ... } - (void)doWorkForFile:(NSString *)file { [self getDataWithContentsOfFile:file completion:^(NSData *data, NSError *error) { if (error) { [self reportError:error]; } else { [self continueWorkForData:data]; } [self deleteFile:file]; }]; }

Slide 16

Slide 16 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { ... } - (void)doWorkForFile:(NSString *)file { [self getDataWithContentsOfFile:file completion:^(NSData *data, NSError *error) { if (error) { [self reportError:error]; [self deleteFile:file]; } else { [self continueWorkForData:data completion:^(NSError *error) { if (error) { [self reportError:error]; } [self deleteFile:file]; }]; } }]; }

Slide 17

Slide 17 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { ... } - (void)doWorkForFile:(NSString *)file { [self getDataWithContentsOfFile:file completion:^(NSData *data, NSError *error) { if (error) { [self reportError:error]; // << Duplicate error handling ! [self deleteFile:file]; } else { [self continueWorkForData:data completion:^(NSError *error) { if (error) { [self reportError:error]; // << Duplicate error handling ! } [self deleteFile:file]; }]; } }]; }

Slide 18

Slide 18 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { ... } - (void)doWorkForFile:(NSString *)file { [self getDataWithContentsOfFile:file completion:^(NSData *data, NSError *error) { if (error) { [self reportError:error]; // << Duplicate error handling ! [self deleteFile:file]; // << Duplicate code ! } else { [self continueWorkForData:data completion:^(NSError *error) { if (error) { [self reportError:error]; // << Duplicate error handling ! } [self deleteFile:file]; // << Duplicate code ! }]; } }]; }

Slide 19

Slide 19 text

- (void)getDataWithContentsOfFile:(NSString *)file completion:(void (^)(NSData *data, NSError *error))completion { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; completion(data, error); }); }

Slide 20

Slide 20 text

- (BFTask *)getDataAsyncWithContentsOfFile:(NSString *)file { // << Return a BFTask ! return [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{ NSError *error = nil; NSData *data = [NSData dataWithContentsOfFile:file options:NSDataReadingMappedIfSafe error:&error]; if (error) { return [BFTask taskWithError:error]; } return data; }]; }

Slide 21

Slide 21 text

- (BFTask *)getDataAsyncWithContentsOfFile:(NSString *)file { ... } - (void)doWorkForFile:(NSString *)file { [[[self getDataAsyncWithContentsOfFile:file] continueWithBlock:^id(BFTask *task) { if (error) { [self reportError:error]; // << Duplicate error handling ! [self deleteFile:file]; // << Duplicate code ! } else { [self continueWorkForData:data completion:^(NSError *error) { if (error) { [self reportError:error]; // << Duplicate error handling ! } [self deleteFile:file]; // << Duplicate code ! }]; } }]; }

Slide 22

Slide 22 text

- (BFTask *)getDataAsyncWithContentsOfFile:(NSString *)file { ... } - (void)doWorkForFile:(NSString *)file { [[[self getDataAsyncWithContentsOfFile:file] continueWithSuccessBlock:^id(BFTask *task) { return [self continueWorkAsyncForData:data]; // << Return a BFTask ! }] continueWithBlock:^id(BFTask *task) { if (task.faulted) { [self reportError:error]; } [self deleteFile:file]; return nil; }]; }

Slide 23

Slide 23 text

- (BFTask *)getDataAsyncWithContentsOfFile:(NSString *)file { ... } - (void)doWorkForFile:(NSString *)file { [[[self getDataAsyncWithContentsOfFile:file] continueWithSuccessBlock:^id(BFTask *task) { return [self continueWorkAsyncForData:data]; }] continueWithBlock:^id(BFTask *task) { if (task.faulted) { [self reportError:error]; } return [self deleteFileAsync:file]; // << Return a BFTask ! }]; }

Slide 24

Slide 24 text

- (BFTask *)getDataAsyncWithContentsOfFile:(NSString *)file { ... } - (BFTask *)doWorkAsyncForFile:(NSString *)file { // << Return a BFTask ! return [[[self getDataAsyncWithContentsOfFile:file] continueWithSuccessBlock:^id(BFTask *task) { return [self continueWorkAsyncForData:data]; }] continueWithBlock:^id(BFTask *task) { if (task.faulted) { [self reportError:error]; } return [self deleteFile:file]; }]; }

Slide 25

Slide 25 text

That's all good...

Slide 26

Slide 26 text

But how do you manage complexity? !?

Slide 27

Slide 27 text

Instance vs Controller vs State ! » Separation of Concerns and Responsbilities » Lazy Dependency Injection » No Mutable State » Thread-safe » Awesome!

Slide 28

Slide 28 text

Instance vs Controller vs State » Instance ! » Public API » Immutable State » State " » Current Object State » Controller # » Acts on State » Returns State

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

@interface PFFile : NSObject @property NSString *urlString; @property NSString *name; - (BFTask *)getDataInBackground; - (BFTask *)saveInBackground; @end

Slide 31

Slide 31 text

@interface PFFileState : PFBaseState @property (nonatomic, copy, readonly) NSString *name; @property (nullable, nonatomic, copy, readonly) NSString *urlString; @property (nullable, nonatomic, copy, readonly) NSString *mimeType; @end @interface PFMutableFileState : PFFileState @property (nonatomic, copy, readwrite) NSString *name; @property (nullable, nonatomic, copy, readwrite) NSString *urlString; @property (nullable, nonatomic, copy, readwrite) NSString *mimeType; @end

Slide 32

Slide 32 text

Base State » Base class for every state. » NSCopying, NSMutableCopying. » Equality and Comparison » -isEqual: » -compare: » -hash

Slide 33

Slide 33 text

@interface PFFileState : PFBaseState @property (nonatomic, copy, readonly) NSString *name; @property (nullable, nonatomic, copy, readonly) NSString *urlString; @property (nullable, nonatomic, copy, readonly) NSString *mimeType; @end @interface PFMutableFileState : PFFileState @property (nonatomic, copy, readwrite) NSString *name; @property (nullable, nonatomic, copy, readwrite) NSString *urlString; @property (nullable, nonatomic, copy, readwrite) NSString *mimeType; @end

Slide 34

Slide 34 text

@interface PFFileState : PFBaseState @property (nonatomic, copy, readonly) NSString *name; @property (nullable, nonatomic, copy, readonly) NSString *urlString; @property (nullable, nonatomic, copy, readonly) NSString *mimeType; - (instancetype)initWithState:(PFFileState *)state; + (instancetype)stateWithState:(PFFileState *)state; - (BOOL)isEqual:(id)object; - (NSInteger)hash; - (NSComparisonResult)compare:(PFFileState *)object; - (NSDictionary *)dictionaryRepresentation; - (id)debugQuickLookObject; @end

Slide 35

Slide 35 text

@implementation PFFileState + (NSDictionary *)propertyAttributes { return @{ @"name" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], @"urlString" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], @"mimeType" : [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy], }; } - (id)copyWithZone:(NSZone *)zone { return [[PFFileState allocWithZone:zone] initWithState:self]; } - (instancetype)mutableCopyWithZone:(NSZone *)zone { return [[PFMutableFileState allocWithZone:zone] initWithState:self]; } @end

Slide 36

Slide 36 text

@implementation PFMutableFileState @dynamic name; @dynamic urlString; @dynamic mimeType; @end

Slide 37

Slide 37 text

Wait...

Slide 38

Slide 38 text

How about controllers?

Slide 39

Slide 39 text

@interface PFFileController : NSObject @property (nonatomic, weak, readonly) id dataSource; + (instancetype)controllerWithDataSource:(id)dataSource; - (BFTask *)downloadFileAsyncWithState:(PFFileState *)fileState... - (BFTask *)uploadFileAsyncWithState:(PFFileState *)fileState... @end

Slide 40

Slide 40 text

@protocol PFCommandRunnerProvider @property (nonatomic, strong, readonly) id commandRunner; @end @protocol PFFileManagerProvider @property (nonatomic, strong, readonly) PFFileManager *fileManager; @end

Slide 41

Slide 41 text

@protocol PFNoYoloControllerProvider @property (nonatomic, strong, readonly) PFNoYoloController *noYoloController; @end @protocol PFYoloControllerProvider @property (null_resettable, nonatomic, strong) PFYoloController *yoloController; @end

Slide 42

Slide 42 text

- (PFYoloController *)yoloController { __block PFYoloController *controller = nil; dispatch_sync(_controllerAccessQueue, ^{ if (!_yoloController) { _yoloController = [PFYoloController controllerWithDataSource:self.dataSource]; } controller = _yoloController; }); return controller; } - (void)setYoloController:(PFYoloController *)controller { dispatch_sync(_controllerAccessQueue, ^{ _yoloController = controller; }); }

Slide 43

Slide 43 text

Thank you!

Slide 44

Slide 44 text

Questions? @nlutsenko github.com/BoltsFramework github.com/ParsePlatform