swiftui

Returning a String value from a Task sync or async


I am trying to put all my database operations to the background. For on function I need to get a setting back.

if let showMessage = dataManager.getSetting(key: "showWelcomeMessage", modelContext: modelContext) {
    if showMessage == "true" {
        showWelcomeMessage = true
    } else {
        showWelcomeMessage = false
    }
}

The func in DataManager is:

func getSetting(key: String) -> String? {
    let task = Task {
        await DataActor.shared.getSetting(key: key)
    }
    // return await task.value
}

The DataManager is a convenience class for me to access a @ModelActor DataActor which does the lookup.

func getSetting(key: String) -> String? {
    do {
        let predicate = #Predicate<Setting> { object in
            object.key == key
        }
        var descriptor = FetchDescriptor(predicate: predicate)
        descriptor.fetchLimit = 1
        let object = try modelContext.fetch(descriptor)
        if let setting = object.first {
            // print("Found setting \(key) with value \(setting.value)")
            return setting.value
        }
    } catch {
        debugPrint("Failed to get object: \(error)")
    }
    return nil
}

Is there an easy way to retrieve this String in the View as seen in the first code snipped?


Solution

  • Assuming showWelcomeMessage is a @State, and that the first code snippet is in a View, you can put the code in a .task { ... } modifier. Then you can just await the getString call. You should not get the string synchronously.

    SomeViewContent()
        .task {
            if let showMessage = await DataActor.shared.getSetting(key: key) {
                showWelcomeMessage = showMessage == "true"
            }
        }
    

    This will be run when the view first appears. If you also want to run it at a later time, use .task(id: someTrigger) { ... }. The task will be run again when someTrigger changes.