Data Persistence — CloudKit

Using CloudKit to save data to iCloud and retrieve it on other devices

Eric Walker
Better Programming

--

Introduction

My last piece was about using NSUbiquitousKeyValueStore to save key-value pairs to iCloud. Well, now it’s time to break out the big guns! Today, we’ll be using CloudKit to create, retrieve, update and delete (C.R.U.D.) data from iCloud.

I would love to do a multi-part series on using everything that CloudKit has to offer, such as C.R.U.D., subscriptions, notifications, and other things along those lines, so I have started here with the easiest (in my opinion) and that is C.R.U.D.

Using CloudKit, even apart from the NSUbiquitousKeyValueStore, is a little more intense than just saving a key-value pair.

So let’s set out a little roadmap of what I want to cover in this piece:

  • The Interface
  • CloudKit Dashboard
  • Saving Records
  • Retrieving Records
  • Updating Records
  • Deleting Records
  • General Notes
  • Demo

What Is CloudKit and How Is It Useful?

CloudKit, at least in my opinion, is a data persistence layer that can really put your app over-the-top. It can give the user a sense of being so connected that they will be poised to continue using your app for the convenience alone. It’s a great feeling when a user is able to start something on their iPhone and then continue it on their iPad or Mac.

Most importantly, it provides the user with the ability to save more complex data than just key-value pairs, which comes in handy when a developer wants to make an even more complex app.

Getting started with CloudKit can be a bit tricky but I’ll be with you every step of the way. If you find the way I describe it difficult to understand, then I suggest heading over to the Apple Developer Documentation on CloudKit.

Requirements

I am going to assume the reader is new to CloudKit because it was such a confusing thing for me to understand at first. However, once you work with it long enough and start to piece things together then it will start to click.

Getting Started

The first thing to do, as always, is to create a new Xcode project. We will select a Single View App for this tutorial (the completed project can be found at the end of this article).

Choose a Single View App for this project

Next, name it whatever you want, for this project I will be naming it CloudKitDemo.

Naming this CloudKitDemo

Click next and save it somewhere where you’ll remember. Now let’s set up some simple capabilities in your app so we can get the CloudKit functionality within it.

Click your app name at the top of the Navigator and choose the Capabilities tab at the top. After that, find the iCloud option in the list of capabilities and turn the switch on. Make sure CloudKit and Use default container are selected. Once those three things are done your app is ready to use CloudKit!

Adding CloudKit to the Capabilities for your app

The last thing we’ll need to do, it’s something I like to do to stay organized, is to make a Variables.swift file so that we have all the variables we’ll need in one place.

To do this, press Command-N, choose Swift File, name it Variables and you should be good to go!

Variable.swift

This is what my final Variables.swift file looks like:

  • Line 2. Importing CloudKit will let me use some of its framework for a variable we’ll define later
  • Line 4. This is an array of Strings that when we retrieve all of our records, all of the titles will go into this variable as multiple strings
  • Line 5. This is an array of CKRecord IDs, for every record you save, iCloud will assign it an ID. When retrieving the records we will put all the IDs into this array

The Interface

As with all my articles, I want the UI to be as simple as can be so you can see and understand the relationship between the code and the interface. By making it as simple as possible, it opens you up to understanding that you can connect the code we write here to any type of IBOutlet, IBAction or even a timer, which will help you when creating an app to give you a bit more freedom than you may think when reading a normal tutorial.

In UI I have set up, I added a text field and 4 buttons. Each button will have its own action for C.R.U.D.

Next, we need to connect the interface to the code. In order to do this, go into the assistant editor by holding down Command-Alt-Enter , control-click the text field, drag it over to ViewController.swift, and release. You have made a IBOutlet — name it whatever you want and do the same for the buttons.

Then control-click the button and drag it over to ViewController.swift , releasing when your cursor is under the ViewDidLoad method. Mine ended up looking like this:

Basic UI for the CloudKitDemo app

If you need more help making IBActions or IBOutlets, check out this YouTube video for more information.

CloudKit Dashboard

In order for you to save data to iCloud, some things have to be set up first. Some of the things I am going to show you are automatically set up when you go to save a record but I would much rather set up everything in the iCloud Dashboard beforehand so we know that everything works.

First thing you’ll want to do is go to the developer dashboard where your iCloud Dashboard lives. On the left side, you’ll see a list of identifiers which are all of the apps that you have flipped the On switch in Xcode for iCloud. Find the app that you created and click on it.

Accessing your iCloud Dashboard

Next, we will set up some things that will help you retrieve data. To retrieve the data you save to iCloud, you need to either query or fetch the data. To make doing either of those possible we have to set up some things in the Schema section of the dashboard. Now just click New Type then just type whatever type of “thing” you want to save.

This part was kind of hard for me to understand at first. The way I began to think of it was as a kind of category of what your app will be. I don’t think that was the best way to explain it, so here is an example: Say you’re making a note-taking app, a good name for this custom type is “Note”. Or if you are making a scorekeeping app, name it “Score”.

This will act somewhat as a “container” for the data you’ll be saving in your app. So going back to the note-taking app example, inside of the custom type “Note” you’ll be saving a title and content for each note. So titles and contents of every note you’ll save in the app will be saved under the custom type of “Note”.

Click new type in the iCloud Dashboard

After you are done entering in a name, click Add Field and enter a “title” without quotes. Make the Field Type a String.

Add field title as a String

Now under the Custom Field of the title you just created there is an Edit Indexes button in blue. Click that and it will bring you to a screen that looks like the following:

Indexes page in the iCloud Dashboard

Click Add Index at the top of the screen, it will give you a row that has two dropdowns, one for the Field Name and one for the Index Type. I prefer to retrieve the records by the latest one that was modified, so I set it like the following:

A final look at how the indexes should be set up

You want to make sure to have at least the modifiedAt be QUERYABLE and SEARCHABLE, and at least recordName be QUERYABLE. Click Save Changes in the lower left-hand corner and you’ll return to this screen:

How your iCloud Dashboard should look when done

The Code

Now with all the boring set up out of the way, it’s time to begin the exciting portion of the tutorial!

Here is all the code for ViewController.swift. This is a lot longer than any of my other tutorials, so instead of going line by line in this whole thing, I’d prefer to go line-by-line and cover each action that will be performed. The first is saveBtn, then retrieveBtn, then updateBtn, and finally deleteBtn.

First off, however:

  • Line 2. import CloudKit just makes it possible to be able to do all the things we need to with CloudKit
  • Line 8. let privateDatabase = CKContainer.default().privateCloudDatabase allows us to shorten our code later in the IBActions. I use a private database instead of public or shared because for this project we don’t need to be sharing any data with anyone else.

Also please don’t be intimidated by the long code! When it is broken up it is a lot easier to understand and follow, trust me!

Saving records

Saving a record is really what starts this whole process, without saving a record first, you cant retrieve, update, or delete anything. Here’s the code for saveBtn:

  • Line 3. This is setting the variable title to the value of textField in the interface
  • Line 5. This is the record of type “Note”. If you entered a different Custom Type from above, enter that in between the quotes for a string instead of Note.
  • Line 7. This is kind of like saving in UserDefaults, you set a value for a key off of the variable record we created above. In the iCloud Dashboard where we entered “title” that’s the key for after the forKey: in the setValue method.
  • Line 9. Now all we do is use the privateDatabase variable we set in the beginning of ViewController.swift and use a .save method. When it asks what record to save, just use the record variable we created above.
  • Line 11–17. This is just error handling at its minimum, if all goes well we should see “Record Saved”.

And that is it for the saving portion of the tutorial, not too bad right? Next, we will focus on retrieving the records back that we have previously saved.

Retrieving records

There are a couple of ways of retrieving records from iCloud. One way is to fetch a record, this only returns one record from the records’ ID you pass to it. The other way is to query all the records. Querying all of the records will retrieve all the records with the sort method you specify.

We are going to query for the records and return them in the order they were modified, which is how we set up the iCloud Dashboard earlier.

  • Line 3. This will just tell our query that we would like the records returned to us sorted in some way, we will tell it in which way we want it in a few lines
  • Line 5. This is the query that will do our retrieving of records, we pass into the predicate we set earlier
  • Line 6. Here we set how we want the query to be sorted, I have entered modificationDate because that’s how we set it up in the iCloud Dashboard
  • Line 8. This is us creating an operation, so when we pass in the query variable, whenever the operation is run, it knows to perform a query
  • Line 10–11. This is just clearing the variables, we do this because if we didn’t and we ran a query several times in one app session, then the records would essentially be duplicated in the variables so we just clear them out so we don’t have that problem
  • Line 13. This is where we actually get the records from iCloud, it’s important here to just append each record into an array and NOT to update any UI in here
  • Line 15–16. Here we are just appending the correct CloudKit types to their respective array variables
  • Line 20–29. This is where you’d update the UI, but be sure to do it in a DispatchQueue.main.async that way you’re not updating UI before the UI is ready to be updated. That part was kind of confusing but just know to update the UI in the queryCompletionBlock and inside of a DispatchQueue.main.async method and you should be OK.
  • Line 31. This just adds the operation to the line of things to happen, without this, the query won’t run, so make sure to pass in your operation variable to this method.

Retrieving data is probably the hardest thing in CloudKit to wrap your mind around. As with anything in this tutorial, if you don’t understand something, please don’t hesitate to contact me or leave a comment down below.

On to updating records!

Updating records

As I stated above, we will be using fetch to update records. This is because we can fetch a record given its record ID. Ideally, we would have all of the data displayed in a UITableView, that way we could get the index of the item you wanted to edit and get the record ID element from the recordIDs variable at that specific index (I’m going to make another tutorial covering that, but we won’t worry about it now).

  • Line 3. Here I am hard setting a string that we will update one of our records’ “title” with this newTitle variable we set
  • Line 5. I am getting a record ID from the array that holds all of them. I am just getting the first one in the array because this is just a demo project, you can customize yours if you want. Also a side note, you have to save and retrieve your records before calling update, so that there are records that you can actually update
  • Line 7. This is where we fetch the record we want to update, we pass in the recordID of the record we want to update and the method returns a variable for us to be able to edit the “title” key but that comes later.
  • Line 9. This is just basic error handling, nothing crazy
  • Line 11. This is where we set the records’ title key to the newTitle variable, it is just like when we were initially saving the record
  • Line 13–23. This is exactly the same as the saveBtn code that we wrote earlier, passing in the record you want to save and do some basic error handling…because it is somewhat important.
  • Line 29. This is the rest of the error handling from when we were fetching the record to begin with

Updating records can be hard to grasp but if you think of it as two different parts, one that fetches a record by its recordID and another that is saving it the same way you did for the saveBtn, then it should be easier to understand.

Deleting records

Deleting records is just about as easy as saving a record. Let’s take a look:

  • Line 3. Just like when updating a variable we need to access a record by its recordID.
  • Line 5–15. Then after getting a recordID for the record we want to delete, we just…delete it! Call delete on the privateDatabase and do some basic error handling…really nothing hard at all!

General notes

And that is it for all of the code! It is really not all that bad. Sadly there is still a lot of information to take in with this article.

My opinion would be to take this article a piece at a time. Start with the top and work your way down. Bookmark this and come back to it later. Don’t get intimidated by the amount of information given here.

CloudKit is not something you want to let defeat you mentally, because once you’ve been looking through so many different tutorials, it can get extremely confusing. Hopefully, this one could be the only one you’ll ever need (at least as far as C.R.U.D. goes).

When working with a real app, make sure to do a check every time the app launches to make sure the user of the app is signed into CloudKit. If they’re not, either ask them to sign in or provide a second data persistent layer to make sure none of their data gets lost.

Demo

All you have to do now is run the scheme that deploys the app to the iPhone simulator (it may take a few minutes if it’s the first time running the app to the simulator).

Type a message into the text field and click the save button.

Demo for the CloudKitDemoApp
  1. Type a “title” into the textField and click Save — you should see Record Saved in the output screen
  2. Click Retrieve and you should see the titles array output with our one record that we have saved and the recordIDs array output with the ID of the one record we’ve saved
  3. Click Update and it will fetch that ID we have for our one record and update it with the new title we have coded into the update function. Click Retrieve again and you should see the new title in the output screen
  4. Click Delete and it will access that ID we have for our one record and it will delete it
  5. Click Retrieve again and you should see two empty brackets in the output screen looking like this: []

Conclusion

I say “that’s it for CloudKit — C.R.U.D” but there is a lot to it.

Using iCloud in your app though will no doubt be really good for user retention and making sure every user can feel as connected as possible using your app. Including CloudKit in your app is definitely worth the time investment if you can make it through all the thick information.

I am continuing on in my quest of attempting to make easy to follow tutorials covering harder topics in the Swift ecosystem.

If anyone at all has any issues with my tutorial or have any questions please don’t hesitate to send me a message on here or leave a reply on this and I will respond as quickly as possible.

Thanks all for reading!

Project Files

Here’s a link to the GitHub project for this tutorial.

--

--

Studying Mechanical Engineering at The University of Akron. Cancer survivor since 2015.