{"action":"created","issue":{"url":"https://api.github.com/repos/Moya/Moya/issues/118","labels_url":"https://api.github.com/repos/Moya/Moya/issues/118/labels{/name}","comments_url":"https://api.github.com/repos/Moya/Moya/issues/118/comments","events_url":"https://api.github.com/repos/Moya/Moya/issues/118/events","html_url":"https://github.com/Moya/Moya/issues/118","id":76408319,"number":118,"title":"RAC 3.0 support","user":{"login":"MichaelMcGuire","id":429790,"avatar_url":"https://avatars.githubusercontent.com/u/429790?v=3","gravatar_id":"","url":"https://api.github.com/users/MichaelMcGuire","html_url":"https://github.com/MichaelMcGuire","followers_url":"https://api.github.com/users/MichaelMcGuire/followers","following_url":"https://api.github.com/users/MichaelMcGuire/following{/other_user}","gists_url":"https://api.github.com/users/MichaelMcGuire/gists{/gist_id}","starred_url":"https://api.github.com/users/MichaelMcGuire/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MichaelMcGuire/subscriptions","organizations_url":"https://api.github.com/users/MichaelMcGuire/orgs","repos_url":"https://api.github.com/users/MichaelMcGuire/repos","events_url":"https://api.github.com/users/MichaelMcGuire/events{/privacy}","received_events_url":"https://api.github.com/users/MichaelMcGuire/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Moya/Moya/labels/enhancement","name":"enhancement","color":"84b6eb"},{"url":"https://api.github.com/repos/Moya/Moya/labels/short%20term","name":"short term","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/Moya/Moya/milestones/2","html_url":"https://github.com/Moya/Moya/milestones/Long%20Term","labels_url":"https://api.github.com/repos/Moya/Moya/milestones/2/labels","id":873021,"number":2,"title":"Long Term","description":"","creator":{"login":"ashfurrow","id":498212,"avatar_url":"https://avatars.githubusercontent.com/u/498212?v=3","gravatar_id":"","url":"https://api.github.com/users/ashfurrow","html_url":"https://github.com/ashfurrow","followers_url":"https://api.github.com/users/ashfurrow/followers","following_url":"https://api.github.com/users/ashfurrow/following{/other_user}","gists_url":"https://api.github.com/users/ashfurrow/gists{/gist_id}","starred_url":"https://api.github.com/users/ashfurrow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ashfurrow/subscriptions","organizations_url":"https://api.github.com/users/ashfurrow/orgs","repos_url":"https://api.github.com/users/ashfurrow/repos","events_url":"https://api.github.com/users/ashfurrow/events{/privacy}","received_events_url":"https://api.github.com/users/ashfurrow/received_events","type":"User","site_admin":false},"open_issues":6,"closed_issues":1,"state":"open","created_at":"2014-11-19T16:42:58Z","updated_at":"2015-08-26T15:05:24Z","due_on":null,"closed_at":null},"comments":11,"created_at":"2015-05-14T16:12:23Z","updated_at":"2015-09-13T01:24:10Z","closed_at":null,"body":"Were you planning on incorporating ReactiveCocoa extensions based on the 3.0 API?  I'm about to create my own extensions and wouldn't mind sending them along if you were interested.\r\n\r\nThanks for the awesome library!"},"comment":{"url":"https://api.github.com/repos/Moya/Moya/issues/comments/139837170","html_url":"https://github.com/Moya/Moya/issues/118#issuecomment-139837170","issue_url":"https://api.github.com/repos/Moya/Moya/issues/118","id":139837170,"user":{"login":"ashfurrow","id":498212,"avatar_url":"https://avatars.githubusercontent.com/u/498212?v=3","gravatar_id":"","url":"https://api.github.com/users/ashfurrow","html_url":"https://github.com/ashfurrow","followers_url":"https://api.github.com/users/ashfurrow/followers","following_url":"https://api.github.com/users/ashfurrow/following{/other_user}","gists_url":"https://api.github.com/users/ashfurrow/gists{/gist_id}","starred_url":"https://api.github.com/users/ashfurrow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ashfurrow/subscriptions","organizations_url":"https://api.github.com/users/ashfurrow/orgs","repos_url":"https://api.github.com/users/ashfurrow/repos","events_url":"https://api.github.com/users/ashfurrow/events{/privacy}","received_events_url":"https://api.github.com/users/ashfurrow/received_events","type":"User","site_admin":false},"created_at":"2015-09-13T01:24:10Z","updated_at":"2015-09-13T01:24:10Z","body":"@justinmakaila has implemented a solution on another repository (pasted below). We can't incorporate this change into the library yet because it's a breaking change to existing users, and we need to update Moya to the next major version (3.0.0). I'd like to get a Swift 2, Carthage-compatible release out _first_ (see #215) and then incorporate Justin's work, since it looks great :100: \r\n\r\n```swift\r\n/// Subclass of MoyaProvider that returns SignalProducer<MoyaResponse, NSError> instances when requests are made. Much better than using completion closures.\r\npublic class ReactiveCocoaMoyaProvider<T where T: MoyaTarget>: MoyaProvider<T> {\r\n    /// Current requests that have not completed or errored yet.\r\n    /// Note: Do not access this directly. It is public only for unit-testing purposes (sigh).\r\n    public var inflightRequests = Dictionary<Endpoint<T>, Signal<MoyaResponse, NSError>>()\r\n    \r\n    /// Initializes a reactive provider.\r\n    override public init(endpointClosure: MoyaEndpointsClosure = MoyaProvider.DefaultEndpointMapping, endpointResolver: MoyaEndpointResolution = MoyaProvider.DefaultEnpointResolution, stubBehavior: MoyaStubbedBehavior = MoyaProvider.NoStubbingBehavior, networkActivityClosure: Moya.NetworkActivityClosure? = nil) {\r\n        super.init(endpointClosure: endpointClosure, endpointResolver: endpointResolver, stubBehavior: stubBehavior, networkActivityClosure: networkActivityClosure)\r\n    }\r\n    \r\n    public func request(token: T) -> SignalProducer<MoyaResponse, NSError> {\r\n        let endpoint = self.endpoint(token)\r\n        \r\n        if let existingSignal = inflightRequests[endpoint] {\r\n            /// returns a new producer which forwards all events of the already existing request signal\r\n            return SignalProducer { sink, disposable in\r\n                /// connect all events of the existing signal to the observer of this signal producer\r\n                existingSignal.observe(sink)\r\n            }\r\n        }\r\n        else {\r\n            /// returns a new producer which starts a new producer which invokes the requests. The created signal of the inner producer is saved for inflight request\r\n            return SignalProducer { [weak self] sink, _ in\r\n                let producer: SignalProducer<MoyaResponse, NSError> = SignalProducer { [weak self] sink, disposable in\r\n                    let cancellableToken = self?.request(token) { data, statusCode, response, error in\r\n                        if let error = error {\r\n                            if let statusCode = statusCode {\r\n                                sendError(sink, NSError(domain: error.domain, code: statusCode, userInfo: error.userInfo))\r\n                            } else {\r\n                                sendError(sink, error)\r\n                            }\r\n                        } else {\r\n                            if let data = data {\r\n                                sendNext(sink, MoyaResponse(statusCode: statusCode!, data: data, response: response))\r\n                            }\r\n                        }\r\n                        sendCompleted(sink)\r\n                    }\r\n                    \r\n                    disposable.addDisposable {\r\n                        if let weakSelf = self {\r\n                            objc_sync_enter(weakSelf)\r\n                            // Clear the inflight request\r\n                            weakSelf.inflightRequests[endpoint] = nil\r\n                            objc_sync_exit(weakSelf)\r\n                            // Cancel the request\r\n                            cancellableToken?.cancel()\r\n                        }\r\n                    }\r\n                }\r\n                \r\n                /// starts the inner signal producer and store the created signal.\r\n                producer |> startWithSignal { [weak self] signal, _ in\r\n                    objc_sync_enter(self)\r\n                    self?.inflightRequests[endpoint] = signal\r\n                    objc_sync_exit(self)\r\n                    /// connect all events of the signal to the observer of this signal producer\r\n                    signal.observe(sink)\r\n                }\r\n            }\r\n        }\r\n    }\r\n    \r\n    public func request(token: T) -> RACSignal {\r\n        return toRACSignal(self.request(token))\r\n    }\r\n}\r\n\r\n/// Extension for mapping to a certain response type\r\npublic extension ReactiveCocoaMoyaProvider {\r\n    public func requestJSON(token: T) -> SignalProducer<AnyObject, NSError> {\r\n        return request(token) |> mapJSON()\r\n    }\r\n    \r\n    public func requestJSONArray(token: T) -> SignalProducer<NSArray, NSError> {\r\n        return requestJSON(token) |> mapJSONArray()\r\n    }\r\n    \r\n    public func requestJSONDictionary(token: T) -> SignalProducer<NSDictionary, NSError> {\r\n        return requestJSON(token) |> mapJSONDictionary()\r\n    }\r\n    \r\n    public func requestImage(token: T) -> SignalProducer<UIImage, NSError> {\r\n        return request(token) |> mapImage()\r\n    }\r\n    \r\n    public func requestString(token: T) -> SignalProducer<String, NSError> {\r\n        return request(token) |> mapString()\r\n    }\r\n}\r\n\r\n/// MoyaResponse free functions\r\n\r\npublic func filterStatusCode(range: ClosedInterval<Int>) -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<MoyaResponse, NSError>  {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { response in\r\n            if range.contains(response.statusCode) {\r\n                return SignalProducer(value: response)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.StatusCode(response).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n\r\npublic func filterStatusCode(code: Int) -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<MoyaResponse, NSError> {\r\n    return filterStatusCode(code...code)\r\n}\r\n\r\npublic func filterSuccessfulStatusCodes() -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<MoyaResponse, NSError> {\r\n    return filterStatusCode(200...299)\r\n}\r\n\r\npublic func filterSuccessfulAndRedirectCodes() -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<MoyaResponse, NSError> {\r\n    return filterStatusCode(200...399)\r\n}\r\n\r\n/// Maps the `MoyaResponse` to a `UIImage`\r\npublic func mapImage() -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<UIImage, NSError> {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { response in\r\n            if let image = UIImage(data: response.data) {\r\n                return SignalProducer(value: image)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.ImageMapping(response).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n\r\n/// Maps the `MoyaResponse` to JSON\r\npublic func mapJSON() -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<AnyObject, NSError> {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { response in\r\n            var error: NSError?\r\n            if let json: AnyObject = NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments, error: &error) {\r\n                return SignalProducer(value: json)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.JSONMapping(response).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n\r\n/// Maps a JSON object to an NSArray\r\npublic func mapJSONArray() -> SignalProducer<AnyObject, NSError> -> SignalProducer<NSArray, NSError> {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { json in\r\n            if let json = json as? NSArray {\r\n                return SignalProducer(value: json)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.JSONMapping(json).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n\r\n/// Maps a JSON object to an NSDictionary\r\npublic func mapJSONDictionary() -> SignalProducer<AnyObject, NSError> -> SignalProducer<NSDictionary, NSError> {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { json in\r\n            if let json = json as? NSDictionary {\r\n                return SignalProducer(value: json)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.JSONMapping(json).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n\r\n/// Maps the `MoyaResponse` to a String\r\npublic func mapString() -> SignalProducer<MoyaResponse, NSError> -> SignalProducer<String, NSError> {\r\n    return { producer in\r\n        return producer |> flatMap(.Latest, { response in\r\n            if let string =  NSString(data: response.data, encoding: NSUTF8StringEncoding) as? String {\r\n                return SignalProducer(value: string)\r\n            } else {\r\n                return SignalProducer(error: ReactiveMoyaError.StringMapping(response).toError())\r\n            }\r\n        })\r\n    }\r\n}\r\n```"}}