Using Protocol to Refactor

28
Using Protocol to Refactor 邱志強, Green Chiu, iOS Developer.

Transcript of Using Protocol to Refactor

  1. 1. Using Protocol to Refactor , Green Chiu, iOS Developer.
  2. 2. iOS Protocol ?
  3. 3. ?
  4. 4. Delegation ?
  5. 5. ?
  6. 6. Delegation Pattern Apple CocoaTouch SDK UITableView, UICollectionView, UIGestureRecognizer and so on. NSURLSession, StoreKit
  7. 7. NS-Protocols archive, copy, enumerate NSCopying NSCoding NSFastEnumeration
  8. 8. Objective-C/Swift Protocols method
  9. 9. Protocol in Objective-C @protocol SampleProtocol - (void)sampleMethodA; - (void)sampleMethodB; @optional - (void)sampleOptionalMethod; @end
  10. 10. Protocol class method
  11. 11. The End
  12. 12. /
  13. 13. Class LocalPlaylistInfo - (void)fetchPlaylistCoverImageWithSize { if () { UIImage *image = nil; DBMetaReference *ref = ; if (ref.sourceType == ) { DBMetaItem *item = ; } else if (ref.sourceType == ) { MPMediaItem *item = ; image = [item.artwork imageWith ]; } else if (ref.sourceType == LocalDBContextSongSourceTypeStore) { DBMetaItem *item = ; if (!item) { return; } NSString *imageFileURLString = ; void (^imageCallback)(NSString *fileURLString, UIImage *image) = ^(NSString *fileURLString, UIImage *image) { if (image ) { } else if ([NSURL URLWithString:item.photoURL]) { [[KKRadioImageManager sharedImageManager] fetchImageWithURL: requester:nil callback:^(UIImage *receiveImage, NSError *error) { if (receiveImage) { } }]; } };
  14. 14. Class SongInfoViewModel - (void)loadSongInfo:(LocalSongInfo *)inSongInfo { if (inSongInfo.type == LocalDBContextSongSourceTypeStore || ...) { DBMetaItem *item = inSongInfo.rawItem; if (inSongInfo.type == LocalDBContextSongSourceTypeStore) { } else { } self.imageFileURLString = ; void (^imageCallback)(NSString *fileURLString, UIImage *image) = ^void(NSString *fileURLString, UIImage *image){ if (image) { weakSelf.albumCoverImage = cropImage; } else if ([NSURL URLWithString:item.photoURL]) { [[KKRadioImageManager sharedImageManager] fetchImageWithURL: requester:nil callback:^(UIImage *receiveImage, NSError *error) { if (receiveImage) { weakSelf.albumCoverImage = } }]; } }; return; } if (inSongInfo.image) { }
  15. 15. Issues View or Model classes/framework
  16. 16. Design Protocol typedef NS_ENUM(NSInteger, ProvideImageWay) { ProvideImageWayNone = NSNotFound, ProvideImageWayFetchWithURLString = 0, ProvideImageWayGetWithSize, ProvideImageWayGenerateWithCallback }; @protocol LocalItemImageProvider - (ProvideImageWay)getCoverImageWay; - (NSString *)coverURLString; - (UIImage *)coverImageWithSize:(CGSize)inSize; - (void)generateImageWithCallback:(void(^)(UIImage *))inCallback; @end
  17. 17. After implemented - (void)loadSongInfo:(LocalSongInfo *)inSongInfo { switch ([inSongInfo getCoverImageWay]) { case UPProvideImageWayGetWithSize: self.albumCoverImageView.image = [inSongInfo coverImageWithSize:CGSizeMake()]; break; case UPProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inSongInfo generateImageWithCallback:^(UIImage *image) { weakSelf.albumCoverImageView.image = image; }]; break; } case UPProvideImageWayFetchWithURLString: break; case UPProvideImageWayNone: break; } }
  18. 18. Optimized // UIImageView+LocalItemImageProvider.m - (void)loadImageWithImageProvider:(id)inImageProvider { if (![inImageProvider conformsToProtocol:@protocol(LocalItemImageProvider)]) { return; } switch ([inImageProvider getCoverImageWay]) { case ProvideImageWayGetWithSize: self.image = [inImageProvider coverImageWithSize:CGSizeMake(44, 44)]; break; case ProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inImageProvider generateImageWithCallback:^(UIImage *image) { weakSelf.image = image; }]; break; } case ProvideImageWayFetchWithURLString: [self fetchImageWithURLString:[inImageProvider coverURLString]]; break; case ProvideImageWayNone: default: break; } }
  19. 19. Finally - (void)loadSongInfo:(UPLocalSongInfo *)inSongInfo { [self.albumCoverImageView loadImageWithImageProvider:inSongInfo]; }
  20. 20. Besides Protocol Mock
  21. 21. Testing // UIImageView+LocalItemImageProvider.m - (void)loadImageWithImageProvider:(id)inImageProvider { if (![inImageProvider conformsToProtocol:@protocol(LocalItemImageProvider)]) { return; } switch ([inImageProvider getCoverImageWay]) { case ProvideImageWayGetWithSize: self.image = [inImageProvider coverImageWithSize:CGSizeMake(44, 44)]; break; case ProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inImageProvider generateImageWithCallback:^(UIImage *image) { weakSelf.image = image; }]; break; } case ProvideImageWayFetchWithURLString: [self fetchImageWithURLString:[inImageProvider coverURLString]]; break; case ProvideImageWayNone: default: break; } }
  22. 22. Testing @interface TCDummyLocalImageProvider: NSObject - (instancetype)initWithType:(ProvideImageWay)inWay; @end @implementation TCDummyLocalImageProvider { ProvideImageWay way; } - (instancetype)initWithType:(ProvideImageWay)inWay { self = [super init]; if (self) { way = inWay; } return self; } - (ProvideImageWay)getCoverImageWay { return way; } ... @end
  23. 23. Testing - (void)testUIImageLoadImageWithImageProvider { UIImageView *imageView = [[UIImageView alloc] init]; [imageView loadImageWithImageProvider:[NSObject new]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:-1000]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:ProvideImageWayGetWithSize]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:...]]; }
  24. 24. This is Protocol-Oriented Programming
  25. 25. One more thing
  26. 26. We are hiring iOS Developer and others
  27. 27. Thanks