1

I have a protocol for fetching database objects by PrimaryKey

typealias PrimaryKey = String

protocol PrimaryKeyConvertible {
    var pkValue : PrimaryKey { get }
    static func pkObject(key: PrimaryKey) -> Self?
}

and I want to extend the SignalProducerType to be able to operate on a SignalProducer.Value of that type.

So the Single object extension (single as in not Array) works fine and implemented as following:

extension SignalProducerType
   where Value: PrimaryKeyConvertible
{
    func fetchOnMainThread() -> SignalProducer<Value?, Error> {
        return
        self.map{ (obj: Value) -> PrimaryKey in
            return obj.pkValue
        }
        .observeOn(UIScheduler())
        .map{ (key: PrimaryKey) -> Value? in
            return Value.pkObject(key)
        }
    }
}

But when I try to implement it on an Array of these elements i hit some compilation challenges:

extension SignalProducerType
{
    func fetchOnMainThread<P: PrimaryKeyConvertible where Self.Value == Array<P>>() -> SignalProducer<[P], Error> { //(1)
        return self.map({ (value: Self.Value) -> [PrimaryKey] in
            return value.map{ $0.pkValue } //(2)
        })
    }
}

(1) i suspect that the signature is not communicating the idea to the compiler correctly

(2) produces the following error:

Type of expression is ambiguous without more context

the issue i'm trying to solve is how to let the compiler recognize the the SignalProducer is operating on an Array<P> where P is PrimaryKeyConvertible and have the .map operate on it accordingly ...

my current solution for the array issue is to implement using a generic function as listed below:

func fetchOnMainThread<Value: PrimaryKeyConvertible, Error: ErrorType>
    (signal: SignalProducer<[Value], Error>) -> SignalProducer<[Value], Error> { 
        return signal
            .map{ (convertibles: [Value]) -> [PrimaryKey] in
                return convertibles.map { $0.pkValue }
            }
            .observeOn(UIScheduler())
            .map{ (keys: [PrimaryKey]) -> [Value] in
                return keys.flatMap{ Value.pkObject($0) }
        }
}

and then used for example:

extension GoogleContact: PrimaryKeyConvertible {...}

extension GoogleContact {
  static func fetchGoogleContactsSignal() -> SignalProducer<[GoogleContact], GoogleContactError> { ...}
}

and the call site would be like:

let signal = fetchOnMainThread(GoogleContacts.fetchGoogleContactsSignal()).onNext...

where I would prefer to have it as an extension where it would flow as usual

GoogleContacts
  .fetchGoogleContactsSignal()
  .fetchOnMainThread()

Update

another version of the function I've tried : (@J.Wang)

extension SignalProducerType
    where Value == [PrimaryKeyConvertible]
{
    func fetchArrayOnMainThread2<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
        return self
            .map{ (values: Self.Value) -> [PrimaryKey] in
                return values.map{ $0.pkValue }
            }
            .deliverOnMainThread()
            .map{ (keys: [PrimaryKey]) -> [T] in
                return keys.flatMap{ T.pkObject($0) }
            }
    }

}


    let signal =
        GoogleContacts
            .fetchGoogleContactsSignal()
            .fetchArrayOnMainThread2() //(3) 

(3) Generates error:

'[PrimaryKeyConvertible]' is not convertible to '[GoogleContact]'

Edward Ashak
  • 2,411
  • 2
  • 23
  • 38

1 Answers1

0

Hmm, although I'm not quite sure what the problem is, but I think the following implementation might be what you want.

extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
    func fetchOnMainThread() -> SignalProducer<[PrimaryKey], Error> {
        return self.map { value in
            value.map { $0.pkValue }
        }
    }
}

Try this:

extension SignalProducerType where Value == [PrimaryKeyConvertible]
{
    func fetchOnMainThread<T: PrimaryKeyConvertible>() -> SignalProducer<[T], Error> {
        return self.map { value in
            value.map { $0.pkValue }
        }.map { keys in
            keys.flatMap { T.pkObject($0) }
        }
    }
}
J.Wang
  • 1,136
  • 6
  • 12
  • the where clause in the extension signature is a good idea, but it ends up becoming a problem on the second mapping from [PrimaryKey] back to the [PrimaryKeyConvertible] – Edward Ashak Mar 11 '16 at 04:24
  • @EdwardAshak Sorry, didn't know you want to convert it back. Check my update. – J.Wang Mar 11 '16 at 04:54