public void LoadImage(string[] keys, Action updateBlock = null) { // Fetches the imageRecord this post record references in its ImageRefKey. // Only fetches the values associated with the keys passed in to the NSArray var imgRecordId = ((CKReference)PostRecord[ImageRefKey]).RecordId; CKFetchRecordsOperation imageOp = new CKFetchRecordsOperation(new CKRecordID[] { imgRecordId }); imageOp.DesiredKeys = keys; imageOp.Completed = (NSDictionary recordDict, NSError error) => { if (error != null && error.Code == (long)CKErrorCode.PartialFailure) { CKErrorInfo info = new CKErrorInfo(error); error = info[imgRecordId]; } Error errorResponse = HandleError(error); switch (errorResponse) { case Error.Success: CKRecord fetchedImageRecord = (CKRecord)recordDict[imgRecordId]; ImageRecord = new Image(fetchedImageRecord); if (updateBlock != null) { updateBlock(); } break; case Error.Retry: Utils.Retry(() => LoadImage(keys, updateBlock), error); ImageRecord = null; break; case Error.Ignore: Console.WriteLine("Error: {0}", error.Description); ImageRecord = null; break; default: throw new NotImplementedException(); } }; PublicDB.AddOperation(imageOp); }
void PublishPost(NSObject sender) { // Prevents multiple posting, locks as soon as a post is made PostButton.Enabled = false; UIActivityIndicatorView indicator = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.Gray); indicator.StartAnimating(); PostButton.CustomView = indicator; // Hides the keyboards and dispatches a UI update to show the upload progress HiddenText.EndEditing(true); TagField.EndEditing(true); ProgressBar.Hidden = false; // Creates post record type and initizalizes all of its values CKRecord newRecord = new CKRecord(Post.RecordType); newRecord [Post.FontKey] = (NSString)ImageLabel.Font.Name; newRecord [Post.ImageRefKey] = new CKReference(ImageRecord.Record.Id, CKReferenceAction.DeleteSelf); newRecord [Post.TextKey] = (NSString)HiddenText.Text; string[] tags = TagField.Text.ToLower().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); newRecord [Post.TagsKey] = NSArray.FromObjects(tags); Post newPost = new Post(newRecord); newPost.ImageRecord = ImageRecord; // Only upload image record if it is not on server, otherwise just upload the new post record CKRecord[] recordsToSave = ImageRecord.IsOnServer ? new CKRecord[] { newRecord } : new CKRecord[] { newRecord, ImageRecord.Record }; // TODO: https://trello.com/c/A9T8Spyp second param is null CKModifyRecordsOperation saveOp = new CKModifyRecordsOperation(recordsToSave, new CKRecordID[0]); saveOp.PerRecordProgress = (CKRecord record, double progress) => { // Image record type is probably going to take the longest to upload. Reflect it's progress in the progress bar if (record.RecordType == Image.RecordType) { InvokeOnMainThread(() => { var val = (float)(progress * 0.95); ProgressBar.SetProgress(val, true); }); } }; // When completed it notifies the tableView to add the post we just uploaded, displays error if it didn't work saveOp.Completed = (CKRecord[] savedRecords, CKRecordID[] deletedRecordIDs, NSError operationError) => { Error errorResponse = HandleError(operationError); switch (errorResponse) { case Error.Success: // Tells delegate to update so it can display our new post InvokeOnMainThread(() => { DismissViewController(true, null); MainController.Submit(newPost); }); break; case Error.Retry: CKErrorInfo errorInfo = new CKErrorInfo(operationError.UserInfo); nint retryAfter = errorInfo.RetryAfter.HasValue ? errorInfo.RetryAfter.Value : 3; Console.WriteLine("Error: {0}. Recoverable, retry after {1} seconds", operationError.Description, retryAfter); Task.Delay((int)retryAfter * 1000).ContinueWith(_ => PublishPost(sender)); break; case Error.Ignore: Console.WriteLine("Error saving record: {0}", operationError.Description); string errorTitle = "Error"; string dismissButton = "Okay"; string errorMessage = operationError.Code == (long)CKErrorCode.NotAuthenticated ? "You must be logged in to iCloud in order to post" : "Unrecoverable error with the upload, check console logs"; InvokeOnMainThread(() => { UIAlertController alert = UIAlertController.Create(errorTitle, errorMessage, UIAlertControllerStyle.Alert); alert.AddAction(UIAlertAction.Create(dismissButton, UIAlertActionStyle.Cancel, null)); PostButton.Enabled = true; PresentViewController(alert, true, null); ProgressBar.Hidden = true; PostButton.CustomView = null; }); break; default: throw new NotImplementedException(); } }; CKContainer.DefaultContainer.PublicCloudDatabase.AddOperation(saveOp); }
// This method fetches the whole ImageRecord that a user taps on and then passes it to the delegate public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath) { // If the user has already tapped on a thumbnail, prevent them from tapping any others if (controller.lockSelectThumbnail) { return; } else { controller.lockSelectThumbnail = true; } // Starts animating the thumbnail to indicate it is loading controller.ImageCollection.SetLoadingFlag(indexPath, true); // Uses convenience API to fetch the whole image record associated with the thumbnail that was tapped CKRecordID userSelectedRecordID = controller.ImageCollection.GetRecordId(indexPath); controller.PublicCloudDatabase.FetchRecord(userSelectedRecordID, (record, error) => { // If we get a partial failure, we should unwrap it if (error != null && error.Code == (long)CKErrorCode.PartialFailure) { CKErrorInfo info = new CKErrorInfo(error); error = info[userSelectedRecordID]; } Error errorResponse = controller.HandleError(error); switch (errorResponse) { case Error.Success: controller.ImageCollection.SetLoadingFlag(indexPath, false); Image selectedImage = new Image(record); InvokeOnMainThread(() => { controller.MasterController.GoTo(controller, selectedImage); }); break; case Error.Retry: Utils.Retry(() => { controller.lockSelectThumbnail = false; ItemSelected(collectionView, indexPath); }, error); break; case Error.Ignore: Console.WriteLine("Error: {0}", error.Description); string errorTitle = "Error"; string errorMessage = "We couldn't fetch the full size thumbnail you tried to select, try again"; string dismissButton = "Okay"; UIAlertController alert = UIAlertController.Create(errorTitle, errorMessage, UIAlertControllerStyle.Alert); alert.AddAction(UIAlertAction.Create(dismissButton, UIAlertActionStyle.Cancel, null)); InvokeOnMainThread(() => controller.PresentViewController(alert, true, null)); controller.ImageCollection.SetLoadingFlag(indexPath, false); controller.lockSelectThumbnail = false; break; default: throw new NotImplementedException(); } }); }
static int FetchRetryDelay(NSError error) { CKErrorInfo errorInfo = new CKErrorInfo(error.UserInfo); return((int)(errorInfo.RetryAfter.HasValue ? errorInfo.RetryAfter.Value : 3)); }