iosswiftreactive-cocoareactive-swift

How create date schedular?


I am trying to create a date scheduler to observe some event. But it does not work. I've looked through

protocol DateScheduler

and it is said that action will take place at currentDate in some methods protocol DateScheduler. I am trying doing this after 10 sec. Below is an example of my custom schedular.

class SomeDateScheduler : DateScheduler {
var currentDate: Date
init() {
    self.currentDate = Date(timeIntervalSinceNow: 10)
}

func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? {
    print(#function)
    print(date)
    return nil
}

func schedule(after date: Date, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, action: @escaping () -> Void) -> Disposable? {
    print(#function)
    print(date)
    print(interval)
    print(leeway)
    return nil
}

func schedule(_ action: @escaping () -> Void) -> Disposable? {
    print(#function)
    return nil
}

}

and then I create bind to observe event

private func testSchedular() {
    let schedular = SomeDateScheduler()

    reactive.makeBindingTarget { appDeleg, value in
        print("SUCCESS")
        print(value)
        } <~ signalSchedular.observe(on: schedular)

    DispatchQueue.main.async { [observerSchedular] in
        observerSchedular.send(value: "Hello World")
        observerSchedular.sendCompleted()
    }
}

I am doing it into AppDelegate. ObserverSchedular and signalSchedular are global properties. Please, explain to me how to invoke all methods DateScheduler.


Solution

  • I have already understood where I made mistake. To fix it I done next:

    1. Created custom observe(on: Schedular). So it looked like this (add these function in extension to Signal):
    func customObserve(on scheduler: DateScheduler, interval: Int, leeway: Int) -> Signal<Value, Error> {
        return customFlatMapEvent(Signal.Event.observe(on: scheduler, interval: DispatchTimeInterval.seconds(interval), leeway: DispatchTimeInterval.seconds(leeway)))
    }
    
    
    func customFlatMapEvent<U, E>(_ transform: @escaping Event.CustomTransformation<U, E>) -> Signal<U, E> {
        return Signal<U, E> { output, lifetime in
            let input = transform(output.send, lifetime)
            lifetime += self.observe(input)
        }
    }
    

    also add this

    extension Signal.Event {
        typealias CustomTransformation<U, E: Swift.Error> = (@escaping Signal<U, E>.Observer.Action, Lifetime) -> Signal<Value, Error>.Observer.Action
        
        static func observe(on scheduler: DateScheduler, interval: DispatchTimeInterval, leeway: DispatchTimeInterval) -> CustomTransformation<Value, Error> {
            return { action, lifetime in
                lifetime.observeEnded {
                    scheduler.schedule {
                        action(.interrupted)
                    }
                }
    
                return { event in
                    scheduler.schedule(after: scheduler.currentDate, interval: interval, leeway: leeway) {
                        if !lifetime.hasEnded {
                            action(event)
                        }
                    }
                }
            }
        }
    }
    
    1. Created bind for observation in didFinishLaunchingWithOptions

      reactive.makeBindingTarget { appDeleg, value in
          print("SUCCESS")
          print(value)
          } <~ signalSchedular.customObserve(on: schedular, interval: 10, leeway: 1)
      
    2. And simulate async code

    
    DispatchQueue.main.async { [observerSchedular] in
           observerSchedular.send(value: "Hello World")
           observerSchedular.sendCompleted()
    }
    
    
    1. Defined global properties
    private let (signalSchedular, observerSchedular) = Signal<String, Never>.pipe(),
    let schedular = SomeDateScheduler()
    
    1. Changed custom date schedular
    class SomeDateScheduler : DateScheduler {
        var currentDate: Date
        init() {
            self.currentDate = Date()
        }
        
        func schedule(after date: Date, action: @escaping () -> Void) -> Disposable? {
            action()
            return nil
        }
        
        func schedule(after date: Date, interval: DispatchTimeInterval, leeway: DispatchTimeInterval, action: @escaping () -> Void) -> Disposable? {
            if case .seconds(let sec) = interval {
                let time = DispatchTime.now() + DispatchTimeInterval.seconds(sec)
                let queue = DispatchQueue(label: "sads", qos: .utility, attributes: .concurrent)
                queue.asyncAfter(deadline: time) { [weak self] in
                    _ = self?.schedule(after: date) {
                        action()
                    }
                }
    
    
                DispatchQueue.main.asyncAfter(deadline: time) { [weak self] in
                    _ = self?.schedule(after: date) {
                        action()
                    }
                }
            }
            return nil
        }
        
        func schedule(_ action: @escaping () -> Void) -> Disposable? {
            action()
            return nil
        }
    }