private void OnRecordFetched(CKRecord record, NSError error)
    {
        if (error != null)
        {
            Debug.LogError(error.LocalizedDescription);
        }
        else
        {
            Debug.Log(string.Format("Record fetched"));

            // CKRecords have a bunch of useful metadata
            Debug.Log("CreationDate: " + record.CreationDate);
            Debug.Log("ModificationDate: " + record.ModificationDate);

            // The record change tag is an id that helps you keep track of the
            // most up to date version
            Debug.Log("RecordChangeTag: " + record.RecordChangeTag);

            // The parent of this record (if any). Parent child relationships
            // can be created using SetRefereneceForKey
            Debug.Log("Parent: " + record.Parent);

            Debug.Log("Type: " + record.RecordType);
            Debug.Log("Share: " + record.Share);

            // The user id of the account to create/modify this record
            // in a private DB I think this is just always the current user
            Debug.Log("CreatorUserRecordID: " + record.CreatorUserRecordID);
            Debug.Log("Last Modified User Record Id: " + record.LastModifiedUserRecordID);

            ModifyRecord();
        }
    }
Esempio n. 2
0
 void Init(CKRecord record, bool onServer)
 {
     IsOnServer = onServer;
     Record     = record;
     Thumbnail  = LoadImage(ThumbnailKey);
     FullImage  = LoadImage(FullsizeKey);
 }
    public void Cant_create_CKShare_with_no_record()
    {
        CKRecord     record = null;
        TestDelegate sut    = () => new CKShare(record);

        Assert.Throws <CloudKitException>(sut);
    }
Esempio n. 4
0
        async void SaveSearchRequest(string searchRequestValue)
        {
            var newRecord = new CKRecord("SearchRequest");

            newRecord ["value"] = (NSString)searchRequestValue;
            await cloudManager.SaveAsync(newRecord);
        }
Esempio n. 5
0
		void Init (CKRecord record, bool onServer)
		{
			IsOnServer = onServer;
			Record = record;
			Thumbnail = LoadImage (ThumbnailKey);
			FullImage = LoadImage (FullsizeKey);
		}
    public void Can_create_CKShare()
    {
        var record = new CKRecord("root_record_type");
        var share  = new CKShare(record);

        Assert.AreEqual(share.RecordID, record.RecordID);
    }
Esempio n. 7
0
        partial void TakePhoto(NSObject sender)
        {
            var imagePicker = new UIImagePickerController();
            var sourceType  = UIImagePickerControllerSourceType.PhotoLibrary;

            if (UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
            {
                sourceType = UIImagePickerControllerSourceType.Camera;
            }

            imagePicker.SourceType            = sourceType;
            imagePicker.FinishedPickingMedia += async(picker, e) => {
                var spinner = ActivityIndicator;
                spinner.Hidden = false;
                imagePicker.View.AddSubview(spinner);
                spinner.StartAnimating();

                UIImage image   = e.Info [UIImagePickerController.OriginalImage] as UIImage;
                var     newSize = new SizeF(512, 512);

                if (image.Size.Width > image.Size.Height)
                {
                    newSize.Height = (int)(newSize.Width * image.Size.Height / image.Size.Width);
                }
                else
                {
                    newSize.Width = (int)(newSize.Height * image.Size.Width / image.Size.Height);
                }

                UIGraphics.BeginImageContext(newSize);
                image.Draw(new RectangleF(0, 0, newSize.Width, newSize.Height));
                var data = UIGraphics.GetImageFromCurrentImageContext().AsJPEG(0.75f);
                UIGraphics.EndImageContext();

                NSError error;
                var     cachesDirectory = NSFileManager.DefaultManager.GetUrl(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, null, true, out error);
                var     temporaryName   = new NSUuid().AsString() + "jpeg";
                var     localUrl        = cachesDirectory.Append(temporaryName, false);
                data.Save(localUrl, true, out error);

                CKRecord record = await CloudManager.UploadAssetAsync(localUrl);

                if (record == null)
                {
                    return;
                }

                spinner.RemoveFromSuperview();
                assetRecordName = record.RecordId.RecordName;

                var alert = new UIAlertView("CloudKitAtlas", "Successfully Uploaded", null, "OK", null);
                alert.Show();

                DismissViewController(true, null);
            };

            imagePicker.Canceled += (picker, e) => DismissViewController(true, null);

            PresentViewController(imagePicker, true, null);
        }
    private void Run()
    {
        Debug.Log("Example 6 - Per Record Progress");

        database = CKContainer.DefaultContainer().PrivateCloudDatabase;

        // Let's create 5 files at 1MB a piece
        // So we have enough data to see some upload progress

        // BTW CloudKit recommends that if your data exceeds 1MB you should put
        // it inside a CKAsset

        for (int i = 0; i < numFiles; i++)
        {
            var    record = new CKRecord("BigFile");
            byte[] bytes  = new byte[1000000];
            record.SetBuffer(bytes, "bytes");

            records[i] = record;
        }

        var op = new CKModifyRecordsOperation(records, null);

        op.Configuration.QualityOfService = NSQualityOfService.UserInitiated;

        op.PerRecordProgressBlock       = OnPerRecordProgress;
        op.PerRecordCompletionBlock     = OnPerRecordComplete;
        op.ModifyRecordsCompletionBlock = OnRecordsSaved;

        database.AddOperation(op);
    }
    void Run()
    {
        Debug.Log("Example 4 - CKAsset");
        database = CKContainer.DefaultContainer().PrivateCloudDatabase;

        var record = new CKRecord("MyType");

        // CloudKit recommends if your data is larger than 1MB that you store
        // it as an asset...
        // our example won't be anything close to 1MB, but it'll illustrate the
        // technique

#if UNITY_TVOS
        string path = Path.Combine(Application.temporaryCachePath, "Asset.bytes");
#else
        string path = Path.Combine(Application.persistentDataPath, "Asset.bytes");
#endif

        byte[] bytes = Encoding.ASCII.GetBytes("AssetData");
        File.WriteAllBytes(path, bytes);

        // Assets have to be files, so you pass it the filepath to something
        // in the user's data directory
        // the asset will be stored in cloudkit, with a URL for retrieval

        var fileurl = NSURL.FileURLWithPath(path);
        record.SetAsset(new CKAsset(fileurl), "MyAsset");

        database.SaveRecord(record, OnRecordSaved);
    }
Esempio n. 10
0
        // Creates an Image from a UIImage (photo was taken from camera or photo library)
        public Image(UIImage image)
        {
            if (image == null)
            {
                throw new ArgumentNullException("image");
            }

            CGImage rawImage = image.CGImage;

            using (CGImage cropped = Crop(rawImage)) {
                // Resizes image to be 1500 x 1500 px and saves it to a temporary file
                UIImage fullImage = Resize(cropped, image.CurrentScale, image.Orientation, new CGSize(1500, 1500));
                var     fullUrl   = SaveToTmp(fullImage, "toUploadFull.tmp");

                // Resizes thumbnail to be 200 x 200 px and then saves to different temp file
                UIImage thumbImage = Resize(cropped, image.CurrentScale, image.Orientation, new CGSize(200, 200));
                var     thumbURL   = SaveToTmp(thumbImage, "toUploadThumb.tmp");

                // Creates Image record type with two assets, full sized image and thumbnail sized image
                CKRecord newImageRecord = new CKRecord(RecordType);
                newImageRecord [FullsizeKey]  = new CKAsset(fullUrl);
                newImageRecord [ThumbnailKey] = new CKAsset(thumbURL);

                // Calls designated initalizer, this is a new image so it is not on the server
                Init(newImageRecord, false);
            }
        }
		Results ProcessResult (CKRecord record)
		{
			var results = new Results ();
			if (record != null)
				results.Items.Add (new CKRecordWrapper (record));

			return results;
		}
Esempio n. 12
0
		public async Task DeleteAsync (CKRecord record)
		{
			try {
				await privateDatabase.DeleteRecordAsync (record.Id);
			} catch (Exception e) {
				Console.WriteLine ("An error occured: {0}", e.Message);
			}
		}
		public async Task<CKRecord> UploadAssetAsync (NSUrl assetUrl)
		{
			var assetRecord = new CKRecord (PhotoAssetRecordType);
			var photo = new CKAsset (assetUrl);
			assetRecord [PhotoAssetField] = photo;

			return await publicDatabase.SaveRecordAsync (assetRecord);
		}
Esempio n. 14
0
    public void Can_create_record_of_a_type()
    {
        var recordType = "record_type";

        var record = new CKRecord(recordType);

        Assert.AreEqual(record.RecordType, recordType);
    }
Esempio n. 15
0
 public async Task DeleteAsync(CKRecord record)
 {
     try {
         await privateDatabase.DeleteRecordAsync(record.Id);
     } catch (Exception e) {
         Console.WriteLine("An error occured: {0}", e.Message);
     }
 }
Esempio n. 16
0
    public void RecordName_is_automatically_generated_if_not_set()
    {
        var recordType = "record_type";

        var record = new CKRecord(recordType);

        Assert.IsFalse(string.IsNullOrEmpty(record.RecordID.RecordName));
    }
Esempio n. 17
0
    public void Record_added_to_share_has_share()
    {
        var rootRecord = new CKRecord("root_record_type");

        var share = new CKShare(rootRecord);

        Assert.AreEqual(rootRecord.Share, share);
    }
		public async Task<CKRecord> AddRecordAsync (string name, CLLocation location)
		{
			var newRecord = new CKRecord (ItemRecordType);
			newRecord [NameField] = (NSString)name;
			newRecord [LocationField] = location;

			return await publicDatabase.SaveRecordAsync (newRecord);
		}
        public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            var      cell   = TableView.DequeueReusableCell(new NSString("cell"), indexPath);
            CKRecord record = records[indexPath.Row];

            cell.TextLabel.Text = (NSString)record["name"];
            return(cell);
        }
    private void Run()
    {
        Debug.Log("Example 3 - Querying");
        database = CKContainer.DefaultContainer().PrivateCloudDatabase;

        // We need a bunch of records to search through
        names = new string[] {
            "Alice",
            "Bob",
            "Charles",
            "Danni",
            "Exavier"
        };

        // Make an array of CKRecords and set the name field to each of the
        // names in the array above...
        //
        recordsToSearch = names.Select(name => {
            var record = new CKRecord("Person");
            record.SetString(name, "name");
            return(record);
        }).ToArray();

        // CloudKit uses a CKModifyRecrodsOperation for both saving and deleting
        // (which is sorta wierd). The first parameter is records to save, the
        // second is record id's to delete
        //
        Debug.Log("Creating records");
        CKModifyRecordsOperation op = new CKModifyRecordsOperation(
            recordsToSearch,
            null
            );

        // Important to set quality of service to UserInitiated or cloudkit
        // may run your query a LONG time from now. Like minutes from now
        // (seriously). The default value of NSQualityOfServiceUtility is insane
        // You can read more about QoS here:
        // https://developer.apple.com/library/archive/documentation/Performance/Conceptual/EnergyGuide-iOS/PrioritizeWorkWithQoS.html
        op.Configuration.QualityOfService = NSQualityOfService.UserInitiated;

        // The modify records completion block is a callback function that's
        // invoked when the operation is complete
        op.ModifyRecordsCompletionBlock = OnRecordsSaved;

        database.AddOperation(op);

        var op2 = new CKFetchRecordsOperation(recordsToSearch.Select(x => x.RecordID).ToArray());

        op2.FetchRecordsCompletionHandler = (dictionary, error) =>
        {
            Debug.Log("Fetch records complete");
            //foreach (var kvp in dictionary)
            //{
            //    Debug.Log(string.Format("key:{0} value:{1}", kvp.Key.RecordName, kvp.Value));
            //}
        };
        database.AddOperation(op2);
    }
Esempio n. 21
0
    public void Can_set_parent_reference_on_record_using_the_record()
    {
        var parent = new CKRecord("parent_record_type");
        var child  = new CKRecord("child_record_type");

        child.SetParentReferenceFromRecord(parent);

        Assert.AreEqual(child.Parent.RecordID, parent.RecordID);
    }
		public void AddImageFromRecord(CKRecord toAdd)
		{
			Image fetchedImage = new Image (toAdd);
			// Ensures that only one object will be added to the imageRecords array at a time
			updateArrayQueue.DispatchAsync (() => {
				imageRecords.Add (fetchedImage);
				InvokeOnMainThread (ReloadData);
			});
		}
    public void Cant_create_CKShare_with_null_share_id()
    {
        CKRecordID shareId    = null;
        var        rootRecord = new CKRecord("rootRecord");

        TestDelegate sut = () => new CKShare(rootRecord, shareId);

        Assert.Throws <CloudKitException>(sut);
    }
Esempio n. 24
0
    public void Cant_encode_system_fields_without_coder()
    {
        NSCoder coder  = null;
        var     record = new CKRecord("record_type");

        TestDelegate sut = () => record.EncodeSystemFieldsWithCoder(coder);

        Assert.Throws <CloudKitException>(sut);
    }
Esempio n. 25
0
        public async Task <CKRecord> UploadAssetAsync(NSUrl assetUrl)
        {
            var assetRecord = new CKRecord(PhotoAssetRecordType);
            var photo       = new CKAsset(assetUrl);

            assetRecord [PhotoAssetField] = photo;

            return(await publicDatabase.SaveRecordAsync(assetRecord));
        }
Esempio n. 26
0
        public async Task <CKRecord> AddRecordAsync(string name, CLLocation location)
        {
            var newRecord = new CKRecord(ItemRecordType);

            newRecord [NameField]     = (NSString)name;
            newRecord [LocationField] = location;

            return(await publicDatabase.SaveRecordAsync(newRecord));
        }
        public void MarkRecordAsModified(CKRecord record)
        {
            var index = IndexOfRecordByRecordID(record.Id);

            if (index >= 0)
            {
                Results.Modified.Add(index);
            }
        }
    public void Can_create_CKShare_with_root_record_and_share_id()
    {
        var shareId    = new CKRecordID("shareId");
        var rootRecord = new CKRecord("root_record_type");

        var share = new CKShare(rootRecord, shareId);

        Assert.AreEqual(share.RecordID, shareId);
    }
Esempio n. 29
0
		// Creates an Image from a CKRecord that has been fetched
		public Image(CKRecord record, bool onServer = true)
		{
			if (record == null)
				throw new ArgumentNullException ("record");

			if (record.RecordType != RecordType)
				throw new ArgumentException ("Wrong type for image record");

			Init (record, onServer);
		}
Esempio n. 30
0
    public void Can_create_record_of_a_type_in_a_zone()
    {
        var recordType = "record_type";
        var zoneName   = "zone_name";
        var recordZone = new CKRecordZone(zoneName);

        var record = new CKRecord(recordType, recordZone.ZoneID);

        Assert.AreEqual(record.RecordID.ZoneID, recordZone.ZoneID);
    }
    private void OnRecordSaved(CKRecord record, NSError error)
    {
        Debug.Log("OnRecordSaved");
        if (error != null)
        {
            Debug.LogError("Could not save record: " + error.LocalizedDescription);
        }

        database.FetchRecordWithID(record.RecordID, OnRecordFetched);
    }
Esempio n. 32
0
    public void Can_set_and_retrieve_asset_by_key()
    {
        var record   = new CKRecord("record_type");
        var asset    = new CKAsset(NSURL.URLWithString("file:///someasset.bytes"));
        var assetKey = "asset_key";

        record.SetAsset(asset, assetKey);

        Assert.AreEqual(record.AssetForKey(assetKey), asset);
    }
Esempio n. 33
0
    public void Can_set_and_retrieve_reference_by_key()
    {
        var record       = new CKRecord("record_type");
        var reference    = new CKReference(new CKRecord("record_type"), CKReferenceAction.DeleteSelf);
        var referenceKey = "reference_key";

        record.SetReference(reference, referenceKey);

        Assert.AreEqual(record.ReferenceForKey(referenceKey), reference);
    }
Esempio n. 34
0
    public void Can_set_parent_reference_on_record()
    {
        var parent          = new CKRecord("parent_record_type");
        var child           = new CKRecord("child_record_type");
        var parentReference = new CKReference(parent, CKReferenceAction.None);

        child.Parent = parentReference;

        Assert.AreEqual(child.Parent.RecordID, parent.RecordID);
    }
Esempio n. 35
0
        public async Task SaveAsync(CKRecord record)
        {
            try {
                await publicDatabase.SaveRecordAsync(record);

                Console.WriteLine("Successfuly saved record!");
            } catch (Exception e) {
                Console.WriteLine("An error occured: {0}", e.Message);
            }
        }
Esempio n. 36
0
    public void Cant_create_record_with_an_invalid_type(
        [Values(null, "", "type with spaces", "_underscore", "2number")] string recordType)
    {
        TestDelegate sut = () =>
        {
            var record = new CKRecord(recordType);
        };

        Assert.Throws <Exception>(sut);
    }
        public void AddImageFromRecord(CKRecord toAdd)
        {
            Image fetchedImage = new Image(toAdd);

            // Ensures that only one object will be added to the imageRecords array at a time
            updateArrayQueue.DispatchAsync(() => {
                imageRecords.Add(fetchedImage);
                InvokeOnMainThread(ReloadData);
            });
        }
		async partial void Add (UIButton sender)
		{
			if (string.IsNullOrEmpty (nameTextField.Text))
				nameTextField.ResignFirstResponder ();
			else {
				var newRecord = new CKRecord (ReferenceItemRecordName);
				newRecord ["name"] = (NSString)nameTextField.Text;
				await CloudManager.SaveAsync (newRecord);

				records.Insert (0, newRecord);
				var indexPath = NSIndexPath.FromRowSection (0, 0);
				TableView.InsertRows (new [] { indexPath }, UITableViewRowAnimation.Automatic);
				nameTextField.ResignFirstResponder ();
				nameTextField.Text = string.Empty;
			}
		}
Esempio n. 39
0
		// Creates an Image from a UIImage (photo was taken from camera or photo library)
		public Image (UIImage image)
		{
			if (image == null)
				throw new ArgumentNullException ("image");

			CGImage rawImage = image.CGImage;
			using (CGImage cropped = Crop (rawImage)) {
				// Resizes image to be 1500 x 1500 px and saves it to a temporary file
				UIImage fullImage = Resize (cropped, image.CurrentScale, image.Orientation, new CGSize (1500, 1500));
				var fullUrl = SaveToTmp (fullImage, "toUploadFull.tmp");

				// Resizes thumbnail to be 200 x 200 px and then saves to different temp file
				UIImage thumbImage = Resize (cropped, image.CurrentScale, image.Orientation, new CGSize (200, 200));
				var thumbURL = SaveToTmp (thumbImage, "toUploadThumb.tmp");

				// Creates Image record type with two assets, full sized image and thumbnail sized image
				CKRecord newImageRecord = new CKRecord (RecordType);
				newImageRecord [FullsizeKey] = new CKAsset (fullUrl);
				newImageRecord [ThumbnailKey] = new CKAsset (thumbURL);

				// Calls designated initalizer, this is a new image so it is not on the server
				Init (newImageRecord, false);
			}
		}
Esempio n. 40
0
		void OnPostFetched(CKRecord record)
		{
			// When we get a record, use it to create an Post
			Post fetchedPost = new Post (record);
			downloadingBatchStorage.Add (fetchedPost);

			fetchedPost.LoadImage (new string[]{ Image.FullsizeKey }, () => {
				// Once image is loaded, tell the tableview to reload
				DispatchQueue.MainQueue.DispatchAsync (reloadHandler);
			});
		}
Esempio n. 41
0
		void OnLoadNewPostFetchRecord(CKRecord record, List<Post> newPosts, ref Post lastRecordInOperation)
		{
			// If the record we just fetched doesn't match recordIDs to any item in our newPosts array, let's make an Post and add it
			var matchingRecord = newPosts.FindIndex (p => p.PostRecord.Id.Equals(record.Id));
			if (matchingRecord == -1) {
				Post fetchedPost = new Post (record);
				newPosts.Add (fetchedPost);
				fetchedPost.LoadImage(new string[] { Image.FullsizeKey }, reloadHandler);
				lastRecordInOperation = fetchedPost;
			} else {
				// If we already have this record we don't have to fetch. We'll still update lastRecordInOperation because we did see it on the server
				lastRecordInOperation = newPosts[matchingRecord];
			}
		}
		public async Task SaveAsync (CKRecord record)
		{
			try {
				await publicDatabase.SaveRecordAsync (record);
				Console.WriteLine ("Successfuly saved record!");
			} catch (Exception e) {
				Console.WriteLine ("An error occured: {0}", e.Message);
			}
		}
Esempio n. 43
0
		public CKRecordWrapper (CKRecord record)
		{
			Record = record;
		}
		public void AddRecord (CKRecord record)
		{
			Results.Items.Add (new CKRecordWrapper (record));
			recordsByID [record.Id] = record;
			Results.Added.Add (Results.Items.Count - 1);
		}
		async void SaveSearchRequest (string searchRequestValue)
		{
			var newRecord = new CKRecord ("SearchRequest");
			newRecord ["value"] = (NSString)searchRequestValue;
			await cloudManager.SaveAsync (newRecord);
		}
Esempio n. 46
0
		public Post (CKRecord postRecord)
		{
			PostRecord = postRecord;
		}
Esempio n. 47
0
		public PatronSubscription (CKRecord record)
		{
			Record = record;
		}
		public void MarkRecordAsModified (CKRecord record)
		{
			var index = IndexOfRecordByRecordID (record.Id);
			if (index >= 0)
				Results.Modified.Add (index);
		}
		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);
		}