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

Posts under Combine tag

18 Posts

Post

Replies

Boosts

Views

Activity

watchOS app with @Published properties fails to compile in Xcode 26.0.1 - "missing import of defining module 'Combine'
I'm developing a watchOS companion app for my iOS app in Xcode 26.0.1 on macOS Sequoia. The watch app uses a simple ObservableObject class with @Published properties for state management, which compiles fine for iOS but fails for the watchOS target. Error: Initializer 'init(wrappedValue:)' is not available due to missing import of defining module 'Combine' Code: swiftimport SwiftUI import WatchConnectivity import Combine // Added explicitly class WatchConnectivityProvider: NSObject, ObservableObject { @Published var distance: Double = 0 @Published var isActive: Bool = false // Additional @Published properties... } Environment: Xcode 26.0.1 (17A400) macOS Sequoia watchOS deployment target: 11.0 Apple Watch Series 11 running watchOS 11.6.1 What I've tried: Adding import Combine explicitly Cleaning build folder Verifying target membership This same code pattern works in the iOS target Is @Published / Combine supported differently in watchOS under Xcode 26? This code worked in previous Xcode versions. What's the recommended approach for observable state in watchOS apps with Xcode 26?
0
1
74
Oct ’25
XCode 26.0.1/iOS 26 unable to mark class as ObservableObject
Started a new X-Code Project after updating to 26.0.1 and realized that I get an error when trying to mark a class as ObservableObject => "Class XYZ does not conform to Protocol 'ObservableObject'. Strange behaviour, because at old projects the code is working even though the build options are the same and other settings like iOS version in Target are the same. There must be something chaged under the hood of XCode? I have to import Combine now, before I could write my class, e.g. CoreData Datamanager: ObservableObject only using CoreData.
4
0
268
Oct ’25
How can I get a Subscriber to subscribe to get only 4 elements from an array?
Hello, I am trying to implement a subscriber which specifies its own demand for how many elements it wants to receive from a publisher. My code is below: import Combine var array = [1, 2, 3, 4, 5, 6, 7] struct ArraySubscriber<T>: Subscriber { typealias Input = T typealias Failure = Never let combineIdentifier = CombineIdentifier() func receive(subscription: any Subscription) { subscription.request(.max(4)) } func receive(_ input: T) -> Subscribers.Demand { print("input,", input) return .max(4) } func receive(completion: Subscribers.Completion<Never>) { switch completion { case .finished: print("publisher finished normally") case .failure(let failure): print("publisher failed due to, ", failure) } } } let subscriber = ArraySubscriber<Int>() array.publisher.subscribe(subscriber) According to Apple's documentation, I specify the demand inside the receive(subscription: any Subscription) method, see link. But when I run this code I get the following output: input, 1 input, 2 input, 3 input, 4 input, 5 input, 6 input, 7 publisher finished normally Instead, I expect the subscriber to only "receive" elements 1, 2, 3, 4 from the array. How can I accomplish this?
0
0
114
Aug ’25
AsyncPublisher for AVPlayerItem not working
Hello, I'm trying to subscribe to AVPlayerItem status updates using Combine and it's bridge to Swift Concurrency – .values. This is my sample code. struct ContentView: View { @State var player: AVPlayer? @State var loaded = false var body: some View { VStack { if let player { Text("loading status: \(loaded)") Spacer() VideoPlayer(player: player) Button("Load") { Task { let item = AVPlayerItem( url: URL(string: "https://sample-videos.com/video321/mp4/360/big_buck_bunny_360p_5mb.mp4")! ) player.replaceCurrentItem(with: item) let publisher = player.publisher(for: \.status) for await status in publisher.values { print(status.rawValue) if status == .readyToPlay { loaded = true break } } print("we are out") } } } else { Text("No video selected") } } .task { player = AVPlayer() } } } After I click on the "load" button it prints out 0 (as the initial status of .unknown) and nothing after – even when the video is fully loaded. At the same time this works as expected (loading status is set to true): struct ContentView: View { @State var player: AVPlayer? @State var loaded = false @State var cancellable: AnyCancellable? var body: some View { VStack { if let player { Text("loading status: \(loaded)") Spacer() VideoPlayer(player: player) Button("Load") { Task { let item = AVPlayerItem( url: URL(string: "https://sample-videos.com/video321/mp4/360/big_buck_bunny_360p_5mb.mp4")! ) player.replaceCurrentItem(with: item) let stream = AsyncStream { continuation in cancellable = item.publisher(for: \.status) .sink { if $0 == .readyToPlay { continuation.yield($0) continuation.finish() } } } for await _ in stream { loaded = true cancellable?.cancel() cancellable = nil break } } } } else { Text("No video selected") } } .task { player = AVPlayer() } } } Is this a bug or something?
0
0
100
Jun ’25
Shouldn't SwiftUI only re-renders if var is used on view?
Why is the SwiftUI re-render the UI event if the view does not use the counter like in the example bellow...shouldn't SwiftUI framework be smart enough to detect that?? import SwiftUI class ViewModel: ObservableObject { @Published var counter: Int = 0 // Not used in the view's body @Published var displayText: String = "Hello" // Used in the view's body } struct ContentView: View { @StateObject private var viewModel = ViewModel() var body: some View { VStack { Text(viewModel.displayText) // Depends on displayText } .onChange(of: viewModel.counter) { newValue in print("Counter changed to: \(newValue)") } } } Is there any solution more elegant without using Publishers??
2
0
132
Jun ’25
ObservableObjects get retained after a TextField is focused
When presenting a SwiftUI sheet containing ObservableObject's injected using environmentObject(_) modifier, the objects are unexpectedly retained after the sheet is dismissed if a TextField within the sheet gains focus or is edited. This issue occurs on iOS and iPadOS (on macOS the objects are always released), observable both in the simulator and on physical devices, and happens even when the view does not explicitly reference these environment objects, and the TextField's content isn't bound to them. Expected Results: When the sheet is dismissed, all environment objects passed to the sheet’s content view should be released (deinitialized), regardless of whether the TextField was focused or edited. Actual Results: If the TextField was focused or edited, environment objects (ObservableA and ObservableB) are retained after the sheet is dismissed. They are not deinitialized as expected, leading to unintended retention. Interestingly, previously retained copies of these environment objects, if any, are released precisely at the moment the TextField becomes focused on subsequent presentations, indicating an inconsistent lifecycle behavior. I have filed an issue FB17226970 Sample Code Below is a sample code that consistently shows the issue on iOS 18.3+. Steps to Reproduce: Run the attached SwiftUI sample. Tap the button labeled “Show Sheet” to present a sheet. Tap on the TextField to focus or begin editing. Dismiss the sheet by dragging it down or by other dismissal methods (e.g., tapping outside on iPadOS). import SwiftUI struct ContentView: View { @State private var showSheet: Bool = false var body: some View { VStack { Button("Show Sheet") { showSheet = true } } .sheet(isPresented: $showSheet) { SheetContentView() .environmentObject(ObservableA()) .environmentObject(ObservableB()) } } } struct SheetContentView: View { @State private var text: String = "" var body: some View { TextField("Select to retain observable objects", text: $text) .textFieldStyle(.roundedBorder) } } final class ObservableA: ObservableObject { init() { print(type(of: self), #function) } deinit { print(type(of: self), #function) } } final class ObservableB: ObservableObject { init() { print(type(of: self), #function) } deinit { print(type(of: self), #function) } } #Preview { ContentView() }
2
0
118
Apr ’25
Actors with Combine publishers as properties.
Is it ok for an Actor type to have a Publisher as a property to let others observe changes over time? Or use the @Published property wrapper to achieve this? actor MyActor { var publisher = PassthroughSubject<Int, Never>() var data: Int { didSet { publisher.send(data) } } ... } // Usage var tasks = Set<AnyCancellable>() let actor = MyActor() Task { let publisher = await actor.publisher publisher.sink { print($0) }.store(in: &tasks) } This seems like this should be acceptable. I would expect a Publisher to be thread safe, and as long as the Output is a value type things should be fine. I have been getting random EXC_BAD_ACCESS errors when using this approach. But turning on the address sanitizer causes these crashes to go away. I know that isn't very specific but I wanted to start by seeing if this type of pattern is ok to do.
1
6
2.6k
Apr ’25
SwiftUI infinite loop issue with @Environment(\.verticalSizeClass)
I see SwiftUI body being repeatedly called in an infinite loop in the presence of Environment variables like horizontalSizeClass or verticalSizeClass. This happens after device is rotated from portrait to landscape and then back to portrait mode. The deinit method of TestPlayerVM is repeatedly called. Minimally reproducible sample code is pasted below. The infinite loop is not seen if I remove size class environment references, OR, if I skip addPlayerObservers call in the TestPlayerVM initialiser. import AVKit import Combine struct InfiniteLoopView: View { @Environment(\.verticalSizeClass) var verticalSizeClass @Environment(\.horizontalSizeClass) var horizontalSizeClass @State private var openPlayer = false @State var playerURL: URL = URL(fileURLWithPath: Bundle.main.path(forResource: "Test_Video", ofType: ".mov")!) var body: some View { PlayerView(playerURL: playerURL) .ignoresSafeArea() } } struct PlayerView: View { @Environment(\.dismiss) var dismiss var playerURL:URL @State var playerVM = TestPlayerVM() var body: some View { VideoPlayer(player: playerVM.player) .ignoresSafeArea() .background { Color.black } .task { let playerItem = AVPlayerItem(url: playerURL) playerVM.playerItem = playerItem } } } @Observable class TestPlayerVM { private(set) public var player: AVPlayer = AVPlayer() var playerItem:AVPlayerItem? { didSet { player.replaceCurrentItem(with: playerItem) } } private var cancellable = Set<AnyCancellable>() init() { addPlayerObservers() } deinit { print("Deinit Video player manager") removeAllObservers() } private func removeAllObservers() { cancellable.removeAll() } private func addPlayerObservers() { player.publisher(for: \.timeControlStatus, options: [.initial, .new]) .receive(on: DispatchQueue.main) .sink { timeControlStatus in print("Player time control status \(timeControlStatus)") } .store(in: &cancellable) } }
2
0
418
Feb ’25
Listening Changes Out of swiftUI in Observation Framework
Hi, folks. I know that in the new observation, class property changes can be automatically notified to SwiftUI, which is very convenient. But in the new observation framework, how to monitor the property changes of different model classes? For example, class1 has an instance of class2, and I need to notify class1 to perform some actions and make some changes when some properties of class2 are changed. How to do it in observation? In the past, I could use combined methods to write the second part of the code for monitoring. However, using the combined framework in observation is a bit confusing. I know this method can be withObservationTracking(_:onChange:) but it needs to be registered continuously. If Observation is not possible, do I need to change my design structure? Thanks. // Observation @Observable class Sample1 { var count: Int = 0 var name = "Sample1" } @Observable class Sample2 { var count: Int = 0 var name = "Sample2" var sample1: Sample1? init (sample1 : Sample1) { self.sample1 = sample1 } func render() { withObservationTracking { print("Accessing Sample1.count: \(sample1?.count ?? 0)") } onChange: { [weak self] in print("Sample1.count changed! Re-rendering Sample2.") self?.handleSample1CountChange() } } private func handleSample1CountChange() { print("Handling count change in Sample2...") self.count = sample1?.count ?? 0 } } // ObservableObject class Sample1: ObservableObject { @Published var count: Int = 0 var name = "Sample1" } class Sample2: ObservableObject { @Published var count: Int = 0 var name = "Sample1" var sample1: Sample1? private var cancellables = Set<AnyCancellable>() init (sample1 : Sample1) { self.sample1 = sample1 setupSubscribers() } private func setupSubscribers() { sample1?.$count .receive(on: DispatchQueue.main) .sink { [weak self] count in guard let self = self else { return } // Update key theory data self.count = count self.doSomeThing() } .store(in: &cancellables) } private func doSomeThing() { print("Count changes, need do some thing") } }
1
0
307
Feb ’25
Combine delay & switchToLatest publisher don't emit value sometimes
Hello, I recently implemented a conditional debounce publisher using Swift's Combine. If a string with a length less than 2 is passed, the event is sent downstream immediately without delay. If a string with a length of 2 or more is passed, the event is emitted downstream with a 0.2-second delay. While writing test logic related to this, I noticed a strange phenomenon: sometimes the publisher, which should emit events with a 0.2-second delay, does not emit an event. The test code below should have all indices from 1 to 100 in the array, but sometimes some indices are missing, causing the assertion to fail. Even after observing completion, cancel, and output events through handleEvents, I couldn't find any cause. Am I using Combine incorrectly, or is there a bug in Combine? I would appreciate it if you could let me know. import Foundation import Combine var cancellables: Set<AnyCancellable> = [] @MainActor func text(index: Int, completion: @escaping () -> Void) { let subject = PassthroughSubject<String, Never>() let textToSent = "textToSent" subject .map { text in if text.count >= 2 { return Just<String>(text) .delay(for: .seconds(0.2), scheduler: RunLoop.main) .eraseToAnyPublisher() } else { return Just<String>(text) .eraseToAnyPublisher() } } .switchToLatest() .sink { if $0.count >= 2 { completion() } }.store(in: &cancellables) for i in 0..<textToSent.count { let stringIndex = textToSent.index(textToSent.startIndex, offsetBy: i) let stringToSent = String(textToSent[textToSent.startIndex...stringIndex]) subject.send(stringToSent) } } var array = [Int]() for i in 1...100 { text(index: i) { array.append(i) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5) { for i in 1...100 { assert(array.contains(i)) } } RunLoop.main.run(until: .now + 10)
0
0
382
Feb ’25
Why timer isn't working with button in SwiftUI
.onReceive construction isn't working with button long press gesture but tap is working, it increases count by 1. Construction should increase count every 0.5 seconds if button pressed struct Test: View { @State private var count = 0 @State private var isLongPressed = false var body: some View { VStack { Text("Count: \(count)") Button("Increase") { count += 1 print("Button tapped") } .onLongPressGesture { isLongPressed = true print("Long press started") } .onLongPressGesture(minimumDuration: 0) { isLongPressed = false print("Long press ended") } } .onReceive(Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()) { _ in if isLongPressed { count += 1 print("Count increased: \(count)") } } } }
1
0
372
Jan ’25
Auto-instrumentaion for URLSession async/wait
We have product for network monitoring and we are't able to add support auto-instrumenting the networking requests for URLSession async/wait methods as these methods are't exposed to dynamic environment or not exposed to ObjC and we con't use any of the run-time functionality and we con't override these methods as these methods are't public. looking for a way to add some kind of logic so that when customers use our product they don't have to add any code from there end to monitor this system.
1
0
395
Jan ’25
Strange crash when using .values from @Published publisher
Given the below code with Swift 6 language mode, Xcode 16.2 If running with iOS 18+: the app crashes due to _dispatch_assert_queue_fail If running with iOS 17 and below: there is a warning: warning: data race detected: @MainActor function at Swift6Playground/PublishedValuesView.swift:12 was not called on the main thread Could anyone please help explain what's wrong here? import SwiftUI import Combine @MainActor class PublishedValuesViewModel: ObservableObject { @Published var count = 0 @Published var content: String = "NA" private var cancellables: Set<AnyCancellable> = [] func start() async { let publisher = $count .map { String(describing: $0) } .removeDuplicates() for await value in publisher.values { content = value } } } struct PublishedValuesView: View { @ObservedObject var viewModel: PublishedValuesViewModel var body: some View { Text("Published Values: \(viewModel.content)") .task { await viewModel.start() } } }
2
0
583
Dec ’24
Async/Await and updating state
When using conformance to ObservableObject and then doing async work in a Task, you will get a warning courtesy of Combine if you then update an @Published or @State var from anywhere but the main thread. However, if you are using @Observable there is no such warning. Also, Thread.current is unavailable in asynchronous contexts, so says the warning. And I have read that in a sense you simply aren't concerned with what thread an async task is on. So for me, that begs a question. Is the lack of a warning, which when using Combine is rather important as ignoring it could lead to crashes, a pretty major bug that Apple seemingly should have addressed long ago? Or is it just not an issue to update state from another thread, because Xcode is doing that work for us behind the scenes too, just as it manages what thread the async task is running on when we don't specify? I see a lot of posts about this from around the initial release of Async/Await talking about using await MainActor.run {} at the point the state variable is updated, usually also complaining about the lack of a warning. But ow years later there is still no warning and I have to wonder if this is actually a non issue. On some ways similar to the fact that many of the early posts I have seen related to @Observable have examples of an @Observable ViewModel instantiated in the view as an @State variable, but in fact this is not needed as that is addressed behind the scenes for all properties of an @Observable type. At least, that is my understanding now, but I am learning Swift coming from a PowerShell background so I question my understanding a lot.
5
0
1.5k
Dec ’24
Diffable Data Source Warning: Non-Thread Confined Updates
Hello, I’ve encountered a warning while working with UITableViewDiffableDataSource. Here’s the exact message: Warning: applying updates in a non-thread confined manner is dangerous and can lead to deadlocks. Please always submit updates either always on the main queue or always off the main queue - view=&lt;UITableView: 0x7fd79192e200; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = &lt;NSArray: 0x600003f3c9f0&gt;; backgroundColor = &lt;UIDynamicProviderColor: 0x60000319bf80; provider = &lt;NSMallocBlock: 0x600003f0ce70&gt;&gt;; layer = &lt;CALayer: 0x6000036e8fa0&gt;; contentOffset: {0, -116}; contentSize: {375, 20}; adjustedContentInset: {116, 0, 49, 0}; dataSource: &lt;TtGC5UIKit29UITableViewDiffableDataSourceOC17ArticleManagement21DiscardItemsViewModel17SectionIdentifierSS: 0x600003228270&gt;&gt; OS: iOS Version: iOS 17+, Xcode Version: 16.0, Frameworks: UIKit, Diffable Data Source, View: UITableView used with a UITableViewDiffableDataSource. Steps to Reproduce: Using a diffable data source with a table view. Applying snapshot updates in the data source from a main thread. Warning occurs intermittently during snapshot application. Expected Behavior: The snapshot should apply without warnings, provided the updates are on a main thread. Actual Behavior: The warning suggests thread safety issues when applying updates on non-thread-confined queues. Questions: Is there a recommended best practice to handle apply calls in diffable data sources with thread safety in mind? Could this lead to potential deadlocks if not addressed? Note :- I confirm I am always reloading / reconfiguring data source on main thread. Please find the attached screenshots for the reference. Any guidance or clarification would be greatly appreciated!
0
0
604
Dec ’24