How does one add Codable conformance to a class that needs to be isolated to the MainActor?
For example, the following code gives compiler errors:
@MainActor final class MyClass: Codable {
var value: Int
enum CodingKeys: String, CodingKey {
case value
}
init(from decoder: Decoder) throws { // <-- Compiler error: Initializer 'init(from:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Decodable'
let data = try decoder.container(keyedBy: CodingKeys.self)
self.value = try data.decode(Int.self, forKey: .value)
}
func encode(to encoder: Encoder) throws { // <-- Compiler error: Instance method 'encode(to:)' isolated to global actor 'MainActor' can not satisfy corresponding requirement from protocol 'Encodable'
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
}
}
I'm definitely struggling to get my head around actors and @MainActor at the moment!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm trying to understand a design pattern for accessing the isolated state held in an actor type from within a SwiftUI view.
Take this naive code:
actor Model: ObservableObject {
@Published var num: Int = 0
func updateNumber(_ newNum: Int) {
self.num = newNum
}
}
struct ContentView: View {
@StateObject var model = Model()
var body: some View {
Text("\(model.num)") // <-- Compiler error: Actor-isolated property 'num' can not be referenced from the main actor
Button("Update number") {
Task.detached() {
await model.updateNumber(1)
}
}
}
}
Understandably I get the compiler error Actor-isolated property 'num' can not be referenced from the main actor when I try and access the isolated value. Yet I can't understand how to display this data in a view. I wonder if I need a ViewModel that observes the actor, and updates itself on the main thread, but get compile time error Actor-isolated property '$num' can not be referenced from a non-isolated context.
class ViewModel: ObservableObject {
let model: Model
@Published var num: Int
let cancellable: AnyCancellable
init() {
let model = Model()
self.model = model
self.num = 0
self.cancellable = model.$num // <-- compile time error `Actor-isolated property '$num' can not be referenced from a non-isolated context`
.receive(on: DispatchQueue.main)
.sink { self.num = $0 }
}
}
Secondly, imagine if this code did compile, then I would get another error when clicking the button that the interface is not being updated on the main thread...again I'm not sure how to effect this from within the actor?
I wanted to know if I am doing something fundamentally wrong with Xcode 13 beta 3, with respect to localisation.
I'm using the new compiler support to extract strings for localisation then exporting -> translating -> importing via the .xcloc files. All this works nicely, aside from one problem...
I'm having difficulty getting it to create the initial Localizable.strings file in the development language, i.e. en in my case. When I try and import the relevant en.xcloc file nothing happens. I can import other languages without problem.
Here are the steps I am taking:
Write some basic code with one piece of text that can be localised
Select Product -> Export Localisations... and choose English - Development Language
Correctly creates en.xcloc file - contents as expected, picking up the text to be localised.
Select Product -> Import Localisations... and re-import the english en.xcloc file
This does nothing. So I try the following:
Go to Project's file -> Info tab and add a new language, say (Fr)
Select Product -> Export Localisations... and choose English - Development Language and French options
Open fr.xcloc file and add translations required
Come back to Xcode and import fr.xcloc file
Now the Localizable.strings file appears - it is of course the French version
Importing the en.xcloc file still does nothing
If I open up the file inspector on the right, I can click to add an English version, but it simply copies the French version (which causes lots of other issues)...
Any ideas?
The following code is stripped out from an app that compiles and works under MacOS 11. It shows a tab bar - clicking on an item will highlight it. Under the tab bar is a text edit field where one can edit the title of the selected tab.
There are 2 issues:
The tab title shown in the title editor is allows out of phase
from the selected tab
Irrespective of which tab item is selected, when the title is edited
it always updates the first tab
I'm not sure if I was very lucky that this code ever worked under Big Sur, or if there is an issue with Monterey. I'm definitely not holding this up as example code - I'm sure there are better ways to implement it, but seek opinions on whether it should work.
import SwiftUI
class Document: ObservableObject {
var tabs = [Tab(id: 0), Tab(id: 1)]
var viewSettings = ViewSettings()
}
class Tab: ObservableObject, Identifiable {
let id: Int
@Published var title: String
init(id: Int) {
self.id = id
self.title = "Tab \(id)"
}
}
class ViewSettings: ObservableObject {
@Published var activeTab: Int = 0
}
@main
struct Test: App {
@StateObject var document: Document = Document()
var body: some Scene {
WindowGroup {
ContentView()
.padding()
.environmentObject(document)
.environmentObject(document.viewSettings)
}
}
}
struct ContentView: View {
@EnvironmentObject var document: Document
@EnvironmentObject var viewSettings: ViewSettings
var body: some View {
TabBar(viewSettings: document.viewSettings)
TabEditView(activeTab: document.tabs[viewSettings.activeTab])
}
}
struct TabEditView: View {
@EnvironmentObject var viewSettings: ViewSettings
@ObservedObject var activeTab: Tab
@State var title: String = ""
init(activeTab: Tab) {
print("CONSOLE - Init TabEditView for tab \(activeTab.id)")
self.activeTab = activeTab
}
var body: some View {
HStack {
Text("Tab title:")
TextField("Tab title:", text: $title, onCommit: { activeTab.title = title })
.onAppear { title = activeTab.title }
.onChange(of: viewSettings.activeTab) { _ in
print("CONSOLE - Updating TextField from tab \(activeTab.id)")
title = activeTab.title
}
}
}
}
struct TabBar: View {
@EnvironmentObject var document: Document
@ObservedObject var viewSettings: ViewSettings
var body: some View {
HStack {
ForEach(document.tabs, content: TabItem.init)
}
}
}
struct TabItem: View {
@EnvironmentObject var viewSettings: ViewSettings
@ObservedObject var tab: Tab
init(_ tab : Tab) { self.tab = tab }
var body: some View {
Text(tab.title)
.padding(2)
.background(tab.id == viewSettings.activeTab ? .red : .clear)
.cornerRadius(4)
.onTapGesture {
viewSettings.activeTab = tab.id
}
}
}
Is there anyway to suppress the very verbose console output I am getting with MacOS Monterey and SwiftUI code? Here's a snippet of the output:
2021-07-20 09:58:18.267602+0100 BBCFTEST[19341:1880374] IMKInputSession (deactivate) CFRunLoopObserver kCFRunLoopExit (State change BEGIN) - innerRunLoopCount=1, otherInnerRunLoopDetected=0
2021-07-20 09:58:18.267637+0100 BBCFTEST[19341:1880374] IMKInputSession (deactivate) CFRunLoopObserver kCFRunLoopExit (State change END) - innerRunLoopCount=0, otherInnerRunLoopDetected=0
2021-07-20 09:58:18.270063+0100 BBCFTEST[19341:1880374] IMKInputSession (deactivate) CFRunLoopRunInMode exited, (kCFRunLoopRunStopped)
2021-07-20 09:58:18.270109+0100 BBCFTEST[19341:1880374] IMKInputSession (deactivate) CFRunLoopRunInMode (kIMKXPCPrivateRunLoopMode) ] RunLoopFinished(1)/Stopped(2) - Run result = 2, (Invocation already done = 1) (Sentinel IsZombie = 0)
2021-07-20 09:58:18.270147+0100 BBCFTEST[19341:1880374] IMKInputSession (deactivate) CFRunLoopRunInMode() LOOP DONE!
How do you add a button in the toolbar to hide/show the sidebar?
Context
I'm writing an app that allows a user to select folders, and then scan them for files. The folder could be on the user's machine, iCloud or external storage. The app persists the details of these files and folders to a document, so the user can access the files or re-scan the folders in the future. The App is being written using the SwiftUI framework for MacOS and the iPad.
Problems
Given this is a Sandboxed app I need to create security-scoped bookmarks to be able to access the files and folders that have been persisted to the document. I have two questions:
How can I create a document-scoped bookmark, when using ReferenceFileDocument protocol. I need the document's URL, but I will never have access to this as I'm using the ReferenceFileDocument. I want to achieve something like this:
func fileWrapper(snapshot: MyDocument, configuration: WriteConfiguration) throws - FileWrapper {
:
let bookmarkDataToPersist = snapshort.sourceFolderURL.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: documentURL)
:
}
2. Ideally the user would be able to:
* connect an external drive to their Mac
* select a folder on that drive
* save the document, which would persist a bookmark to that folder's URL
* send the document to an iPad (via email or iCloud drive)
* open the document using the iPad version of the App
* connect the external bookmark to the iPad
* re-scan the folder which was book marked in the document from the folder
But given the problem in 1) and the fact that document-bookmarks cannot point to folders, is there a way?
Any ideas or suggestions would be very welcome
I'm unable to compile my project for UnitTests at present - get a load of Cannot Find XXXXX in Scope errors.
Anybody else getting this, or ideas of how to resolve.
I've tried cleaning the build folder, restarting Xcode, checking my unit test code file is only a member of the test target.
Code compiles for the debug and production builds.