RxSwift入门教程(二)

*Creating an Observable that performs work
Ok,now something more interesting.Let’s create that interval operator that was used in previous examples.This is equivalent of actual implementation for dispatch queue schedulers
func myInterval(_ interval: TimeInterval) -> Observable {

   return Observable.create { observer in
        print("Subscribed")
        let timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
        timer.scheduleRepeating(deadline: DispatchTime.now() + interval, interval: interval)

        let cancel = Disposables.create {
            print("Disposed")
            timer.cancel()

}
var next = 0
timer.setEventHandler {
if cancel.isDisposed {
return
}
observer.on(.next(next))
next += 1
}
timer.resume()
return cancel
}
}
let counter = myInterval(0.1)
print(“Started ——–”)
let subscription = counter
.subscribe(onNext: { n in
print(n)
})
Thread.sleep(forTimeInterval: 0.5)
subscription.dispose()
print(“Ended—–”)
This will print
started ——–
subscribed
0
1
2
3
4
Disposed
Ended——
What if you would write
let counter = myInterval(0.1)))
print(“Started——”)
let subscription1 = counter
.subscribe(onNext: { n in
print(“First (n)”)
})
let subscription2 = counter
.subscribe(onNext: { n in
print(“Second (n)”)
})
Thread.sleep(forTimeInterval: 0.5)
subscription1.dispose())
Thread.sleep(forTimeInterval: 0.5)
subscirption2.dispose()
print(“Ended——”)

Every subscriber upon subscription usually generates it’s own separate sequence of elements.Operators are stateless by default.There are vastly more stateless operators than stateful one .
Sharing subscription and shareReplay operator
But what if you want that multiple observers share events(elements)from only one subscription?
There are two things that need to be defined.
How to handle past elements that have been received before the new subscriber was interested in observing them (replay latest only)

let counter = myInterval(0.1)
.shareReplay(1)
print(“Started ——”)

let subscription1 = counter
.subscribe(onNext: { n in
print(“First (n)”)
})

let subscription2 = counter
.subscribe(onNext: { n in
print(“Second (n)”)
})

Thread.sleep(forTimeInterval: 0.5)
subscription1.dispose()
Thread.sleep(forTimeInterval: 0.5)
subscirption2.dispose()
print(“Ended ——-“)

Notice how now there is only one Subscribed and Disposed event.
Behavior for URL observables is equivalent.
This is how HTTP requests are wrapped in Rx.It’s pretty much the same pattern like the interval operator.
extension Reactive where Base: URLSession {

public func response(_ request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {

    return Observable.create { observer in

           let task = self.dataTaskWithRequest(request) {(data, response, error) in

                  guard let response = response, let data = data else {

                        observer.on(.error(error ?? RxCocoaURLError.Unknown))
                        return
                  }
                  guard let httpResponse = reponse as ? HTTPURLResponse else {

                          observer.on(.error(RxCocoaURLError.nonHttpResponse(response: response)))
                          return
                  }
                  observer.on(.next(data, httpResponse))
                  observer.on(.completed)
           }
           task.resume()
           return Disposables.create {

                task.cancel()
           }

    }
}

}

Operators
There are numerous operators implemented in RxSwift.Marble diagrams for all operators can be found on ReactiveX.io.
Almost all operators are demonstrated in playgrounds.To use playgrounds please open Rx.xcworkspace,build RxSwift-macOs scheme and then open playgrounds in Rx.xcworkspace tree view.In case you need an operator, and don’t know how to find it there a dicision tree of operators.
Custom operators(There are two ways how you can create custom operators.)
Easy way
All of the interval code uses hightly optimized versions of operators,so they aren’t the best tutorial material.That’s
why it’s hightly encouraged to use standard operators.
Fortunately there is an easier way to create operators.Creating new operators is actually all about creating observables,
and previous chapter already descirbes how to do that.
lets see how an unoptimized map operator can be implemented.

extension ObservableType {

func myMap<R>(transform: @escaping(e) -> R) -> Observable<R> {

     return Observable.create { observer in
           let subscription = self.subscribe { e in

                switch e {

                case .next(let value):
                   let result = transform(value)
                   observer.on(.next(result))
                case .error(let error):
                    observer.on(.error(error))
                case .completed:
                    observer.on(.completed)
                }
           }
      return subscription
     }
}

}

So now you can use your own map:
let subscription = myInterval(0.1)
.myMap { e in
return “This is simply (e)”
}
.subscribe(onNext: { n in
print(n)
})
Life happens(So what if it’s just too hard to solve some cases with custom operators? You can exit the Rx monad,perform actions in
imperative world,and then tunnel results to Rx again using Subjects.This isn’t something that should be practiced often ,and is a bad code smell,but you can do it.)
let magicBegins: Observable = summonFromMiddleEarth()
magicBegins
.subscribe(onNext: { being in
self.doSomeStateMagic(beging)
})
.disposed(by: disposeBag)

//Mess
let kitten = globalParty(
 being,
 UIApplication.delegate.dataSomething.attendees
)
kittens.on(.next(kitten))//send result back to rx

//another mess
let kittens = Variable(firstKitten)//agagin back in Rx monad
kittens.asObservable()
   .map { kitten in
      return kitten.purr()
   }

Every time you do this ,somebody will probably write this code somewhere:
kittens
.subscribe(onNext: { kitten in
//do something with kitten
})
.disposed(by: disposeBag)
So please try not to do this

Error handling(There are two error mechanisms.)
Asynchronous error handling mechanism in observables(Error handling is pretty straightforward.If one sequence
terminates with error,then all of the dependent sequences will terminate with error.It’s usual short circuit logic.You can recover from failure of observable by using catch operator.There are various overloads that enable you to specify recovery in great detail.
There is also retry operator that enables retries in case of errored sequence.)
Debugging compile Errors
When writing elegant RxSwift/RxCocoa code,you are probably relying heavily on compiler to deduce types of Observables.This is
one of the reasons why swift is awesome,but it can also be frustrating sometimes.
images = word
.filter { $0.containsString(“important”)}
.flatMap{ word in
return self.api.loadFlickrFeed(“Karate”)
.catchError { error in
return just(JSON(1))
}
}
If compiler reports that there is an error somewhere in this expression,I would suggest first annotating return types.
images = word
.filter { s -> Bool in s.containsString(“important”)}
.flatMap{ word -> Observable in
return self.api.loadFlickrFeed(“karate”)
.catchError {error -> Observable in
return just(JSON(1))
}
}

If that doesn’t work,you can continue adding more type annotations until you’ve localized the error.
images = word
.filter { (s: String) -> Bool in s.containsString(“important”)}
.flatMap{ (word: String) -> Observable in
return self.api.loadFlickrFeed(“karate”)
.catchError{ (error: Error) -> Observable in
return just(JSON(1))
}
}

I would suggest first annotating return types and arguments of closure.(usually after you have fixed the error,you can remove the type annotations to clean up your code again.)
Debugging(Using debugger alone is useful,but usually using debugf operator will be more efficient.debug operator will print out all events to standard output and you can add also label those events.debug acts like a probe.Here is an example of using it:)
let subscription = myInterval(0.1)
.debug(“my probe”)
.map{ e in
return “This is simply (e)”
}
.subscribe(onNext: { n in
print(n)
})
Thread.sleepForTimeInterval(0.5)
subscription.dispose()

You can also easily create your version of the debug operator.
extension ObservableType {

public func myDebug(identifier: String) -> Observable<Self.E> {

     return Observable.create { observer in 

         print("subscribed \(identifier)")
         let subscription = self.subscribe { e in
             print("event \(identifier)   \(e)")
             switch e {

             case .next(let value):
                 observer.on(.next(value))
              case .error(let error):
                 observer.on(.error(error))
              case .completed:
                 observer.on(.completed)
             }
         }
         return Disposables.create {

             print("disposing \(identifier)")
             subscription.dispose()
         }
     }
}

}

/*add somewhere in
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey : Any]? = nil)
*/

_ = Observable.interval(1, scheduler: MainScheduler.instance)
.subscribe(onNext: { _ in
print(“Resource count (RxSwift.Resources.total)”)
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值