Reactivecocoa: рдЖрд░рдПрд╕реА рд╕рд┐рджреНрдзрд╛рдВрдд: рдХреЗрд╡рд▓ рдПрдХ рдмрд╛рд░ `рд╕рд┐рдЧреНрдирд▓рдкреНрд░реЛрдбреНрдпреВрд╕рд░` рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдХреИрд╕реЗ?

рдХреЛ рдирд┐рд░реНрдорд┐рдд 22 рдордИ 2016  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: ReactiveCocoa/ReactiveCocoa

рд╣рд╛рдп - рдпрд╣ рдЕрднреА рдПрдХ рджреЛрд╕реНрдд (cc @mgrebenets) рдХреЗ рд╕рд╛рде рдЪрд░реНрдЪрд╛ рдХреЗ рджреМрд░рд╛рди рд╕рд╛рдордиреЗ рдЖрдпрд╛

рдореЗрд░реЗ рдкрд╛рд╕ UserManager рдЬреЛ рдиреЗрдЯрд╡рд░реНрдХ рд╕реЗ User рдСрдмреНрдЬреЗрдХреНрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ UserManager рдкрд░ рдПрдХ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ:

func getUsers() -> SignalProducer<User, ErrorType>

(рдореИрдВ SignalProducer рд╡рд╛рдкрд╕ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдХреНрдпреЛрдВрдХрд┐ рдореИрдВ flatMap рдЗрд╕ рдиреЗрдЯрд╡рд░реНрдХ рдЕрдиреБрд░реЛрдз рдХреЛ рдЕрдиреНрдп рдиреЗрдЯрд╡рд░реНрдХ рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХреЗ рд▓рд┐рдП рдЕрдиреБрд░реЛрдз рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ)ред

рдкрдХрдбрд╝ рдпрд╣ рд╣реИ рдХрд┐ рдореИрдВ рдХреЗрд╡рд▓ start рдиреЗрдЯрд╡рд░реНрдХ рдЕрдиреБрд░реЛрдз рдХреЛ SignalProducer рдореЗрдВ рд▓рдкреЗрдЯрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдпрджрд┐ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдХреЛрдИ рдЕрдиреБрд░реЛрдз рдирд╣реАрдВ рд╣реИред

рдЗрд╕реЗ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдореЗрд░рд╛ рдЕрдВрддрд░реНрдЬреНрдЮрд╛рди рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдерд╛:

class UserManager {

    static let sharedManager = UserManager()

    private var requestSignal: Signal<User, NSError>? = nil

    func getUsers() -> SignalProducer<User, NSError> {
        if let requestSignal = requestSignal {
            print("There is already a request in progress so simply return a reference to its signal")
            return SignalProducer(signal: requestSignal)
        } else {
            let requestSignalProducer = SignalProducer<User, NSError> { observer, disposable in
                let url = NSURL(string:"http://jsonplaceholder.typicode.com/users/1")!
                let task = NSURLSession.sharedSession()
                    .dataTaskWithURL(url) { (data, response, error) in
                    if error != nil {
                        observer.sendFailed(NSError(domain:"", code:5, userInfo:nil))
                    } else {
                        let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
                        let user = User(JSON: json)
                        print("Completed user request at \(NSDate())")
                        observer.sendNext(user)
                        observer.sendCompleted()
                    }
                }
                print("Started user request at \(NSDate())")
                task.resume()
            }
            requestSignalProducer.startWithSignal{ [weak self] (signal, disposable) in
                guard let `self` = self else { return }
                `self`.requestSignal = signal
                signal.observeCompleted {
                    print("Completing the user request signal")
                    `self`.requestSignal = nil
                }
            }
            return SignalProducer(signal: self.requestSignal!)
        }
    }
}

requestSignal рдореЗрдВ рдкрд╛рд░рд╕реНрдкрд░рд┐рдХ рд╕реНрдерд┐рддрд┐ рдХреЛ рдмреБрдпреБрдЯ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ "рдЕрди-рдЖрд░рдПрд╕реА" рдорд╣рд╕реВрд╕ рдХрд░рддрд╛ рд╣реИред рдХрд┐рд╕реА рднреА рд╕рдВрдХреЗрдд рдХреА рд╕рд░рд╛рд╣рдирд╛ рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдЖрдк рдПрдХ рд╕рдорд╛рдзрд╛рди рдкрд░ рджреЗ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдЖрд░рдПрд╕реА рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХреЗ рдЕрдиреБрд░реВрдк рд╣реИред

рдЪрд┐рдпрд░реНрд╕! ЁЯША

рд╕рднреА 5 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

replayLazily рдХрд░рдирд╛ рдЪреАрдЬреЛрдВ рдХреЛ рдереЛрдбрд╝рд╛ рд╕рд░рд▓ рдХрд░рддрд╛ рд╣реИ - рдлрд┐рд░ рдЖрдк рд╕рд┐рдЧреНрдирд▓ рдХреЛ рд░реАрдбрд╛рдпрд░реЗрдХреНрдЯ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп SignalProducer рд╕реАрдзреЗ рдмрдЪрд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣рд╛рдВ рдПрдХ рджреМрдбрд╝ рдХреА рд╕реНрдерд┐рддрд┐ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдк RAC рдХреЗ Atomic рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣ рд╕рдХрддреЗ рд╣реИрдВред (рдпрджрд┐ 2 рдзрд╛рдЧреЗ рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ getUsers рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк 2 рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд╕рдорд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред)

рдореИрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдПрдкреАрдЖрдИ рдХреЛрдб рдХреЛ рдПрдХ рдЕрд▓рдЧ рд╡рд┐рдзрд┐ рдпрд╛ рд╡рд░реНрдЧ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рдиреЗ рдХреА рднреА рд╕рд▓рд╛рд╣ рджреВрдВрдЧрд╛ред рдЗрд╕рд╕реЗ рдХреЛрдб рдХреЛ рдкрдврд╝рдиреЗ рдореЗрдВ рдереЛрдбрд╝реА рдЖрд╕рд╛рдиреА рд╣реЛрдЧреА рдФрд░ рдЪрд┐рдВрддрд╛рдУрдВ рдХреЛ рдереЛрдбрд╝рд╛ рдмреЗрд╣рддрд░ рдврдВрдЧ рд╕реЗ рдЕрд▓рдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХреЗрдЧрд╛ред

struct API {
    static func getUsers() -> SignalProducer<User, NSError> {
            return SignalProducer<User, NSError> { observer, disposable in
                let url = NSURL(string:"http://jsonplaceholder.typicode.com/users/1")!
                let task = NSURLSession.sharedSession()
                    .dataTaskWithURL(url) { (data, response, error) in
                    if error != nil {
                        observer.sendFailed(NSError(domain:"", code:5, userInfo:nil))
                    } else {
                        let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
                        let user = User(JSON: json)
                        print("Completed user request at \(NSDate())")
                        observer.sendNext(user)
                        observer.sendCompleted()
                    }
                }
                print("Started user request at \(NSDate())")
                task.resume()
         }
    }
}

class UserManager {
    static let sharedManager = UserManager()

    private var requestProducer: Atomic<SignalProducer<User, NSError>?> = Atomic(nil)

    func getUsers() -> SignalProducer<User, NSError> {
        return requestProducer.modify { producer in
            if let producer = producer {
                print("There is already a request in progress so simply return a reference to its signal")
                return producer
            }

            return API.getUsers()
                .on(completed: {
                    self.requestProcuder.modifify { _ in nil }
                })
               .replayLazily()
        }
    }
}

тЪая╕П рдореИрдВрдиреЗ рдЗрд╕реЗ рд╕реАрдзреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдЯрд╛рдЗрдк рдХрд┐рдпрд╛ рдФрд░ рдпрд╣ рдЕрдкреНрд░рдпреБрдХреНрдд рд╣реИред тЪая╕П

рдЕрднреА рднреА рд░рд╛рдЬреНрдп рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдереЛрдбрд╝рд╛ рдмреЗрд╣рддрд░ рд╣реИред

RAC-iest рддрд░реАрдХрд╛ рдпрд╣ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдирдпрд╛ рдСрдкрд░реЗрдЯрд░ рдЬреЛрдбрд╝рдирд╛ рд╣реЛрдЧрд╛ рдЬрд┐рд╕рдореЗрдВ рд░рд╛рдЬреНрдп рд╢рд╛рдорд┐рд▓ рд╣реИред

extension SignalProducer {
    func replayIfStarted() -> SignalProducer<Value, Error> {
        let active: Atomic<SignalProducer<Value, Error>?> = nil
        return active.modify { producer in
            if let producer = producer {
                return producer
            }

            return self
                .on(completed: {
                    active.modify { _ in nil }
                })
                .replayLazily()
        }
    }
}

рдореБрдЭреЗ рдпрдХреАрди рд╣реИ рдХрд┐ рдЖрд░рдПрд╕реА рдХреЛрдбрдмреЗрд╕ рдХреЗ рдЦрд┐рд▓рд╛рдл рджреЛрд╣рд░реА рдЬрд╛рдВрдЪ рд╣реЛрдЧреА, рдФрд░ рдирд┐рд░реНрдорд╛рддрд╛ рдЬреАрд╡рди рднрд░ рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рднреА рд▓рд┐рдЦреЗрдВрдЧреЗ, рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдореИрдВрдиреЗ рдЗрд╕реЗ рдареАрдХ рд╕реЗ рд▓рд┐рдЦрд╛ рд╣реИред тШ║я╕П

рдЕрдЪреНрдЫрд╛ рд▓рдЧрд╛! рдФрд░ рдореЗрд░реА рдУрд░ рд╕реЗ рдХреЛрдб-рдмрд╛рд░рдл рдХреЗ рд▓рд┐рдП рдорд╛рдлреА, рдореБрдЭреЗ рдЙрд╕ "рдкрд╣рд▓реЗ рд╕реЗ рдХреЛрдб рдирд╣реАрдВ рд╣реИ" рд╕рдВрджреЗрд╢ рдХреЗ рд╕рд╛рде рдмреНрд▓реЙрдХ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП

рдЬрдм рдореИрдВ Xcode рдХреЗ рд╕рд╛рдордиреЗ рд╣реВрдБ, рддреЛ рдореИрдВ рдЗрд╕реЗ рджреЗрдЦ рд▓реВрдВрдЧрд╛; рдЖрдкрдХреЗ рд╕рднреА рдЕрдВрддрд░реНрджреГрд╖реНрдЯрд┐ рдХреА рд╕рд░рд╛рд╣рдирд╛ рдХрд░рддреЗ рд╣реИрдВред

рдореИрдВ рдЗрд╕реЗ рдмрдВрдж рдХрд░рдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдВред рдЕрдЧрд░ рдЖрдкрдХреЛ рдЗрд╕реЗ рджреЗрдЦрдиреЗ рдХреЗ рдмрд╛рдж рдХреЛрдИ рд╕рд╡рд╛рд▓ рд╣реИ рддреЛ рдлрд┐рд░ рд╕реЗ рдЖрдЬрд╝рд╛рдж рд╣реЛрдВ!

ЁЯСН рдзрдиреНрдпрд╡рд╛рдж рдореИрдЯ

рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдиреЗ рдХрд╛ рдПрдХ рдФрд░ рддрд░реАрдХрд╛ рд╣реИ: Property !

final class UserManager {
    /// The public API is as easy as observing this.
    public let users: AnyProperty<[User]>
    private let usersMutableProperty = MutableProperty<[User]>([])

    init() {
        self.users = AnyProperty(self.usersMutableProperty)  
    }

    private func getUsers() -> SignalProducer<User, NSError> { }

    public func requestUsers() {
        /// This is not idea because multiple calls to this method would step onto one-another, just doing it like this for simplicity
        self.usersMutableProperty <~ self.usersRequest()
    }
}
рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

danishin picture danishin  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

dougmcbride picture dougmcbride  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

v-silin picture v-silin  ┬╖  4рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

iby picture iby  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

samidalouche picture samidalouche  ┬╖  6рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ