Sink does not receive values when connecting to finished a Publisher
PassthroughSubject
receives a value and passes it along, it doesn't store the value so if you subscribe to it after the value passed through it, you won't receive it.
You can use CurrentValueSubject
, which will store the latest value and resend it whenever somebody subscribes to it.
All of this is moot if you send completion: .finished
though. A completed publisher won't send any values to a subscriber because it's completed.
This is your fixed code:
func somePublisher() -> AnyPublisher<Bool, Never> {
let subject = CurrentValueSubject<Bool, Never>(true)
return subject.eraseToAnyPublisher()
}
var bag: Set<AnyCancellable> = []
somePublisher()
.first()
.sink { _ in
print("Completed")
} receiveValue: {
print("Received \($0)")
}
.store(in: &bag)
Combine sink: ignore receiveValue, only completion is needed
CurrentValueSubject
seems a confusing choice, because that will send an initial value (of Void
) when you first subscribe to it.
You could make things less ambiguous by using Future
, which will send one-and-only-one value, when it's done.
To get around having to receive values you don't care about, you can flip the situation round and use an output type of Result<Void, Error>
and a failure type of Never
. When processing your network request, you can then fulfil the promise with .failure(error)
or .success(())
, and deal with it in sink:
let pub = Future<Result<Void, Error>, Never> {
promise in
// Do something asynchronous
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
promise(.success(.success(())))
//or
//promise(.success(.failure(error)))
}
}.eraseToAnyPublisher()
// somewhere else...
pub.sink {
switch $0 {
case .failure(let error):
print("Whoops \(error)")
case .success:
print("Yay")
}
}
You're swapping ugly code at one end of the chain for ugly code at the other, but if that's hidden away behind AnyPublisher
and you're concerned with correct usage, that seems the way to go. Consumers can see exactly what to expect from looking at the output and error types, and don't have to deal with them in separate closures.
Swift Combine: sink() called after core data entity deletion
You can use faultingState
to track this scenario. From documentation:
0
if the object is fully initialized as a managed object and not transitioning to or from another state, otherwise some other value. This property allows you to determine if an object is in a transitional phase when receiving a key-value observing change notification.
So you can ignore this event like this:
.sink { [weak self] sold in
guard let self = self, car.faultingState == 0 else { return }
//
}
If you wanna actually cancel this sink, you can store cancellables inside the object so you can cancel them during prepareForDeletion
.
To do this you need to change object code generation, more info can be found here. Change to Category/Extension - in this case you can create a class and override prepareForDeletion
, and Xcode will still generate all the properties for you.
Now you can move all publisher logic into your class:
@objc(Car)
public class Car: NSManagedObject {
private var subscribers = Set<AnyCancellable>()
func observe<Value: Equatable>(keyPath: KeyPath<Item, Value>, receiveValue: @escaping ((Value) -> Void)) {
publisher(for: keyPath, options: [.new])
.removeDuplicates()
.receive(on: RunLoop.main)
.sink(receiveValue: receiveValue)
.store(in: &subscribers)
}
public override func prepareForDeletion() {
super.prepareForDeletion()
subscribers.removeAll()
}
}
Or just use it to store cancellables.
Future Combine sink does not recieve any values
Try to change/remove .cancel() method on your subscriptions. Seems you subscribe to the publisher, and then immediately cancel the subscription. The better option is to retain and store all your subscriptions in the cancellable set.
Combine sink does not complete when publisher fails
The issue you're seeing may be that a 404 isn't considered an error by underlying Cocoa libraries (URLSession specifically). This seems like something that's being done (or not) within the Squid library, and you're reacting to what it provides.
URLSession itself only throws errors when it's unable to get a response from the server - so when issues are related to not being able to resolve the hostname or make a connection kinds of things. If you do make a connection and get a response, URLSession won't throw an error, but the status code will be correctly reflected - it'll just "look" like a successful request.
The example above doesn't show that section, but what happens with a 404 response from URLSession
is that the request completes, and the status code 404
is encoded within the response, but it's not thrown as an error state.
A typical pattern of dealing with this in a Combine publisher chain is by passing the result data from URLSession into a tryMap
operator, where you can do further inspection of the result (status code, data, etc) and determine if you want to turn that into a thrown error (such as if you get a 404 response).
You can find an example of this kind of pattern at https://heckj.github.io/swiftui-notes/#patterns-datataskpublisher-trymap. The gist of this pattern is allowing you to define whatever error you want (assuming you a thrown exception in those cases) based on inspection of the result.
You can find other examples of using Combine (along with URLSession.dataTaskPublisher
) in Using Combine.
Related Topics
How to Get Random Element from a Set in Swift
How to Use Sf Rounded Font in Swiftui
Why Unsaferawpointer Shows Different Result When Function Signatures Differs in Swift
How to Bind Different Associated Values in a Swift Enum to the Same Var
"Generic Parameter Could Not Be Inferred" in Swiftui Uiviewrepresentable
How to Test Whether Generic Variable Is of Type Anyobject
Extending Collection with a Recursive Property/Method That Depends on the Element Type
Swiftui - Wait Until Firestore Getdocuments() Is Finished Before Moving On
Self' Captured by a Closure Before All Members Were Initialized
Can an Enum Contain Another Enum Values in Swift
Swiftyjson - Call Can Throw, But It Is Marked with 'Try' and the Error Is Not Handled
Swiftui Exporting or Sharing Files
Confusion Due to Swift Lacking Implicit Conversion of Cgfloat
Swift Dictionary: Remove Time Complexity