I'm trying to better understand how the onReceive modifier behaves in SwiftUI, specifically how its subscription lifecycle relates to view updates.
Consider this example:
TextField("Name", text: $name)
.onReceive(Just(name)) { value in
print(value)
}
This closure runs every time name changes. A common explanation is that SwiftUI recomputes body, which creates a new Just(name) publisher each time.
However, this raises some questions for me about how onReceive actually works internally:
When SwiftUI recomputes body, is the onReceive modifier recreated and resubscribed?
Does SwiftUI automatically cancel the previous subscription when the view updates?
Combine
RSS for tagCustomize handling of asynchronous events by combining event-processing operators using Combine.
Posts under Combine tag
13 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
This is a successor to:
https://developer.apple.com/forums/thread/814231
I went into a slightly different direction. I generated more AI slop that use NSLock. Then I had the NSLock usage changed to Mutex usage. Now it crashes with:
Task 13: EXC_BREAKPOINT (code=1, subcode=0x18d29326c)
On one of the mutex closures. With an extended description:
warning: TypeSystemSwiftTypeRef::operator(): had to engage SwiftASTContext fallback for type $s7Combine10PublishersO21LineBreakingPublisherE11SplitAtZeroV12Subscription33_D18F5AAE73662968F407B0A79FBD1F8DLLCy_x_qd__GD
I put the class, a Subscription nested in its corresponding Publisher operator, in the given file
Subscription.txt
As the title suggests, I have a class marked with @Observable. Within the class I have multiple var's. When one of my var's changes (formation), I want to run an updateOrCreateContent(). I had thought I could just do this with a bit of combine, but I'm struggling to get it working...
The code below has a compile error at $formation
When I mark formation @Published, it generates a different compile error: "Invalid redeclaration of synthesized property '_formation'"
any help appreciated
thanks
class LayoutModel {
var players: [Player] = []
var formation: Formation = .f433
var cancellables = Set<AnyCancellable>()
init(players: [Player], formation: Formation) {
self.players = players
self.formation = formation
updateOrCreateContent()
$formation.sink(receiveValue: { _ in
self.updateOrCreateContent()
})
.store(in: &cancellables)
}
I'm making an operator Publisher, which has to wrap the upstream publisher. But I want the operator to conditionally conform to ConnectablePublisher, but only when the upstream publisher does the same.
I can make my connect() call the upstream's connect(), but is that all I have to do? That Apple's plumbing will automatically hold back the initial call to the Subscription object if the publisher is connectable. Otherwise, I need to make a flag in the subscription for when to connect, which would involve an infinitely copyable struct somehow send a message to a unique class/actor.
That last part makes sense to me, but it also seems like too much work for something plug-and-play. Having Apple's implementation taking care of that issue also makes sense, and would be a better solution.
Just trying to understand the documentation.
Obviously, we can send a request to the service to return all the data at once. Can the data arrive in pieces, involving either multiple async callbacks or a Combine Publisher?
I have several combine pipelines in my watch and iPhone app. While background tasks on the iPhone work correctly (the combine pipelines all activate), on the watch the pipelines do not get activated. I have an internal log reporting that data is being fed to the sources but is not propagating to the sinks.
Thoughts?
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
watchOS
Combine
Background Tasks
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?
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.
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?
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?
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??
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()
}
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.