Customize handling of asynchronous events by combining event-processing operators using Combine.

Combine Documentation

Posts under Combine tag

26 Posts
Sort by:
Post not yet marked as solved
1 Replies
1.1k Views
This code crashes ("Unexpectedly found nil while unwrapping an Optional value") import Combine class Receiver { &#9;&#9;var value: Int! &#9;&#9;var cancellables = Set<AnyCancellable>([]) &#9;&#9;init(_ p: AnyPublisher<Int,Never>) { &#9;&#9;&#9;&#9;p.assign(to: \.value, on: self).store(in: &cancellables) &#9;&#9;} } let receiver = Receiver(Just(5).eraseToAnyPublisher()) It does not crash if I use p.sink { self.value = $0 }.store(in: &cancellables) instead of the assign, and it does not crash if I do not use an optional for the value-property. To me this looks like a bug in Swift's constructor code, but maybe I am overlooking something?
Posted
by
Post not yet marked as solved
3 Replies
3.9k Views
I am working on a library, a Swift package. We have quite a few properties on various classes that can change and we think the @Published property wrapper is a good way to annotate these properties as it offers a built-in way to work with SwiftUI and also Combine. Many of our properties can change on background threads and we've noticed that we get a purple runtime issue when setting the value from a background thread. This is a bit problematic for us because the state did change on a background thread and we need to update it at that time. If we dispatch it to the main queue and update it on the next iteration, then our property state doesn't match what the user expects. Say they "load" or "start" something asynchronously, and that finishes, the status should report "loaded" or "started", but that's not the case if we dispatch it to the main queue because that property doesn't update until the next iteration of the run loop. There also isn't any information in the documentation for @Published that suggests that you must update it on the main thread. I understand why SwiftUI wants it on the main thread, but this property wrapper is in the Combine framework. Also it seems like SwiftUI internally could ask to receive the published updates on the main queue and @Published shouldn't enforce a specific thread. One thing we are thinking about doing is writing our own property wrapper, but that doesn't seem to be ideal for SwiftUI integration and it's one more property wrapper that users of our package would need to be educated about. Any thoughts on direction? Is there anyway to break @Published from the main thread?
Posted
by
Post not yet marked as solved
1 Replies
1.6k Views
Hello For apps built with xcode 13 but ran on ios 16 beta devices, im seeing a potential leak in Combine that i suspect may be causing a breaking bug(the view fails to load anything on subsequent visits after being dismissed, which im suspecting is due to the leaked observable object) . The issue is resolved when built with Xcode 14 Beta, no bug and no leak I am triggering a flow multiple times and seeing the instances of an @ObservableObject used as a @StateObject in a view keep going up in the memory debugger, without the corresponding view leaking to accompany it like ive seen before. so the observable object is being leaked I see the following in the memory graph debugger. It indicates a BoxVTable relation to the observable datastore, which i presume is an internal type i dont have access to, when the view has disappeared Any Ideas? ive checked all the typical closure leak possibilities ive fixed before and this seems to be different
Posted
by
Post not yet marked as solved
2 Replies
1.9k Views
Hi, I'm trying to use async/await for KVO and it seems something is broken. For some reason, it doesn't go inside for in body when I'm changing the observed property. import Foundation import PlaygroundSupport class TestObj: NSObject {   @objc dynamic var count = 0 } let obj = TestObj() Task {   for await value in obj.publisher(for: \.count).values {     print(value)   } } Task.detached {   try? await Task.sleep(for: .microseconds(100))   obj.count += 1 } Task.detached {   try? await Task.sleep(for: .microseconds(200))   obj.count += 1 } PlaygroundPage.current.needsIndefiniteExecution = true Expected result: 0, 1, 2 Actual result: 0 Does anyone know what is wrong here?
Posted
by
Post not yet marked as solved
1 Replies
1.1k Views
I'm trying to get push tokens for Live Activities but I am getting the same token twice, Why is that? Is there any other way to get the token just once, so I can hit the API to save the token. Here's the code: Task { for await data in runningActivity.pushTokenUpdates { let myToken = data.hexString self.count += 1 print("Count \(self.count)\n" + myToken) } Output: Count 1 80dc21086f81.........646d7084805dc Count 2 80dc21086f81.........646d7084805dc I can't seem to understand why this is happening, and some times it takes significantly longer to get the tokens. Am I doing anything wrong? Please do share your thoughts. Thank you!
Posted
by
Post not yet marked as solved
1 Replies
634 Views
I'm converting some Combine publishers to async using .values and trying to understand what happens when: A Publisher completes before emitting a value. Here is some example code: let response = client.fetch(query: query) .tryMap { /* map the data, or throw a mapping error if it doesn't map. */ } return Model() } .eraseToAnyPublisher() .values for try await result in response { return result } throw AsyncCombineError.finishedWithoutValue // my custom error What happens with .values if the publisher completes without emitting any values? It returns a type of AsyncThrowingSequence<Self> Will it throw? return? hang? or is this state impossible to happen? Thanks and please enlighten me if I am misunderstanding structured concurrency. This is new to me!
Posted
by
Post not yet marked as solved
1 Replies
702 Views
Hello, I've completed the Landmarks App Apple Developer tutorial, since finishing it I decided to create a project using the .JSON code from the project. I've wrote the code in an identical layout as the tutorial, however I am receiving the error - Couldn't parse lesssonData.json as array - when trying to load a preview and the app builds successfully but crashes when I launch it. Below is the code, any help is appreciated! LessonList.swift import SwiftUI struct LessonList: View { var lesson: Lesson var body: some View { VStack { List { Section { ForEach(lessons, id: \.self) { lesson in Label(lesson.name, systemImage: "house") } } Section { Label("Hello World!", systemImage: "globe") } } } } } struct LessonList_Previews: PreviewProvider { static var lessons = ModelData().lessons static var previews: some View { LessonList(lesson: lessons[0]) } } ModelData.swift import Foundation import Combine final class ModelData: ObservableObject { @Published var lessons: [Lesson] = load("lessonData.json") } var lessons: [Lesson] = load("lessonData.json") func load<T: Decodable>(_ filename: String) -> T { let data: Data guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else { fatalError("Couldn't find \(filename) in main bundle.") } do { data = try Data(contentsOf: file) } catch { fatalError("Couldn't load \(filename) from main bundle:\n\(error)") } do { let decoder = JSONDecoder() return try decoder.decode(T.self, from: data) } catch { fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") // Error on this Line } } lessonData.JSON [ { "id":"1001", "lessonNo":"Lesson 1", "name":"Introduction to Photography", "category":"Introduction", }, { "id":"1002", "lessonNo":"Lesson 2", "name":"Negative and Positive Space", "category":"Introduction", }, { "id":"1003", "lessonNo":"Lesson 3", "name":"Introduction to Camera Angles", "category":"Introduction", }, { "id":"1004", "lessonNo":"Lesson 4", "name":"Lighting and Framing", "category":"Beginners", }, { "id":"1005", "lessonNo":"Lesson 5", "name":"Still Photography", "category":"Beginners", }, { "id":"1006", "lessonNo":"Lesson 6", "name":"Motion Photograhy", "category":"Beginners", }, { "id":"1007", "lessonNo":"Lesson 7", "name":"Aperture and F-Stops", "category":"Intermiediate", }, { "id":"1008", "lessonNo":"Lesson 8", "name":"Shutter Speeds", "category":"Intermiediate", }, { "id":"1009", "lessonNo":"Lesson 9", "name":"Advanced Framing", "category":"Advanced", }, { "id":"1010", "lessonNo":"Lesson 10", "name":"Advanced Aperture, F-Stops and Shutter Speeds", "category":"Advanced", }, ]
Posted
by
E-K
Post not yet marked as solved
0 Replies
688 Views
I've been struggling with writing some dependency injection types for Foundation types like NotificationCenter, UIDevice, etc. I've created a sample package to demonstrate: https://github.com/MFB-Technologies-Inc/notification-center-client-demo. The goal is to mimic NotificationCenter's Publisher and notifications AsyncSequence. LiveNotificationCenterClientTests.testStream will fail unless it's run in isolation. If more than one test is run, it will never complete because no values are ever received from the stream. MockNotificationCenterClientTests.testPublisher fails because the expectations are not fulfilled before the timeout. For whatever reason, the published values are not being received in the sink. MockNotificationCenterClientTests.testStream never completes because the value is being endlessly awaited. It seems that there are some fundamental things about concurrency that I'm getting wrong. Can anybody help me debug this? For the two stream tests, I know I could rewrite them to fail with a timeout like the publisher tests but that's not the real problem.
Posted
by
Post not yet marked as solved
0 Replies
509 Views
What is the expected thread on which a .collect operation in Swift's Combine will emit? Specifically I am seeing this code crash in the second precondition, but not the first: return Publishers.MergeMany(publishers) .handleEvents(receiveOutput: { _ in precondition(Thread.isMainThread) // <- This is fine }) .collect() .handleEvents(receiveOutput: { _ in precondition(Thread.isMainThread) // <- Crash is here }) It is as if the .collect operation is picking a different thread to use, even though the final publisher in the MergeMany must have emitted on the main thread. I have deduced this behavior via crash logs uploaded from Firebase. Can anyone explain this observed behavior?
Posted
by
Post not yet marked as solved
0 Replies
659 Views
Can someone please help me understand PassthroughSubject and CurrentValueSubject? What I understand so far is that they are subjects where subscribers can listen to changes made to these subjects, but I'm really straggling to understand the following. I'm I correct by saying that PassthroughSubject or CurrentValueSubject could replace delegation and asynchronous function calls? Is it possible to delare a subject in Class A and subscribe to listen to those subject changes in Class B and in some other classes or are listeners meant to only be used direclty in SwiftUI structs? Thanks
Posted
by
Post not yet marked as solved
1 Replies
893 Views
I'm working on an iOS project and I'm wondering if it's possible to mix Combine and the new async/await feature in Swift. I want to leverage the power of Combine's reactive programming paradigm along with the simplified asynchronous code flow provided by async/await. Has anyone tried mixing these two approaches in the same project? Are there any known issues or considerations to keep in mind? Are there any best practices or patterns to follow when combining Combine and async/await? I would appreciate any insights or experiences shared. Thank you in advance!
Posted
by
Post not yet marked as solved
0 Replies
1.2k Views
We currently have our entire app written as SwiftUI Views with ViewModels (currently set as @StateObjects). SwiftUI has a new feature in iOS 17 called @Observable which simplifies the MVVM pattern and would greatly reduce the complexity of our codebase. However, our current ViewModels implement Combine pipelines on the @Published properties which allows us to do all sorts of things from validation of inputs to ensuring lists are filtered correctly. Without the @Published property wrapper in the new @Observable macro, we don't have access to those combine pipelines and so we were wondering how others have solved this? One idea we are floating around is using CurrentValueSubjects as the variable types, but that does pollute the code a little as we have to utilise .send() and .value in the Views which seems like an anti-pattern. Any thoughts or help would be greatly appreciated!
Posted
by
Post not yet marked as solved
0 Replies
618 Views
We are experiencing crash on Combine on iOS 16 Exception Type: EXC_BREAKPOINT (SIGTRAP) Exception Codes: 0x0000000000000001, 0x000000021dc6408c Termination Reason: SIGNAL 5 Trace/BPT trap: 5 Terminating Process: exc handler [11489] Triggered by Thread: 0 Thread 0 name: Thread 0 Crashed: 0 libsystem_platform.dylib 0x000000021dc6408c _os_unfair_lock_recursive_abort + 36 (lock.c:508) 1 libsystem_platform.dylib 0x000000021dc5e898 _os_unfair_lock_lock_slow + 280 (lock.c:567) 2 Combine 0x00000001d8298174 Publishers.ReceiveOn.Inner.cancel() + 28 (Locking.swift:35) 3 Combine 0x00000001d8297c98 protocol witness for Cancellable.cancel() in conformance Publishers.ReceiveOn<A, B>.Inner<A1> + 24 (<compiler-generated>:0) 4 Combine 0x00000001d828ffc0 Subscribers.Sink.cancel() + 628 (Sink.swift:161) 5 Combine 0x00000001d82ca8ec protocol witness for Cancellable.cancel() in conformance Subscribers.Sink<A, B> + 24 (<compiler-generated>:0) 6 Combine 0x00000001d82945fc AnyCancellable.cancel() + 212 (Subscription.swift:107) 7 Combine 0x00000001d828e250 AnyCancellable.__deallocating_deinit + 16 (Subscription.swift:92) 8 libswiftCore.dylib 0x00000001c9c24134 _swift_release_dealloc + 56 (HeapObject.cpp:706) 9 libswiftCore.dylib 0x00000001c9c15294 swift_arrayDestroy + 124 (Array.cpp:208) 10 libswiftCore.dylib 0x00000001c9a38140 _SetStorage.deinit + 328 (UnsafePointer.swift:954) 11 libswiftCore.dylib 0x00000001c9a38164 _SetStorage.__deallocating_deinit + 16 (SetStorage.swift:0) 12 libswiftCore.dylib 0x00000001c9c24134 _swift_release_dealloc + 56 (HeapObject.cpp:706) 13 AppNameCore 0x00000001033ae3ec MessagesThreadPresenterImpl.deinit + 396 (<compiler-generated>:0) 14 AppNameCore 0x00000001033ae43c MessagesThreadPresenterImpl.__deallocating_deinit + 12 (MessagesThreadPresenter.swift:0) 15 libswiftCore.dylib 0x00000001c9c24134 _swift_release_dealloc + 56 (HeapObject.cpp:706) 16 libswiftCore.dylib 0x00000001c9c25120 bool swift::HeapObjectSideTableEntry::decrementStrong<(swift::PerformDeinit)1>(unsigned int) + 292 (RefCount.h:1032) 17 AppNameCore 0x00000001033b7c44 0x102f64000 + 4537412 18 libswiftCore.dylib 0x00000001c9c24134 _swift_release_dealloc + 56 (HeapObject.cpp:706) 19 Combine 0x00000001d82c0b48 Publishers.FlatMap.Outer.deinit + 232 (<compiler-generated>:0) 20 Combine 0x00000001d82b8244 Publishers.FlatMap.Outer.__deallocating_deinit + 16 (FlatMap.swift:0) 21 libswiftCore.dylib 0x00000001c9c24134 _swift_release_dealloc + 56 (HeapObject.cpp:706) 22 Combine 0x00000001d829366c assignWithTake for SubscriptionStatus + 68 (<compiler-generated>:0) 23 Combine 0x00000001d82906ec outlined assign with take of SubscriptionStatus + 76 (<compiler-generated>:0) 24 Combine 0x00000001d82dd138 closure #1 in Publishers.ReceiveOn.Inner.receive(completion:) + 144 (ReceiveOn.swift:201) 25 Foundation 0x00000001c9fb312c $sIegh_IeyBh_TR + 36 (<compiler-generated>:0) 26 CoreFoundation 0x00000001cfab6514 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28 (CFRunLoop.c:1805) 27 CoreFoundation 0x00000001cfb1ed6c __CFRunLoopDoBlocks + 368 (CFRunLoop.c:1847) 28 CoreFoundation 0x00000001cfaef1cc __CFRunLoopRun + 2452 (CFRunLoop.c:3201) 29 CoreFoundation 0x00000001cfaf3eb0 CFRunLoopRunSpecific + 612 (CFRunLoop.c:3418) 30 GraphicsServices 0x0000000209ce9368 GSEventRunModal + 164 (GSEvent.c:2196) 31 UIKitCore 0x00000001d1fe9668 -[UIApplication _run] + 888 (UIApplication.m:3758) 32 UIKitCore 0x00000001d1fe92cc UIApplicationMain + 340 (UIApplication.m:5348) 33 AppName 0x000000010009190c main + 180 (main.swift:11) 34 dyld 0x00000001ee3ec960 start + 2528 (dyldMain.cpp:1170)
Posted
by
Post not yet marked as solved
0 Replies
449 Views
I'm learning Combine. I'm trying to understand some behavior when there is concurrent queue applied to receiveOn as a scheduler. I extracted code to present the situation that I'm trying to understand. let queue = DispatchQueue.global() // Or OperationQueue() let subscription = (1...10).publisher .receive(on: queue) .sink { value in print("Received \(value)") } I'm receiving in debug console such output: Received 1 Received 2 Received 3 Received 7 Received 6 But I was expecting 10 lines in the output. Everytime I run it I'm receiving different results but not 10 lines - 10 values. What is happening here? Does anybody know? Where is 4, 5, 8, 9 and 10? Why completion is arriving faster before all values? Is this a combine bug? I was looking in 2 books related to Combine and in apple documentation. No warnings about using concurrent queues as a schedulers in Combine.
Posted
by
Post not yet marked as solved
1 Replies
712 Views
I've made a small reproducing example app to demonstrate this issue. Just create an iOS App project and replace the App and ContentView files with the following code. The app works as expected when running it in a simulator or on a device but the SwiftUI Preview will get stuck showing only the loading state, even though the print statement in the View's body has printed viewModel.state: showingContent(SwiftUITest.ViewModel.Content(text: "Hello world!", reloadButtonTitle: "Reload"))to the console. When stuck showing the loading state (an animated ProgressView), If I change .padding(.all, 12) to e.g. .padding(.all, 2) or vice versa, then that will make the Preview render the expected content. Also, if I tap the Reload-button, it will not show the ProgressView for 2 seconds as expected (and as the app will do when run in a simulator or on a device), instead it will just show a white screen, and the same workaround (changing the padding amount) can be used to make the it render the expected content. Can other people reproduce this behavior, is it a known bug, or am I doing something wrong? TestApp.swift import SwiftUI @main struct SwiftUITestApp: App { var body: some Scene { WindowGroup { ContentView(viewModel: ViewModel( contentLoader: { try! await Task.sleep(nanoseconds: 2_000_000_000) return .init(text: "Hello world!", reloadButtonTitle: "Reload") } )) } } } ContentView.swift import SwiftUI struct ContentView: View { @StateObject var viewModel: ViewModel var body: some View { let _ = print("viewModel.state:", viewModel.state) Group { switch viewModel.state { case .notStarted, .loading: ProgressView() case .showingContent(let content): VStack { Text(content.text) .padding(.all, 12) Button(content.reloadButtonTitle) { viewModel.handle(event: .reloadButtonWasTapped) } } } } .onAppear { viewModel.handle(event: .viewDidAppear) } } } // MARK: - ViewModel @MainActor final class ViewModel: ObservableObject { @Published var state: State = .notStarted let contentLoader: () async -> Content init(contentLoader: @escaping () async -> Content) { self.contentLoader = contentLoader } func handle(event: Event) { switch state { case .notStarted: if event == .viewDidAppear { loadContent() } case .loading: break case .showingContent: if event == .reloadButtonWasTapped { loadContent() } } } func loadContent() { guard state != .loading else { return } state = .loading Task { print("starts loading", Date.now) let content = await contentLoader() print("finished loading", Date.now) state = .showingContent(content) } } enum State: Equatable { case notStarted case loading case showingContent(Content) } struct Content: Equatable { let text: String let reloadButtonTitle: String } enum Event: Equatable { case viewDidAppear case reloadButtonWasTapped } } // MARK: - Preview struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView(viewModel: ViewModel( contentLoader: { try! await Task.sleep(nanoseconds: 2_000_000_000) return .init(text: "Hello world!", reloadButtonTitle: "Reload") } )) } } Here's the simple behavior of the app (recorded from simulator): Each time the view appears, it loads it's content for two seconds while showing a ProgressView, then it shows the content, which includes a Reload button, and if you tap that button it will reload the content for 2 seconds again. I would expect the Preview to behave in the same way.
Posted
by
Post not yet marked as solved
1 Replies
1.1k Views
That may not be the best way to explain it. Essentially, I'm requesting data from Reddit and storing it in an object called Request. This object has a timestamp and an array of objects called Post. Everything was working fine until I started to add some code to filter the post that were being fetched from reddit. extension [Request] { func insert(request: Request, in context: ModelContext) { if self.count == 0 { context.logMessage("There are no existing request") context.insert(request) context.logMessage("Request saved") }else { print(request) // No crash print(request.timestamp) // No crash print(request.posts) // Causes a crash } } } When it does crash, it points to this code inside the SwiftData model. This code seems to be something within SwiftData. I didn't write any of this. { @storageRestrictions(accesses: _$backingData, initializes: _posts) init(initialValue) { _$backingData.setValue(forKey: \.posts, to: initialValue) _posts = _SwiftDataNoType() } get { _$observationRegistrar.access(self, keyPath: \.posts) return self.getValue(forKey: \.posts) } set { _$observationRegistrar.withMutation(of: self, keyPath: \.posts) { self.setValue(forKey: \.posts, to: newValue) } } } It has the error at the return self.getValue() line: Thread 5: EXC_BREAKPOINT (code=1, subcode=0x2406965c4) This is the sequence that occurs: View is loaded Checks if it should load a new request If it should, it calls this function private func requestNewData() { redditService.fetchRedditAllData(completion: { result in DispatchQueue.main.async { switch result { case .success(let newRequest): modelContext.logMessage("Successfully retreived and decoded data from Reddit") // Log the success //modelContext.insert(newRequest) requests.insert(request: newRequest, in: modelContext) case .failure: modelContext.logMessage("Failed to retrieve and decode data from Reddit") } } }) } The code for the fetch function is here: func fetchRedditAllData(completion: @escaping (Result<Request, Error>) -> Void) { // Try to construct the URL and return if it fails guard let url = URL(string: RedditRequests.all) else { context.logMessage("Failed to contruct the url for r/all") return } // Try to retrieve data from the URL session.dataTask(with: url, completionHandler: { data, _, error in // If it fails, log the failure if let error = error { self.context.logMessage("Failed to retrieve data from the r/all URL.\n\(error.localizedDescription)") } else { // If it doesn't fail, try to decode the data do { let data = data ?? Data() // Get data let response = try self.decoder.decode(Request.self, from: data) // Decode JSON into Request model completion(.success(response)) // Return response (request) if successful self.context.logMessage("Decoded data") } catch { completion(.failure(error)) self.context.logMessage("Failed to decode data into a Request.\n\(error.localizedDescription)") } } }).resume() } If I don't try to access request.posts before saving it to the context, it works fine. It will fetch the data and store it to the phone and then display it on the phone. When I try to access request.posts to do some filtering, it crashes. Does anyone have any ideas?
Posted
by
Post not yet marked as solved
1 Replies
439 Views
Using ImageCaptureCore, to send PTP devices to cameras via tether, I noticed that all of my Nikon cameras can take up to an entire minute for PTP events to start logging. My Canons and Sonys are ready instantly. Any idea why? I use the ICDeviceBrowser to browse for cameras, and then request to open the session. According to the docs, it says it's ready after it enumerates its objects? If that's the case, is there a way to bypass that? Even on an empty SD card it's slow.
Posted
by
Post marked as solved
7 Replies
1k Views
Given that SwiftUI and modern programming idioms promote asynchronous activity, and observing a data model and reacting to changes, I wonder why it's so cumbersome in Swift at this point. Like many, I have run up against the problem where you perform an asynchronous task (like fetching data from the network) and store the result in a published variable in an observed object. This would appear to be an extremely common scenario at this point, and indeed it's exactly the one posed in question after question you find online about this resulting error: Publishing changes from background threads is not allowed Then why is it done? Why aren't the changes simply published on the main thread automatically? Because it isn't, people suggest a bunch of workarounds, like making the enclosing object a MainActor. This just creates a cascade of errors in my application; but also (and I may not be interpreting the documentation correctly) I don't want the owning object to do everything on the main thread. So the go-to workaround appears to be wrapping every potentially problematic setting of a variable in a call to DispatchQueue.main. Talk about tedious and error-prone. Not to mention unmaintainable, since I or some future maintainer may be calling a function a level or two or three above where a published variable is actually set. And what if you decide to publish a variable that wasn't before, and now you have to run around checking every potential change to it? Is this not a mess?
Posted
by
Post not yet marked as solved
0 Replies
398 Views
I have a regular app and an app with LSUIElement=YES. Both have Swift app lifecycle main entry points. Both have an app group ".com.company.app". Both can read and write prefs and see each other's values. But I can't for the life of me get changes from one app to notify the other. At first I tried NotificationCenter, but then learned (thanks to this thread) that you have to use KVO or Combine. I tried both. Both get the initial value, but never see subsequent changes. Combine seems to just wrap KVO, looking at the stack trace. I'm subscribing to updates like this: let defaults = UserDefaults(suiteName: "<TEAMID>.com.company.app")! defaults .publisher(for: \.enabled) .handleEvents(receiveOutput: { enabled in print("Enabled is now: \(enabled)") }) .sink { _ in } .store(in: &subs) … extension UserDefaults { @objc var enabled: Bool { get { return bool(forKey: "Enabled") } set { set(newValue, forKey: "Enabled") } } } I'm writing to prefs like this: let defaults = UserDefaults(suiteName: "<TEAMID>.com.company.app")! defaults.set(enabled, forKey: "Enabled") Am I missing something?
Posted
by
Post not yet marked as solved
0 Replies
676 Views
Hi all, I have some code that works on iOS 14 up to 16 and now suddenly doesn't work on iOS 17 so I guess it's a regression. Here's a snippet of a minimal reproducible example. SubView is supposed to update correctly when the button is pressed but that's not happening on iOS 17. onChange is not called either. struct ContentView: View { @ObservedObject var viewModel: ViewModel var body: some View { SubViewContainer(wrapper: $viewModel.wrapper) } } struct SubViewContainer: View { @Binding var wrapper: ValueWrapper var body: some View { SubView(value: $wrapper.value) } } struct SubView: View { @Binding var value: Bool var body: some View { Button(action: { value.toggle() }) { Text(value ? "true" : "false") } } } class ViewModel: ObservableObject { @Published var wrapper = ValueWrapper() } class ValueWrapper: ObservableObject { @Published var value = true } Any clue what is going on?
Posted
by