How to have scene-specific state in SwiftUI? - swift

I'm creating a macOS application that allows you to have multiple windows open. Each one of those windows (or scenes) need their own state, as the document/text/other states of that window are bound to it.
I also want to get that state in the .commands modifier so I can use the state to add keybindings.
The following I tried does not quite work, as all the state is global across all open windows (i.e. keybindings and text affect all instances of the application, while they must be different):
import SwiftUI
#main
struct QCCApp: App {
#StateObject var compiler = UIStateController()
var body: some Scene {
DocumentGroup(newDocument: QCCDocument()) { file in
ContentView()
// A function I've written that combines the file into state
.environmentObject(compiler.attachDocument(file))
}
.commands {
// Able to get the state to the commands area
CommandDelegate(compiler: compiler)
}
Settings {
SettingsView()
}
}
}
The following also doesn't quite work, but the behaviour of the document state is correct. The keybindings can't really be implemented this way:
import SwiftUI
#main
struct QCCApp: App {
var body: some Scene {
DocumentGroup(newDocument: QCCDocument()) { file in
// I implemented an initialiser instead of the `.attachDocument` method
let compiler = UIStateController(targetting: file)
ContentView()
.environmentObject(compiler)
}
.commands {
// I can't reach the `compiler` variable
CommandDelegate(compiler: /* compiler */)
}
Settings {
SettingsView()
}
}
}
Is there a way to have the keybindings specific to one app instance/scene/window? If so, how does one implement such a thing? In the first example, pressing a keybind would affect both windows (and the same goes for other state).

Related

SetFocusFilterIntent macOS system extension doesn't run perform() function

I am writing a Focus system extension (sandboxed, Cocoa, entitlements set) for macOS 13/Ventura using Xcode 14.2
I have the extension loading it's UI into the macOS system settings > Focus pane.
so here are the issues:
Even though it is loaded, it doesn't seem to ever run the perform() function when the UI is changed by the user or the user invokes Focus > Do Not Disturb.
What can be done in the perform() function? Like, what is supposed to go there? Nothing seems to work.
import AppIntents
struct MacOSFocus: SetFocusFilterIntent {
static var title: LocalizedStringResource {
return "Focus Settings"
}
// The description as it appears in the Settings app
static var description: LocalizedStringResource? = "Focus Settings" // name under Minus icon in options list
// How a configured filter appears on the Focus details screen
var displayRepresentation: DisplayRepresentation {
return DisplayRepresentation(stringLiteral: "Focus Settings") // name under filter once added to Foucs
}
#Parameter(title: "Show Task Bar", default: false)
var showDefaultTaskBar: Bool
#Parameter(title: "Start Timer")
var startTimer: Bool
func perform() async throws -> some IntentResult {
// This doesnt seem to run
// What can I put here?
// I need to write string data to a text file somewhere or communicate with the host app in some way.
return .result()
}
}
Just trying to get unstuck. Thanks for any help.
Tried adding an NSLog() call in the perform() function for debugging. Even tried using NSSound.beep() just to check that it is getting called. Didn't work.

SwiftUI: Error message when using .onMove on List built with forEach

I have an array containing some data that I want to present in a View. It should be possible to rearrange the data by holding and dragging it to its new position. I have used the .onMove modifier on a List generated using ForEach to iterate over every element in the list. My code works exactly as expected, but throws the following error/warning:
2022-10-12 18:48:52.833139+0200 Playground[1212:207280]
[EventDispatcher] Found no UIEvent for backing event of type: 11;
contextId: 0xFEEA6094
This only happens when using an actual device (iPhone 12 Pro Max on iOS 16.0.2), not when using the simulator (Xcode Version 14.0.1 (14A400), build in simulator of any device that runs iOS 16).
The code (the whole project is way bigger, but this is enough to trigger the error/warning):
struct ContentView: View {
#State var stuffList = ["Beer", "Chips", "Boardgames"]
var body: some View {
NavigationStack {
List {
ForEach(stuffList, id: \.self) { stuff in
Text(stuff)
}
.onMove { stuffList.move(fromOffsets: $0, toOffset: $1) }
}
}
}
}
Edit:
I have now tried different code snippets from online tutorials, e.g. this one (Hacking in Swift) and I still get this message no matter what I try. As my code seems to work as intended, I will continue my project and treat this as "log noise". If someone knows more about this than I do or simply can confirm having the same issue, it would be highly appreciated.

Redux conform Bindable for SwiftUI 2.0

I've been implementing Redux in my SwiftUI Project successfully but struggle when it comes to handling bindings properly. I want to use the binding functionality of SwiftUI while also storing the information in the Redux State.
As you see this kind of contradicts itself since the state can't be bound two-way.
This is my current code
#State var tab: Tab = .tab1
TabView(selection: $tab) { ... }
Ideally it should be comfortably usable like this if it's possible. I am also open to other ideas, that's just what i initially came up with - it's far from perfect.
#State var store = ReduxStore(...)
TabView(selection: $store.state.tab) { ... }
I figured out how to handle such cases. The solution for me is a Binding like:
let tabBinding = Binding<Tab> (
get: { self.store.state.currentTab }, // return the value from the state
set: { self.store.dispatch(action: NavigationAction.updateTab(tab: $0)) } // send the action here
)
Although this probably could be beautified it works, is pretty straight forward and solves the problem of working with Bindings in Redux.

SwiftUI view sometimes creates Thread 1: EXC_BAD_ACCESS (code=1, address=0x6002005b8368), when I put array as a parameter

SwiftUI View file
struct NoteListView: View {
#EnvironmentObject var presenter: NoteListPresenter
var body: some View {
NavigationView{
AddNoteButton()
Text(presenter.noteViewModels[0].title)
}
}
}
struct NoteListView_Previews: PreviewProvider {
static var previews: some View {
NoteListView()
}
}
I first got this error when I finished writing the logic and started to writing Views. After add my custom button AddNoteButton() to main view, it gave the following error, although it worked before I added this button. I tried to rewrite my views, but although it works at the beginning at some random point of time it throws this error again(by other words sometimes it works, but when i add some irrelevant code it stops working and discarding changes doesn't solve the problem). It always highlights line, where I use noteViewModels array inside of the NoteListView.
Also I added method which prints array to presenter and it always prints everything.
func printAll(){
for element in self.noteViewModels{
print(element.title)
}
}
I can not add other files to question, because there is too many of them. But i hope that I gave enough information, If you think that there is not enough information you are free to ask me or you can check out github:
https://github.com/ShynggysSaparbek/ViperNotesWithRealm/
Xcode version 11.3
Mac OS version Catalina 10.15.2

Ambiguous reference with a Picker in Swift UI

I'm trying to make a Picker with SwiftUI. I've follow a tutorial but don't have the same result. There is Ambiguous reference on the self.category.count and self.category[$0]. After one entire day, I still don't know how to fix it ...
import SwiftUI
struct Picker : View {
var category = ["Aucun", "BF Glaive", "Baguette", "Negatron", "Larme", "Ceinture", "Arc", "Cotte", "Spatule"]
#State private var selectedCategory = 0
var body: some View {
VStack {
Picker(selection: $selectedCategory, label: Text("Item")) {
ForEach(0 ..< self.category.count) {
Text(self.category[$0])
.tag($0)
}
}
Text("Selected : \(category[selectedCategory])")
}
}
}
To resolve name conflicts between modules, you can either:
Rename your Picker to something else.
Use the qualified (full) name:
SwiftUI.Picker(selection: $selectedCategory, label: Text("Item")) {
The error message Ambiguous reference to member 'count’ is misleading. What you have is a naming conflict between SwiftUI.Picker and your Picker struct. Just change the name of your struct to something other than Picker. For example:
struct CategoryPicker : View {
// ...
}
Alternatively, you can resolve the naming conflict between the modules by providing the fully qualified name for SwiftUI.Picker (as Sulthan pointed out):
SwiftUI.Picker(selection: $selectedCategory, label: Text("Item")) {
// ...
}
However, I wouldn’t advise this option unless your intention is to replace SwiftUI.Picker everywhere in your app. Your code includes the category array and a Text view, so it's unlikely this is what you're after.
If the app eventually needs OtherPicker with a SwiftUI.Picker and the module name is omitted again, it’ll be even more confusing to track down the error—and you’ve already spent an “entire day” on it. So, best to avoid this possibility by not introducing the conflict at all :)