Docs Menu

Docs HomeAtlas App Services

SwiftUI iOS Tutorial

On this page

  • Overview
  • Prerequisites
  • Start with the Template
  • Explore the Template App
  • Open the App
  • Explore the App Structure
  • Run the App
  • Check the Backend
  • Modify the Application
  • Add a New Property
  • Add a Property to the Model
  • Set the Priority when Creating a New Item
  • Run and Test
  • Change the Subscription
  • Update the subscription
  • Run and Test
  • Update Flexible Sync on the Server
  • Test the changes
  • Conclusion
  • What's Next?

Realm provides a Swift SDK that allows you to create a native iOS mobile application with Swift or Objective-C. This tutorial is based on the SwiftUI Flexible Sync Template App, named swiftui.todo.flex, which illustrates the creation of a to-do list application. This application enables users to:

  • Register their email as a new user account.

  • Sign in to their account with their email and password (and sign out later).

  • View, create, modify, and delete tasks.

This tutorial adds on to the Template App. You will add a new priority field to the existing Item model and update the Flexible Sync subscription to only show items within a range of priorities.

Depending on your experience with SwiftUI, this tutorial should take around 30 minutes.

Note

Check Out the Quick Start

If you prefer to get started with your own application rather than follow a guided tutorial, check out the Swift Quick Start. It includes copyable code examples and the essential information that you need to set up an Atlas App Services backend.

  • Ensure that you have the necessary software installed. The Swift SDK requires Xcode version 13.1 or newer.

  • This tutorial starts with a Template App. You need an Atlas Account, an API key, and realm-cli to create a Template App.

    • You can learn more about creating an Atlas account in the Atlas Getting Started documentation. For this tutorial, you need an Atlas account with a free-tier cluster.

    • You also need an Atlas API key for the MongoDB Cloud account you wish to log in with. You must be a Project Owner to create a Template App using realm-cli.

    • To learn more about installing realm-cli, see Install realm-cli. After you have installed realm-cli, login using the API key for your Atlas project.

This tutorial is based on the SwiftUI Flexible Sync Template App named swiftui.todo.flex. We start with the default app and build new features on it.

From Realm CLI, you can run the realm-cli apps create command to set up the backend and create the SwiftUI base app. The following command creates a new app based on the swiftui.todo.flex template. With the options flags we can pass during creation, the command below creates an app:

  • Named "MyTutorialApp"

  • Deployed in the US-VA region

  • With the environment set to "development" (instead of production or QA)

realm-cli apps create -n MyTutorialApp --template swiftui.todo.flex \
--deployment-model global --location us-va --environment development

To learn more about the Template Apps, and to install the Template App that this tutorial uses in the Atlas App Services UI, see template applications.

1

Navigate to the directory where the Realm CLI created the Template App. The client code is in a frontend/swiftui.todo.flex directory. Open the App.xcodeproj in Xcode.

2

Take a few minutes to explore how the project is organized while Swift Package Manager downloads the latest version of the Realm Swift SDK. Within the App directory, you can see a few files worth noting:

File
Purpose
AppConfig.swift
This file contains the logic to read the appId and baseUrl from the Realm.plist. This is pre-populated with the appId for your Template App.
realmApp.swift

This file uses the values from AppConfig.swift to initialize the realmApp. The realmApp is how your app communicates with the App Services backend. This provides access to login and authentication.

To learn more about how you can customize your app configuration, see: Connect to an Atlas App Services Backend.

This file is also the entrypoint to the SwiftUI app. We pass the realmApp to the ContentView that observes the app state for user authentication state.

In this tutorial, you'll be working in the following files:

File
Purpose
Item.Swift
This file, located at the root of the project, defines the Realm object we store in the database.
CreateItemView.swift
This file, located in the Views directory, provides the functionality to add a new item to the list.
ContentView.Swift
This file, located in the Views directory, defines the Flexible Sync subscription.
3

Without making any changes to the code, you should be able to run the app in the iOS Simulator or on a physical device. When you installed the Template App, the Realm CLI also set up a new backend for you and populated the realm.json file with the correct app ID.

Run the app, register a new user account, and then add a new Item to your todo list.

4

Log in to Atlas App Services. In the Atlas tab, click on Browse Collections. In the list of databases, find and expand the Todo database, and then the Item collection. You should see the document you created in this collection.

1

Now that you have confirmed everything is working as expected, we can add changes. In this tutorial, we have decided that we want to add a "priority" property to each Item so that we can filter Items by their priorities. The priority property uses a PriorityLevel enum to constrain the possible values.

To do this, follow these steps:

  1. Open the App.xcodeproj in Xcode.

  2. Open the Item.swift class file.

  3. Add the following property to the Item class:

    @Persisted var priority: PriorityLevel
  4. Also add a PriorityLevel PersistableEnum below the Item class:

    enum PriorityLevel: Int, PersistableEnum, CaseIterable {
    case severe = 0
    case high = 1
    case medium = 2
    case low = 3
    var description: String {
    switch self {
    case .severe: return "Severe"
    case .high: return "High"
    case .medium: return "Medium"
    case .low: return "Low"
    }
    }
    }

    PersistableEnum is the protocol that marks enum types as persistable directly in Realm. We set the enum's type as Int here instead of String so we can query based on a numeric priority level later. We use a description computed property to display a nice string representation of the priority in the UI.

2
  1. In the Views directory, go to CreateItemView.swift. Add a new @State property under the existing itemSummary property. For now, set the default value to medium priority:

    @State var itemSummary = ""
    @State var priority = PriorityLevel.medium
  2. Now, in the Form body, add a Picker that enables the user to choose which priority level to set on the new Item. Locate the Section that contains the buttons, and insert the following code above it:

    Section(header: Text("Priority")) {
    Picker(selection: $priority, label: Text("Set priority")) {
    ForEach(PriorityLevel.allCases, id: \.self) { priority in
    Text(priority.description)
    }
    }
    }
  3. Now, move down to the Button(action: that sets the values of the newItem when the user presses the Save button. Add a line below newItem.summary to also set the priority property:

    newItem.summary = itemSummary
    newItem.priority = priority
3

At this point, you can run the application again. Log in using the account you created earlier in this tutorial. You will see the one Item you previously created. Add a new Item, and you will see that you can now set the priority. Choose High for the priority and save the Item.

Now switch back to the Atlas data page in your browser, and refresh the Item collection. You should now see the new Item with the priority field added and set to 1. The existing Item does not have a priority field.

Two items in a collection

Note

Why Didn't This Break Sync?

Adding a property to a Realm object is not a breaking change and therefore does not require a client reset. The template app has Development Mode enabled, so changes to the client Realm object are reflected in the server-side schema. For more information, see Development Mode and Update Your Data Model.

1

In the ContentView.swift file, we create the Flexible Sync subscription that defines which documents we sync with the user's device & account. Look for the let config = user.flexibleSyncConfiguration(initialSubscriptions: variable where we set the initial subscriptions. Within the subscriptions.append() method, you can see that we are currently subscribing to all documents where the owner_id property matches the authenticated user's id. We want to maintain that, but only sync Items that are marked as High or Severe priority.

This is why we set the PriorityLevel enum to type Int, where the highest priority (severe) has a value of 0, and the lowest priority (low) has a value of 3. We can make direct comparisons between an Int and the priority property. To do so, update the query statement to include documents where the priority is equal to or less than PriorityLevel.High (or 1), as shown here.

We'll also add the reRunOnOpen bool, and set it to true, to force the subscription query to recalculate which documents to sync every time we open the app.

let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
if let foundSubscription = subs.first(named: "user_tasks") {
foundSubscription.updateQuery(toType: Item.self, where: {
$0.owner_id == user.id && $0.priority <= PriorityLevel.high
})
} else {
subs.append(QuerySubscription<Item>(name: "user_tasks") {
$0.owner_id == user.id && $0.priority <= PriorityLevel.high
})
}
}, rerunOnOpen: true)
2

Run the application again. Log in using the account you created earlier in this tutorial. You would expect the list to change, since we added the reRunOnOpen to force the subscription query to recalculate which documents to sync. If you check the console logs, you will see an entry that looks something like this:

2022-08-08 17:54:24.128249-0400 App[17920:237672] Sync: Connection[2]:
Session[2]: Received QUERY_ERROR "Client provided query with bad syntax:
unsupported query for table "Item": key "priority" is not a queryable
field" (error_code=300, query_version=3)

This message tells us that we have added a field to our subscription without configuring Flexible Sync to use that field.

3
  1. Switch back to the Atlas page in your browser. Select the Atlas App Services tab and open the app you are using.

  2. In the left-hand navigation, choose Device Sync, and then click OK in the dialog box about Development Mode being enabled.

  3. Scroll down to the Select Queryable Fields section. In the dropdown labeled Select or create a queryable field, choose "priority". The priority field will be added to the fields shown:

    Priority field is now queryable.
  4. Save your changes.

4

Return to your mobile app. Because we added reRunOnOpen, the app should re-sync only the documents that match the Flexible Sync query. After an initial moment when Realm resyncs the document collection, you will see the new Item of High priority that you created.

If you want to further test the functionality, you can create Items of various priorities. You will see that a new Item with a lower priority briefly appears in the list of Items and then disappears. You can see a message similar to this one in the logs:

2022-08-08 18:00:45.726138-0400 App[18133:242396] Sync: Connection[2]:
Session[2]: Received: ERROR "Client attempted a write that is outside
of permissions or query filters; it has been reverted"
(error_code=231, try_again=true, recovery_disabled=false)

Realm creates the Item locally, syncs it with the backend, and then reverts the write because it doesn't meet the subscription rules.

You'll note, too, that the document you initially created is not synced, because it does not have a priority field. If you want this Item to be synced, you can edit the document in the Atlas UI and add a value for the priority field.

Adding a property to an existing Realm object is a non-breaking change, and Development Mode ensures that the schema change is reflected server-side. If you add or change a subscription to use an additional field, whether newly added or previously existent, you need to modify the Flexible Sync settings to enable querying against that field.

  • Consider adding the new Priority property to the ItemList, ItemRow, and ItemDetail Views.

  • Read our Swift SDK and SwiftUI documentation.

  • Find developer-oriented blog posts and integration tutorials on the MongoDB Developer Hub.

  • Join the MongoDB Community forum to learn from other MongoDB developers and technical experts.

Note

Give Feedback

How did it go? Use the Give Feedback tab at the bottom right of the page to let us know if this tutorial was helpful or if you had any issues.

←  Get StartedAndroid With Kotlin Tutorial →
Give Feedback
© 2022 MongoDB, Inc.

About

  • Careers
  • Investor Relations
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2022 MongoDB, Inc.