Использование обобщений Swift в функции получения RxSwift — различные проблемы

#swift #generics #rx-swift

#swift #обобщения #rx-swift

Вопрос:

Вот функция:

 func registerFor<Element>(relayId id: String) -> Driver<Element>? {
    guard let relay = relays[id] as? BehaviorRelay<Element> else { return nil }
    return relay.asObservable()
        .distinctUntilChanged { a, b in
            return a != b
        }.flatMapLatest { value in
            return Observable.create { observer in
                observer.on(.next(value))
                return Disposables.create()
            }
        }.asDriver(onErrorJustReturn: Element())
}
  

distinctUntilChanged Строка выдает следующую ошибку:

 Contextual closure type '(Element) throws -> _' expects 1 argument, 
but 2 were used in closure body
  

asDriver Строка выдает следующую ошибку (конечно):

 Non-nominal type 'Element' does not support explicit initialization
  

Контекст: У меня есть класс, который в идеале имеет коллекцию BehaviorRelay ов различных типов (строки, целые числа и т.д.). Element обычно используется для этих типов, но это создает две проблемы:

  1. distinctUntilChanged настаивает на наличии замыкания (например: если бы этот метод вернулся, Driver<String> было бы достаточно просто использовать, distinctUntilChanged() но обобщение Element заставляет его жаловаться на отсутствие замыкания);
  2. onErrorJustReturn требуется конкретное значение, но Element оно является общим.

Следующий «обходной путь» может сработать, но я подозреваю, что есть лучшие решения

 protocol Inii {
    init()
}

func registerFor(relayId id: String, def: Inii.Type) -> Driver<Inii>? {
    return relays[id]?.asObservable()
        .distinctUntilChanged { _, _ in
            return true
        }.flatMapLatest { value in
            return Observable.create { observer in
                observer.on(.next(value))
                return Disposables.create()
            }
        }.asDriver(onErrorJustReturn: def.init())
}
  

Хотя я все еще не уверен, что поместить в distinctUntilChanged закрытие.


Приложение A

Я считаю, что при реализации distinctUntilChanged замыкания для не общего типа требуется следующее:

 .distinctUntilChanged { previousValue, currentValue in
    return previousValue == currentValue
}
  

Однако при использовании с обобщением Element по-прежнему выдается следующая ошибка:

 Contextual closure type '(Inii) throws -> _' expects 1 argument, 
but 2 were used in closure body
  

Приложение B

Вот еще одна альтернатива с немного другой проблемой:

 protocol Inii {
    init()
}

var relay = BehaviorRelay<String>(value: "")

func registerFor<Element>(def: Element.Type) -> Driver<Element> where Element: Inii {
    return relay.asObservable()
        .distinctUntilChanged { previousValue, currentValue in
            return previousValue == currentValue
        }.flatMapLatest { value in
            return Observable.create { observer in
                observer.on(.next(value))
                return Disposables.create()
            }
        }.asDriver(onErrorJustReturn: def.init())
}
  

Ошибка в этом случае заключается в:

 Member 'next' in 'Event<_>' produces result of type 'Event<Element>', 
but context expects 'Event<_>'
  

в observer.on строке

Ответ №1:

Вы можете использовать distinctUntilChanged() без закрытия, если Element соответствует Equatable :

 protocol EmptyInit {
    init()
}

func registerFor<Element>(relayId id: String) -> Driver<Element>? where Element: Equatable, Element: EmptyInit {
    guard let relay = relays[id] as? BehaviorRelay<Element> else { return nil }
    return relay.asObservable()
        .distinctUntilChanged()
        .flatMapLatest { value in
            return Observable.create { observer in
                observer.on(.next(value))
                return Disposables.create()
            }
        }.asDriver(onErrorJustReturn: Element())
}