Hi,
I have a complex structure of classes, and I'm trying to migrate to swift6
For this classes I've a facade that creates the classes for me without disclosing their internals, only conforming to a known protocol
I think I've hit a hard wall in my knowledge of how the actors can exchange data between themselves. I've created a small piece of code that can trigger the error I've hit
import SwiftUI
import Observation
@globalActor
actor MyActor {
static let shared: some Actor = MyActor()
init() {
}
}
@MyActor
protocol ProtocolMyActor {
var value: String { get }
func set(value: String)
}
@MyActor
func make(value: String) -> ProtocolMyActor {
return ImplementationMyActor(value: value)
}
class ImplementationMyActor: ProtocolMyActor {
private(set) var value: String
init(value: String) {
self.value = value
}
func set(value: String) {
self.value = value
}
}
@MainActor
@Observable
class ViewObserver {
let implementation: ProtocolMyActor
var value: String
init() async {
let implementation = await make(value: "Ciao")
self.implementation = implementation
self.value = await implementation.value
}
func set(value: String) {
Task {
await implementation.set(value: value)
self.value = value
}
}
}
struct MyObservedView: View {
@State var model: ViewObserver?
var body: some View {
if let model {
Button("Loaded \(model.value)") {
model.set(value: ["A", "B", "C"].randomElement()!)
}
} else {
Text("Loading")
.task {
self.model = await ViewObserver()
}
}
}
}
The error
Non-sendable type 'any ProtocolMyActor' passed in implicitly asynchronous call to global actor 'MyActor'-isolated property 'value' cannot cross actor boundary
Occurs in the init on the line "self.value = await implementation.value"
I don't know which concurrency error happens... Yes the init is in the MainActor , but the ProtocolMyActor data can only be accessed in a MyActor queue, so no data races can happen... and each access in my ImplementationMyActor uses await, so I'm not reading or writing the object from a different actor, I just pass sendable values as parameter to a function of the object..
can anybody help me understand better this piece of concurrency problem?
Thanks
Post
Replies
Boosts
Views
Activity
Hi
This issue is present both in previous, current, and beta versions of Xcode
When a project has localization in packages, Xcode FAILS to export localization
The reason is that when trying to export in this configuration, the compiler will try to export using macOS sdk, but if the package support only iOS of course will not find symbols for frameworks and functions that are scoped only for iOS
This behaviour is not changed when the platforms for the package includes ".macOS("99") has I've found as a suggestion online
The only way to perform this operation is to run, from CLI the following command
xcodebuild -exportLocalizations -localizationPath . -sdk iphoneos -project TranslateTest.xcodeproj -exportLanguage en
The problem is that this command will not work when one of the packages contains a macro.
The following error will return in CLI if the macro is used (if the package is only imported no error appears)
error: external macro implementation type 'MyMacroMacros.StringifyMacro' could not be found for macro 'stringify'
I've found online some suggestions from apple folks that says to replace
-sdk iphoneos
with
-destination 'platform=iOS,name=Any iOS Device’
in case your project contains a macro, but this changes nothing in the final result, the export will fail
I'm providing a zip for apple in the radar (cannot upload zip here) that contains 3 example project for the 3 cases:
A project without any packages that can extract its strings with any mode
A project with a package that can only extract its strings only by CLI
A project with multiple packages, one of which has a macro, and it can never extract its string
This problem affects any possibility to have a modularised application with localisations, macros, and packages.
Feedback FB13902424
I've a workspace with multiple packages, and due to the a bug in Xcode I cannot export the app localizations using the Xcode GUI tool, but I need to resort on using a command from terminal
xcodebuild -exportLocalizations -localizationPath . -workspace <path_workspace> -sdk iphoneos -exportLanguage en
One of my packages contains some macros, and I use them from my code without any problem, the code compile
But when I try to export localizations using that command, the build fails due to "compiler plugin not loaded"
So I cannot use Xcode normal exporting because Xcode bug, and cannot export by running a command due to the macro problem
What should I do? It is very discouraging this situation, do you have any suggestion?
I've found a similar problem
When using as a State in view that contains a class object (as an example an Enum with associated values, or directly the class as a nillable state):
When the body will trigger a state change, that should deallocate that object (as an example setting the enum to another value that do not contains the object as the associated value, or setting the state class to nil), the object is not deallocated
If then the state change another time, setting the state to a third different value (ex: setting enum to a third case) the class object will be then deallocated.
In case of not passing through a third state (ex. alternating nil and not nil object) the first object will be deallocated just before the new one is initiated.
This behaviour is not consistent with Swift memory retain system, State apparently is masking a retain inside its implementation, and no documentation appears to show this problem
I may suppose that the problem may due to the "onChange" modifier that will advertise a view of its old and new values, so state may retain the old class until the onChange is fired, but it should be smarter then the current behaviour
Any suggestions for a workaround? radar filed as FB13614543
You can reproduce using the following code
class MyClass {
init() {
print("Allocate")
}
deinit {
print("Deallocate")
}
}
enum LocalState {
case normal
case withClass(MyClass)
case third
}
struct ContentView: View {
@State
var localState: LocalState = .normal
@State
var oldState: LocalState?
var body: some View {
switch localState {
case .normal:
Button("Go To Class") {
self.localState = .withClass(MyClass())
}
case .withClass:
Button("Go To Third") {
self.localState = .third
}
case .third:
Button("Go To Normal") {
self.localState = .normal
}
}
}
}
@main
struct DemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
I've encountered a problem which I don't know if is related to swiftui, the observation framework, or both
If I run the following code I have two tabs, with the second tab that use a "lazy model" which is deallocated each time disappears (this is necessary for my use case)
If I switch to the second tab, all works right. If I return to the first tab, the onDisappear on the foo view should force the "Bar" variable to nil , because the FooView may be still allocated (it is a tab bar) but that resource should be released
If that bar variable is set to nil, the MyBar should be replaced by the ProgressView in the "background"
I expect regarding that the Bar that:
the instance is nil on Foo
no other view should be shown with that instance (MyBar is now disappeared)
Because no ref, the Bar observable object should be now deallocated
In reality the Bar object is still in my memory graph
Any suggestions? is it a bug?
@Observable
class Bar {
var hello: String = ""
}
struct Foo: View {
@State
var bar: Bar?
@ViewBuilder
private var content: some View {
if let bar {
MyBar(bar: bar)
} else {
ProgressView()
}
}
var body: some View {
content
.onAppear {
bar = Bar()
}
.onDisappear {
self.bar = nil
}
}
}
struct MyBar: View {
@Bindable
var bar: Bar
var body: some View {
Text("MyBar")
}
}
struct ContentView: View {
@State
var tag: Int = 0
var body: some View {
TabView(selection: $tag) {
Text("First")
.tag(0)
.tabItem {
Text("First")
}
Foo()
.tag(1)
.tabItem {
Text("Foo")
}
}
}
}
Hi,
When defining a KeyFrameAnimation with spring tracks, the animation do not complete until the very end , but, as default when using withAnimation with a spring animation, it will start the next animation
For example if a complete spring animation is 600ms, the next animation may start only after 300ms
My designer has given me the mass, stiffness, and dumping values for the animation, and expects that the animation will perform for X time, and after that a new animation will start, as it occurs on figma
Using withAnimation with completition callback i can replicate the behaviour, using the ".removed" CompletitionCriteria, but i'd rather use the new KeyFrame phase animator, because i'm not very confident having 5-6 animations started recursively with "withAnimation"