Accessing Actor Properties Synchronously from Task-Less Context

Accessing actor properties synchronously from task-less context

I found a way to do it by using Combine, like this:

import Combine

actor Foo {

nonisolated let valuePublisher = CurrentValueSubject<Int, Never>(0)

var value: Int = 0 {
didSet {
valuePublisher.value = value
}
}
}

By providing an non-isolated publisher, we can propagate the actor value safely to the outside, since Publishers are thread safe.

Callers can either access foo.valuePublisher.value or subscribe to it from the outside, without requiring an async context.

How to access an actor from within a filter closure

Put the actor's contents in a local let constant first, then use that local constant in filter:

Task {
let actorContents = await myActor.contents
let result = list.filter { item in actorContents.contains { $0 == item } }
}

You wouldn't want filter to use the updated myActor.contents halfway through, if myActor.contents changes at some point during the filtering, right? Therefore, make a local copy of it first, and use that.

Also, consider using a Set for better efficiency:

let actorContents = Set(await myActor.contents)
let result = list.filter { item in actorContents.contains(item) }

Swift, actor: Actor-isolated property 'scanning' can not be mutated from a non-isolated context

How can I use an actor to store/read state information correctly so the MyObj can use it to read and set state?

You cannot mutate an actor's instance variables from outside the actor. That is the whole point of actors!

Instead, give the actor a method that sets its own instance variable. You will then be able to call that method (with await).

MainActor vs MainActor(unsafe)

The main purpose of @MainActor(unsafe) is to make incremental adoption of concurrency easier. You can read this in detail explained in the proposal. If you mark your type as @MainActor(unsafe) if you try to access properties and methods synchronously in code that hasn't adopted async/await without breaking anything.

For example following code won't compile with only @MainActor as here actor isolated property accessed from synchronous context:

@MainActor
class SomeViewModel {
let value1 = 0
var value2 = 0
func getValue2() -> Int { value2 }
func setValue2(_ newValue: Int) { value2 = newValue }
}

func doSomething(with viewModel: SomeViewModel) {
_ = viewModel.value1
_ = viewModel.value2 // ERROR: Property 'value2' isolated to global actor 'MainActor' can not be referenced from this synchronous context
_ = viewModel.getValue2() // ERROR: Call to main actor-isolated instance method 'getValue2()' in a synchronous nonisolated context
viewModel.setValue2(3) // ERROR: Call to main actor-isolated instance method 'setValue2' in a synchronous nonisolated context
}

But if you change @MainActor to @MainActor(unsafe) there are no more build errors. Note that @MainActor(unsafe) is only applicable to swift 5 code to make incremental adoption of concurrency easier and in swift 6 it won't have any effect and would behave the same as @MainActor.

Also @MainActor(unsafe) is interchangeable with @preconcurrency @MainActor. You also can use -warn-concurrency compiler option to get all the errors that you would get with only @MainActor as warnings.

How to use URLSession in MainActor

Does it also run on main thread

No. That's the whole point of saying await. At that moment, the system can switch contexts to a background thread without you knowing or caring. Moreover, at await your code pauses without blocking — meaning that while you're waiting for the download, the main thread is free to do other work, the user can interact with your app, etc. Don't worry be happy.



Related Topics



Leave a reply



Submit