0

I have created an AnyPublisher for subscribing to Firestore documents and the Output type is a DocumentSnapshot.

I run it like this...

firestoreSubscription.subscribe(MyHashable(), "/user/1234567890")
    .compactMap { try? $0.data(as: UserDoc.self }

The return type of this is <UserDoc, Never> which I want to keep.

This worked but I thought it would be cool if I could use the .decode function on Publisher to make it a bit more Combiney.

So I created this...

public struct FirestoreDecoder: TopLevelDecoder {
    public init() {}
    
    public typealias Input = DocumentSnapshot
    
    public func decode<T>(_ type: T.Type, from: DocumentSnapshot) throws -> T where T : Decodable {
        try! from.data(as: type)!
    }
}

So now I try this...

environment.firestoreSubscription.subscribe(userSubscriptionID, "/user/\(state.userID)")
    .decode(type: UserDoc.self, decoder: FirestoreDecoder())

But... TopLevelDecoder throws rather than returning nil. I wonder, is it possible to throw away the throws and default back to my compact map solution that I had whilst using the .decode method?

Or... should I just keep using the .compactMap?

Also... will the throw on the .decode end the publisher?

Fogmeister
  • 76,236
  • 42
  • 207
  • 306

2 Answers2

1

will the throw on the .decode end the publisher

Yes, this will result a failure in the stream - if you are subscribing to changes on Firestore this is probably not what you want

You can use the catch operator of Combine to handle a failure in the stream

Using your example, to ignore a failure you would catch and return an Empty publisher.

environment.firestoreSubscription.subscribe(userSubscriptionID, "/user/\(state.userID)")
    .decode(type: UserDoc.self, decoder: FirestoreDecoder())
    .catch{ _ in Empty<UserDoc, Never>().eraseToAnyPublisher() }
Oliver Atkinson
  • 7,970
  • 32
  • 43
0

OK, by changing the FirestoreDecoder to this...

import Firebase

public struct FirestoreDecoder {
    public static func decode<T>(_ type: T.Type) -> (DocumentSnapshot) -> T? where T: Decodable {{
        try? $0.data(as: type)
    }}
}

I was able to do...

firestoreSubscription.subscribe(MyHashable(), "/user/1234567890")
    .compactMap(FirestoreDecoder.decode(UserDoc.self))

Which is similar to what I had in the first place but pushes the dependency on Firestore out to the separate module.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306