Using Swift generics in a RxSwift getter function - various problems - swift

Here's the function:
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())
}
The distinctUntilChanged line throws the following error:
Contextual closure type '(Element) throws -> _' expects 1 argument,
but 2 were used in closure body
The asDriver line throws the following error (of course):
Non-nominal type 'Element' does not support explicit initialization
Context: I have a class that ideally has a collection of BehaviorRelays of various types (Strings, Ints, etc). Element stands in generically for these types, but that creates two problems:
distinctUntilChanged insists of having a closure (eg: if this method returned Driver<String> it would be content simply to use distinctUntilChanged() but the generic Element makes it complain about missing a closure);
onErrorJustReturn requires a concrete value, but Element is generic.
The following "workaround" might work but I suspect there are better solutions
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())
}
Although I'm still unsure what to put in the distinctUntilChanged closure.
Appendix A
I believe that the following is what is required if one is implementing the distinctUntilChanged closure for a non-generic type:
.distinctUntilChanged { previousValue, currentValue in
return previousValue == currentValue
}
However, when used with the generic Element the following error is still thrown:
Contextual closure type '(Inii) throws -> _' expects 1 argument,
but 2 were used in closure body
Appendix B
Here's another alternative with a slightly different problem:
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())
}
Error in this case being:
Member 'next' in 'Event<_>' produces result of type 'Event<Element>',
but context expects 'Event<_>'
at the observer.on line

You can use distinctUntilChanged() without a closure as long as Element conforms to 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())
}

Related

How can I create an operator to implement error chaining?

I want to implement the following operator »:
throwingFunction(arg: T?)».doStuff()
/* if throwingFunction throws an error:
print or log the error
else
returns an object having the doStuff() Method
OR
An Alternative design that I'm open to is
Instead of a throwing Error, the `throwingFunction()`
can be swapped out for a method that returns `Result`
OR
a custom Type, with a generic payload type.
*/
Here is an example of something similar to what I'm looking for. It is a custom implementation of optional chaining made possible using the KeyPath object (credit to Sergey Smagleev
).
precedencegroup Chaining {
associativity: left
}
infix operator ~> : Chaining
extension Optional {
static func ~><T>(value: Wrapped?, key: KeyPath<Wrapped, T> ) -> T? {
return value.map { $0[keyPath: key] }
}
static func ~><T>(value: Wrapped?, key: KeyPath<Wrapped, T?> ) -> T? {
return value.flatMap { $0[keyPath: key] }
}
}
struct Object {
let anotherOptionalObject: AnotherObject?
}
struct AnotherObject {
let value: String
}
let optionalObject: Object? = Object(anotherOptionalObject: AnotherObject(value: "Hello world"))
print(optionalObject~>\.anotherOptionalObject~>\.value) //this prints Optional("Hello world")
print(optionalObject?.anotherOptionalObject?.value) //this also prints Optional("Hello world")
Except, I want the implementation to provide an opportunity for me to handle the error by printing or logging it.
prefix and postfix are unary operators i.e. they only accept one operand, whereas an infix operator is a binary operator i.e. it accepts two operands.
So, static func »(value:key:) -> Preferred? is not correct because it's taking two operands whereas you have defined » as a postfix operator.
For some reason I get "» is not a postfix unary operator" when I don't use the escape syntax. But apart from that it seems to work.
precedencegroup Chaining {
associativity: left
}
infix operator » : Chaining
extension Result {
static func »<T>(value: Self, key: KeyPath<Success, T>) -> T? {
switch value {
case .success(let win):
return win[keyPath: key]
case .failure(let fail):
print(fail.localizedDescription)
return nil
}
}
}
// I included a custom type so that it could be customizable if needed.
enum Err<Wrapped> {
static func »<T>(value: Self, key: KeyPath<Wrapped, T>) -> T? {
switch value {
case .error(let err):
print(err.localizedDescription)
return nil
case .some(let wrapper):
return wrapper[keyPath: key]
}
}
case error(Error)
case some(Wrapped)
}
func errorWrapped() -> Err<String> {
.some("Hello World")
}
func pleaseWork() {
print(errorWrapped()»\.isEmpty)
}

Optional extension for any types

I want to write Optional extension for any types.
My code for integer:
extension Optional where Wrapped == Int {
func ifNil<T>(default: T) -> T {
if self != nil {
return self as! T
}
return default
}
}
var tempInt: Int?
tempInt.ifNil(default: 2) // returns 2
tempInt = 5
tempInt.ifNil(default: 2) // returns 5
It works but it is Optional(Int) extension (Optional where Wrapped == Int), I want to use this extension for any types like Date, String, Double etc.
What are your suggestions?
The answer to your basic question is to just remove the where clause:
extension Optional {
// ... the rest is the same
func isNil<T>(value: T) -> T {
if self != nil {
return self as! T
}
return value
}
}
Now it applies to all Optionals.
But this code is quite broken. It crashes if T is not the same as Wrapped. So you would really mean a non-generic function that works on Wrapped:
extension Optional {
func isNil(value: Wrapped) -> Wrapped {
if self != nil {
return self! // `as!` is unnecessary
}
return value
}
}
But this is just an elaborate way of saying ?? (as matt points out)
extension Optional {
func isNil(value: Wrapped) -> Wrapped { self ?? value }
}
Except that ?? is much more powerful. It includes an autoclosure that avoids evaluating the default value unless it's actually used, and which can throw. It's also much more idiomatic Swift in most cases. You can find the source on github.
public func ?? <T>(optional: T?, defaultValue: #autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
But I can imagine cases where you might a method-based solution (they're weird, but maybe there are such cases). You can get that by just rewriting it as a method:
extension Optional {
public func value(or defaultValue: #autoclosure () throws -> Wrapped) rethrows -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
}
tempInt.value(or: 2)
Optional is already a generic. It already takes any type as its parameterized type. Its parameterized already has a name: Wrapped. Just say Wrapped instead of T. Your T is Wrapped.
extension Optional {
func isNil<Wrapped>(value: Wrapped) -> Wrapped {
if self != nil {
return self as! Wrapped
}
return value
}
}
If you really prefer T for some reason, use a type alias. It's only a name:
extension Optional {
typealias T = Wrapped
func isNil<T>(value: T) -> T {
if self != nil {
return self as! T
}
return value
}
}
But in any case your extension is completely unnecessary because this is what the nil-coalescing operator ?? already does.
var tempInt: Int?
tempInt ?? 2 /// returns 2
tempInt = 5
tempInt ?? 2 /// returns 5

error: cannot invoke 'onNext' with an argument list of type '(String)' RxSwift

Why is the following RxSwift code not compiling and how do I solve the problem? This line observer.onNext("test123") is the problem.
final class TestA<String>: ObservableType {
typealias E = String
private let _observable: Observable<String>
init() {
_observable = Observable<String>.create { observer -> Disposable in
print("mark 1")
observer.onNext("test123")
observer.onCompleted()
return Disposables.create()
}
}
func subscribe<O>(_ observer: O) -> Disposable where O : ObserverType, O.E == E {
return _observable.subscribe(observer)
}
}
let observable = TestA<String>()
print("mark 2")
observable.subscribe(onNext: { element in
print(element)
})
I am testing in the playground and get the following error:
Playground execution failed:
error: Introduction.xcplaygroundpage:25:26: error: cannot invoke 'onNext' > with an argument list of type '(String)'
observer.onNext("test123")
^
Introduction.xcplaygroundpage:25:26: note: expected an argument list of > type '(String)'
observer.onNext("test123")
^
One of the reasons behind this setup with the class is that I want to pass in the dependencies with constructor injection and use them in the create closure in order to avoid having to capture self. I also want to avoid having all those Observable.creates in the wild and have a more OOP approach.
The swift compiler was not helpful with this error...
The problem here is that, when declaring TestA, you override the name String to represent the generic parameter for TestA. It is then an error to sent a Swift.String as a parameter to an observer expecting a TestA.String, which could be anything.
You can fix the issue with removing the unused generic parameter (final class TestA: ObservableType { ...), or taking the value sent to onNext as a parameter to the init, depending on the use case.
final class TestA<Element>: ObservableType {
typealias E = Element
private let _observable: Observable<Element>
init(_ value: Element) {
_observable = Observable<Element>.create { observer -> Disposable in
print("mark 1")
observer.onNext(value)
observer.onCompleted()
return Disposables.create()
}
}
func subscribe<O>(_ observer: O) -> Disposable where O : ObserverType, O.E == E {
return _observable.subscribe(observer)
}
}

RxSwift unwrap optional handy function?

Currently I have created a function unwrapOptional to safely unwrap the optional input in the stream.
func unwrapOptional<T>(x: Optional<T>) -> Observable<T> {
return x.map(Observable.just) ?? Observable.empty()
}
let aOpt: String? = "aOpt"
_ = Observable.of(aOpt).flatMap(unwrapOptional).subscribeNext { x in print(x)}
let aNil: String? = nil
_ = Observable.of(aNil).flatMap(unwrapOptional).subscribeNext { x in print(x)}
let a: String = "a"
_ = Observable.of(a).flatMap(unwrapOptional).subscribeNext { x in print(x)}
// output
aOpt
a
What I want to archive is to create a handy function instead of using flatMap(unwrapOptional), for example
Observable.of(a).unwrapOptional()
Something I tried to do, but it never compiles...
extension ObservableType {
func unwrapOptional<O : ObservableConvertibleType>() -> RxSwift.Observable<O.E> {
return self.flatMap(unwrapOptional)
}
}
You want the unwrapOptional method to only work on observables that have optional type.
So you somehow have to constraint the Element of Observable to conform to the Optional protocol.
extension Observable where Element: OptionalType {
/// Returns an Observable where the nil values from the original Observable are
/// skipped
func unwrappedOptional() -> Observable<Element.Wrapped> {
return self.filter { $0.asOptional != nil }.map { $0.asOptional! }
}
}
Unfortunately, Swift does not define such a protocol (OptionalType). So you also need to define it yourself
/// Represent an optional value
///
/// This is needed to restrict our Observable extension to Observable that generate
/// .Next events with Optional payload
protocol OptionalType {
associatedtype Wrapped
var asOptional: Wrapped? { get }
}
/// Implementation of the OptionalType protocol by the Optional type
extension Optional: OptionalType {
var asOptional: Wrapped? { return self }
}
checkout unwrap at https://github.com/RxSwiftCommunity/RxSwift-Ext :)
or https://github.com/RxSwiftCommunity/RxOptional
For now, you should use RxOptional for your personal needs
However, RxSwift-Ext will be growth exponentially in next 2-3 months :)
RxSwift now supports compactMap(). So, now you can do things like:
func unwrap(_ a: Observable<Int?>) -> Observable<Int> {
return a.compactMap { $0 }
}
Here's a version without needing OptionalType (from https://stackoverflow.com/a/36788483/13000)
extension Observable {
/// Returns an `Observable` where the nil values from the original `Observable` are skipped
func unwrap<T>() -> Observable<T> where Element == T? {
self
.filter { $0 != nil }
.map { $0! }
}
}

While optional, unwrap if contains something

The context : I have a generic stack of objects. The stack responds to pop() (returns an optional) and I have a function that needs to process the stack
If the stack is empty, throw an error
Otherwise, repeat repeat operation until next optional is not unwrappable
So far
guard var nextVar = myStack.pop() else {
throw MyError.EmptyStack
}
repeat {
// Process nextVar
} while nextVar = myStack.pop()
Problem : the first nextVar is NOT an Optional, therefore my while call fails. How can I rewrite so that the while checks whether the optional contains something AND if that succeeds, assign the content to the variable ? ()
You should modify your stack type to include an isEmpty property. Then you won't need to duplicate the pop call, and this will be a simple while-let loop.
guard !myStack.isEmpty else {
throw MyError.EmptyStack
}
while let nextVar = myStack.pop() {
// Process nextVar
}
You can also convert this into a SequenceType, but then iterating won't consume the stack. Then this would just be:
guard !myStack.isEmpty else {
throw MyError.EmptyStack
}
for nextVar in myStack {
// Process nextVar
}
Since pop is exactly the needed implementation of GeneratorType.next(), it should be easy to turn this into a GeneratorType and a SequenceType (by returning self in generate()).
But since Stack is a value type, iterating over it with for-in will make a copy and then consume that copy, rather than consuming the original stack. That could be good or bad.
Here's a sketch of what I mean:
struct Stack<Element> {
private var stack: [Element] = []
mutating func push(element: Element) {
stack.append(element)
}
mutating func pop() -> Element? {
guard let result = stack.last else { return nil }
stack.removeLast()
return result
}
var isEmpty: Bool { return stack.isEmpty }
}
extension Stack: GeneratorType {
mutating func next() -> Element? {
return pop()
}
}
extension Stack: SequenceType {
func generate() -> Stack {
return self
}
}