0

I have numerous pages where users may input information. They may input the fields with dates, numbers, or text.

I am trying to receive all changes in Combine and get their outputs as Encodable so I can easily upload the results to the network.

A String is Encodable, so I thought this would be easy but I cannot get this to work in Combine. I get a compiler error:

Cannot convert return expression of type 'Publishers.Map<Published.Publisher, Encodable>' to return type 'Published.Publisher'

There is a workaround where I add another property in SampleTextHandler that is @Published var userTextEncodable: Encodable but that's not what I want to do.

import Combine

protocol FieldResponseModifying {
    var id: String { get }
    var output: Published<Encodable>.Publisher { get }
}


struct SampleTextWrapper {
    var output: Published<Encodable>.Publisher {
        // Cannot convert return expression of type 'Publishers.Map<Published<String>.Publisher, Encodable>' to return type 'Published<Encodable>.Publisher'
        handler.$userTextOutput.map { $0 as Encodable}
    }
    
    let id = UUID().uuidString
    let handler = SampleTextHandler()
}

class SampleTextHandler {
   @Published var userTextOutput = ""
    init () { }
}
Dr. Mr. Uncle
  • 484
  • 5
  • 12
  • Why not `var output: Published.Publisher` and remove `map`? – vadian Oct 18 '21 at 16:14
  • @vadian good question. Because I have dates, integers, and doubles that I also want to be outputted. So the type may vary, but all of them conform to encodable. – Dr. Mr. Uncle Oct 18 '21 at 16:16
  • I think the intention of the [Combine `eraseToAnyType` function](https://developer.apple.com/documentation/combine/publisher/erasetoanypublisher()) is to help with times like this, where you have a nested publisher type like `Publishers.Map.Publisher, Encodable>` and want something simpler to expose as an API. I'm not 100% sure where it'd fit in here though – 4D45 Oct 18 '21 at 19:06
  • @4D45 thanks for the response. Adding `eraseToAnyPublisher` just yields another compiler error, am I using it correctly? "error: MyPlayground.playground:13:56: error: cannot convert return expression of type 'AnyPublisher.Publisher.Failure>' (aka 'AnyPublisher') to return type 'Published.Publisher' handler.$userTextOutput.map { $0 as Encodable}.eraseToAnyPublisher()" – Dr. Mr. Uncle Oct 18 '21 at 19:22
  • Yeah, I *think* it's what's needed, possibly together with a subsequent cast, but I'm unfortunately not familiar enough with Combine to figure it out myself either! – 4D45 Oct 18 '21 at 19:56

1 Answers1

1

Combine uses generics heavily. For example, the type returned by your use of map is Publishers.Map<Published<Value>.Publisher, Encodable>. So you could declare your property like this:

var output: Publishers.Map<Published<Encodable>.Publisher, Encodable> {
    handler.$userTextOutput.map { $0 as Encodable}
}

But now your property's type depends closely on how it is implemented. If you change the implementation, you'll have to change the type.

Instead, you should almost certainly use the “type eraser” AnyPublisher, like this:

var output: AnyPublisher<Encodable, Never> {
    handler.$userTextOutput
        .map { $0 as Encodable }
        .eraseToAnyPublisher()
}

You're probably going to run into another issue down the line, due to your use of the Encodable existential. When you hit that, you'll want to post another question.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848