CloudCaptions/AAPLPost.m
/* |
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
*/ |
@import CloudKit; |
#import "AAPLPost.h" |
#import "AAPLImage.h" |
typedef NS_ENUM(NSInteger, AAPLPostErrorResponse) { |
AAPLPostErrorIgnore, |
AAPLPostErrorRetry, |
AAPLPostErrorSuccess, |
}; |
@implementation AAPLPost |
- (instancetype) initWithRecord:(CKRecord *)postRecord |
{ |
self = [super init]; |
if(self) |
{ |
_postRecord = postRecord; |
} |
return self; |
} |
- (void) loadImageWithKeys:(NSArray *)keys completion:(void(^)())updateBlock |
{ |
// Fetches the imageRecord this post record references in its ImageRefKey. Only fetches the values associated with the keys passed in to the NSArray |
CKReference *imageRecordReference = self.postRecord[AAPLPostImageRefKey]; |
CKFetchRecordsOperation *imageOp = [[CKFetchRecordsOperation alloc] initWithRecordIDs:@[imageRecordReference.recordID]]; |
imageOp.desiredKeys = keys; |
imageOp.fetchRecordsCompletionBlock = ^(NSDictionary *recordDict, NSError *error) { |
if(error.code == CKErrorPartialFailure) { |
error = error.userInfo[CKPartialErrorsByItemIDKey][[self.postRecord[AAPLPostImageRefKey] recordID]]; |
} |
AAPLPostErrorResponse errorResponse = [self handleError:error]; |
if(errorResponse == AAPLPostErrorSuccess) |
{ |
CKRecord *fetchedImageRecord = recordDict[imageRecordReference.recordID]; |
self.imageRecord = [[AAPLImage alloc] initWithRecord:fetchedImageRecord]; |
updateBlock(); |
} |
else if(errorResponse == AAPLPostErrorRetry) |
{ |
NSNumber *seconds = error.userInfo[CKErrorRetryAfterKey] ?: @3; |
NSLog(@"Error: %@. Recoverable, retry after %@ seconds", [error description], seconds); |
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds.integerValue * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
[self loadImageWithKeys:keys completion:updateBlock]; |
}); |
self.imageRecord = nil; |
} |
else if(errorResponse == AAPLPostErrorIgnore) |
{ |
NSLog(@"Error: %@", [error description]); |
self.imageRecord = nil; |
} |
}; |
CKDatabase *publicDB = [[CKContainer defaultContainer] publicCloudDatabase]; |
[publicDB addOperation:imageOp]; |
} |
- (AAPLPostErrorResponse) handleError:(NSError *)error |
{ |
if(error == nil) return AAPLPostErrorSuccess; |
switch ([error code]) |
{ |
case CKErrorNetworkUnavailable: |
case CKErrorNetworkFailure: |
// A reachability check might be appropriate here so we don't just keep retrying if the user has no service |
case CKErrorServiceUnavailable: |
case CKErrorRequestRateLimited: |
return AAPLPostErrorRetry; |
break; |
case CKErrorBadContainer: |
case CKErrorMissingEntitlement: |
case CKErrorPermissionFailure: |
case CKErrorBadDatabase: |
// This app uses the publicDB with default world readable permissions |
case CKErrorUnknownItem: |
case CKErrorAssetFileNotFound: |
// This shouldn't happen. If an Image record is deleted it should delete all Post records that reference it (CKReferenceActionDeleteSelf) |
case CKErrorIncompatibleVersion: |
case CKErrorQuotaExceeded: |
//App quota will be exceeded, cancelling operation |
case CKErrorOperationCancelled: |
// Nothing to do here, we intentionally cancelled |
case CKErrorNotAuthenticated: |
case CKErrorInvalidArguments: |
case CKErrorResultsTruncated: |
case CKErrorServerRecordChanged: |
case CKErrorAssetFileModified: |
case CKErrorChangeTokenExpired: |
case CKErrorBatchRequestFailed: |
case CKErrorZoneBusy: |
case CKErrorZoneNotFound: |
case CKErrorLimitExceeded: |
case CKErrorUserDeletedZone: |
// These errors are pretty irrelevant here |
// We're fetching only one record by its recordID |
// These errors could be hit fetching multiple records, using zones, saving records, or fetching with predicates |
case CKErrorInternalError: |
case CKErrorServerRejectedRequest: |
case CKErrorConstraintViolation: |
NSLog(@"Nonrecoverable error, will not retry"); |
default: |
return AAPLPostErrorIgnore; |
break; |
} |
} |
@end |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-09-17