Realm Atlas Swiftui MVVM

I have spent several days chasing my tail trying to set up the most basic SwiftUi app. (I am not a pro coder)

all the examples far excedd what I need or what I am looking to do and thus are far to complicated and confusing.

I am writing a simple “polling” app. All i need is to write the data (vote) and read it back… Basic CRUD stuff.

No log in athorization needed.

What would be helpful for us non-geeks is a simple template app written in SwiftUI MVVM showing how to get authorization, update and download the result. I am just looking to use this as a database.

forget syncing Lists or options Combine. I am sure someone could write such an app in 5 minutes.

Your examples all assume a profession level of skills and knowledge. there is nothing for “Beginners.”

Giving us idiots the most basic app to learn and build from would be truly appreciated.

I use realm in all my phone apps but for this i might had to surrender and use Firebase.

thank you very much.

@t_b sorry to hear, I hope the below code helps.


@main
struct PollingApp: SwiftUI.App {

    @StateObject var app = RealmSwift.App(id: appId)
    // We need to handle some basic state which shows a `ProgressView`
    // if we are not logged in, and the `PollView` if we are.
    var body: some Scene {
        WindowGroup {
            if app.currentUser != nil {
                PollingView()
                    .environment(\.realmConfiguration, configuration)
            } else {
                ProgressView()
                    .onAppear(perform: logIn)
            }
        }
    }

    // This function will be called when the View appears.
    private func logIn() {
        app.login(credentials: .anonymous) { _ in
            print("Logged In")
        }
    }
}

// Typically you would have the Poll options in as a class in a collection, but for simplicity
// we will declare them as individual properties.
class Poll: Object, Identifiable {
    @Persisted(primaryKey: true) var _id: ObjectId
    @Persisted var title: String
    @Persisted var optionATitle: String
    @Persisted var optionAValue: Int
    @Persisted var optionBTitle: String
    @Persisted var optionBValue: Int
    @Persisted var partitionKey: String?
}

// We need to get the configuration for the current user and specify a null
// partition value.
var configuration: Realm.Configuration {
    RealmSwift.App(id: appId).currentUser!.configuration(partitionValue: .null)
}

struct PollingView: View {
    // We will use a configuration that gets all polls will a null partition value.
    @ObservedResults(Poll.self) var polls
    @State var showResults = false

    var body: some View {
        VStack {
            // Naturally this App will only ever work for one Poll in the
            // collection.
            if let poll = polls.first {
                if showResults {
                    Text("Option A: \(poll.optionAValue)")
                    Text("Option B: \(poll.optionBValue)")
                } else {
                    Text(poll.title)
                        .padding()
                    Button(poll.optionATitle, action: { optionTapped(.a) })
                    Button(poll.optionBTitle, action: { optionTapped(.b) })
                    Button("Show Results", action: { optionTapped(.showResults) })
                }
            } else {
                Text("Add a new Poll from Atlas.")
            }
        }
    }

    private enum Option {
        case a
        case b
        case showResults
    }

    private func optionTapped(_ option: Option) {
        // We need to unwrap the first Poll in the results, then get its Realm
        // and thaw it so that we can make updates to the object.
        guard let poll = polls.first?.thaw(), let realm = poll.realm else {
            return
        }

        switch option {
            case .a:
                try! realm.write {
                    poll.optionAValue += 1
                }
                showResults = true
                break
            case .b:
                try! realm.write {
                    poll.optionBValue += 1
                }
                showResults = true
                break
            case .showResults:
                showResults = true
                break
        }
    }
}

It is a SwiftUI app (you do not need MVVM) which has a text label for a question and 3 buttons, “Yes”, “No” and “Show result”. You’ll need some sort of authentication for this to work at all so I used anonymous authentication. Below are some more steps to set up your Realm backend.

1 Like

Enable Anonymous authentication:

1 Like

Prepare to generate a schema after creating a Poll collection in Atlas

You should use this schema:

{
  "title": "Poll",
  "required": [
    "_id",
    "optionATitle",
    "optionAValue",
    "optionBTitle",
    "optionBValue",
    "title"
  ],
  "properties": {
    "_id": {
      "bsonType": "objectId"
    },
    "optionATitle": {
      "bsonType": "string"
    },
    "optionAValue": {
      "bsonType": "long"
    },
    "optionBTitle": {
      "bsonType": "string"
    },
    "optionBValue": {
      "bsonType": "long"
    },
    "title": {
      "bsonType": "string"
    },
    "partitionKey": {
      "bsonType": "string"
    }
  }
}
1 Like

Tthank you for your amazing customer support! i am very impressed and appreciative.
Cheers.

1 Like

Ok, this work in the View great:

@ObservedResults(Vote. self ) var vote

yes i can update and put in a list like you presented.

My “poll” has about a dozen variables and this is about analyzing the data - which is best done in the ViewModel. This app is not just about “counting” votes.

I also need to call back the data in several views while adding functions like sorts.

So given this layout design, how do i BEST call back the data into the ViewModel so i can do all the evaluation and data analysis?

class TestViewModel:ObservableObject {

    let vote = realm.objects(Vote.self)     // something simple like this

}

struct TestView: View {

  @ObservedResults(Vote.self) var vote    // your code that i would like to move to the VM

  @ObservedObject var vmTest = TestViewModel()   // would like to do it this way
 
    var body: some View {

         Text("\(vmTest.vote.count)")

    }

}

thanks so much for your kind help. your assistance is very impressive!

@t_b It’s best to approach this with MVI (Model View Intent) as opposed to MVVM. Apple has some WWDC talks on MVI and us also, see here. In that video we talk about how to use Realm with MVI in SwiftUI with different scenarios. To sum it up, instead of placing logic in a ViewModel, you can extend your Realm model and place the logic there e.g

extension Poll {
    func analyseData() -> String {
        // If the object is managed by using `@ObservedResults` / `@ObservedRealmObject` etc you
        // will be able to get a reference to a Realm.
        if let realm = realm {
            // Do some analysis with other objects here.
           return ...
        }
        return "Could not analyse"
    }
}

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.