Search code examples
swiftdecodecombinepublisher

Combine replace `.compactMap` with custom `.decode`


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?


Solution

  • 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.

    👍🏻