This works on macOS:
Window("SingleWindow", id: "main") {
I found your question while searching for how to do it on iPadOS.
Post
Replies
Boosts
Views
Activity
This worked for me, in ContentView, add:
@Environment(\.undoManager) var undoManager
and
.onChange(of: undoManager, initial: true) {
modelContext.undoManager = undoManager
}
@Observable is for your model data. For your view data, it's best to model it in the View struct hierarchy and use .task for your async actions.
You can use @Observable outside of SwiftUI with AsyncStream like this:
struct CountingService {
@Observable
class Model {
public var count: Int = 0
}
let model = Model()
static var shared = CountingService()
init() {
Task { [model] in
let modelDidChange = AsyncStream {
await withCheckedContinuation { continuation in
let _ = withObservationTracking {
model.count
} onChange: {
continuation.resume()
}
}
}
var iterator = modelDidChange.makeAsyncIterator()
repeat {
let x = model.count
// do something
} while await iterator.next() != nil
}
}
}
Or if you want the count in the stream, like this:
let countDidChange = AsyncStream {
await withCheckedContinuation { continuation in
let _ = withObservationTracking {
model.count
} onChange: {
continuation.resume()
}
}
return model.count
}
for await count in countDidChange {
}
Inside of SwiftUI, @Observable is only designed for model data, for view data please use the View struct hierarchy with @State structs to model your view data and .task for async/await. You'll avoid consistency issues that way and can use many of the powerful features like environment and preferences which you would lose if you attempt to use classes for view data instead.
You need to start a task that listens for events simultaneously to when you add a new region to monitor, that's the only way to grab the first event at the right time. This can be done with task groups.
It's simple to provide conformance using ObjectIdentifier, e.g.
import SwiftUI
@Observable class ObservableContent: Hashable {
var text1 = "Default"
var text2 = ""
static func == (lhs: ObservableContent, rhs: ObservableContent) -> Bool {
lhs === rhs
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
struct ContentView: View {
@State var observableContent: ObservableContent?
var body: some View {
Group {
if let observableContent {
NavigationStack {
NavigationLink(value: observableContent) {
Text("Navigation Link")
}
.navigationDestination(for: ObservableContent.self) { content in
ObservableContentView(content: content)
}
}
}
}
.onAppear {
if observableContent == nil {
observableContent = ObservableContent()
}
}
.onDisappear {
observableContent = nil
}
}
}
struct ObservableContentView: View {
@Bindable var content: ObservableContent
var body: some View {
Form {
TextField("Text1", text: $content.text1)
Text(content.text1)
}
}
}
launchOptions is always nil in app delegate if using scene delegates
scene delegate launchOptions does not provide access to the significant location launch reason
here is a workaround: https://stackoverflow.com/a/78228563/259521
os_log(.info, "launched") appeared in the Console app (With Action->Include Info Messages checked) for me when using the simulator and launching the app from the home screen.
These did not appear:
print("launched")
os_log("launched")
os_log(.debug, "launched") // nothing even with Action->Include Info Debug checked!!!
I experienced the same bug and have reported it as FB13696956
You can find my report on openradar (the link is not allowed here for some reason?). Another issue I noticed is the for try await events does not throw a cancellation exception when the outer task is cancelled like normal streams do.
Please submit your own feedback and reference my report so hopefully it will bring more attention to it so it can be fixed!
I'm experiencing the same bug with Xcode Version 15.3 and iPhone Simulator 17.4. When the app is launched monitoring events are received but are not received for any regions added while app already running. I noticed this sample (which is some very ropey SwiftUI by the way) shows the bug too so I will include that in my feedback report as the test harness: https://developer.apple.com/documentation/corelocation/monitoring_location_changes_with_core_location
You need to mark the func as nonisolated if you want a background thread, e.g.
nonisolated func startWriteImagesNext() async -> Bool {
Because you defined it in a ViewController it inherited the @MainActor which doesn't seem what you want to nonisolated bypasses that. Check the NSViewController/UIViewController header to see its @MainActor annotation.
You could also declare the func outside of the ViewController to achieve the same effect, e.g. in a struct that is not marked as @MainActor, or a actor if you want some shared state between all the image tasks.
Can reproduce this with Xcode Version 15.1 beta 3 (15C5059c) deploying to iPhone 14 Pro running iOS 17.1.1.
Make a test project with Apple's LazyVGrid sample code. Launch app in portrait, scroll to bottom, rotate to landscape, rotate to portrait, scroll to top, crash!
let columns = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: columns) {
ForEach(0x1f600...0x1f679, id: \.self) { value in
Text(String(format: "%x", value))
Text(emoji(value))
.font(.largeTitle)
}
}
}
}
private func emoji(_ value: Int) -> String {
guard let scalar = UnicodeScalar(value) else { return "?" }
return String(Character(scalar))
}
id: \.self tut tut Apple sample code developer! (this is not the cause of this crash though).
It seems you have to override the environment locale and switch between one that defaults to am/pm (like GB) and one that is 24hr (like US), e.g.
@State var is12h = false
let date = Date()
...
Text(date, format: .dateTime.hour(.twoDigits(amPM: .abbreviated)).minute(.twoDigits))
.environment(\.locale, Locale(identifier: is12h ? "en_GB" : "en_US"))
...
11:01PM
23:01
∙View can conform to Equatable...
True, .equatable() is no longer required, not sure what version that changed in.
.
∙When view contains @ObservedObject...
False, regardless of the outcome of ==, body is always called when any @Published property of the object changes or objectWillChange is published.
.
∙Does view, that is Equatable, need to care about equality of its subviews..
No, a subview will just follow the same algorithm. Best to only init a View with the data it actually needs and uses in its body to keep invalidation tight.
.
∙When view returns true on equality check...
True, if you returned true from == but had @Environment(\.calendar) var calendar and the calendar changed the body would still be called. This is the same behaviour as @ObservedObject in the 2nd question.
.
∙When the observed object conforms to Equatable...
In my testing the if an ObservedObject conformed to Equatable the == func was never called.
My test code:
import SwiftUI
import Combine
struct ViewTest {
class MyObject: ObservableObject, Equatable {
static func == (lhs: ViewTest.MyObject, rhs: ViewTest.MyObject) -> Bool {
// not called
let x = lhs.counter1 == rhs.counter1 && lhs.counter2 == rhs.counter2
return x
}
@Published var counter1 = 0
@Published var counter2 = 0
func inc() {
counter1 += 1
}
}
struct ContentView: View {
@State var counter = 0
@State var counter2 = 0
@StateObject var object = MyObject()
var body: some View {
Form {
Button("Increment \(counter)") {
counter += 1
}
Button("Increment \(counter2)") {
counter2 += 1
}
// ContentView2(x: counter) { [c = counter] in
// print("\(c)") // body is called once when counter2 is changed the first time (once after every time if it has the onReceive
// }
ContentView2(x: counter2)
.environment(\.calendar, ((counter % 2 == 0) ? Calendar.current : Calendar(identifier: .chinese)))
//{
// print("\(self.counter)") // body called every time counter2 is changed
// }
// .equatable()
}
}
}
struct ContentView2: View, Equatable {
// @Environment(\.managedObjectContext) var context
//@State var counter = 0
//@ObservedObject var object: MyObject
@Environment(\.calendar) var calendar
let x: Int
// let y: () -> () // if the closure passed in does somethiung with self then body is always called.
static func == (lhs: Self, rhs: Self) -> Bool {
let result = lhs.x == rhs.x
//&& lhs.object.counter2 == rhs.object.counter2
return result
}
var body: some View {
let _ = Self._printChanges()
HStack {
Text("ff")
Button("Button") {
//y()
}
}
// .onReceive(Just(0)) { _ in // causes body to be called
// print("")
// }
// .task {
// counter = 0
// }.onChange(of: x) { a in
//
// }
// .onAppear {
// counter = 0
// }
}
}
}
I wouldn't put a core data stack in an actor since it is designed to be used from the main actor. You could perhaps create a background context in an actor though.