{"action":"created","issue":{"url":"https://api.github.com/repos/Moya/Moya/issues/348","labels_url":"https://api.github.com/repos/Moya/Moya/issues/348/labels{/name}","comments_url":"https://api.github.com/repos/Moya/Moya/issues/348/comments","events_url":"https://api.github.com/repos/Moya/Moya/issues/348/events","html_url":"https://github.com/Moya/Moya/issues/348","id":122399902,"number":348,"title":"Approach to TargetType","user":{"login":"mpurland","id":113033,"avatar_url":"https://avatars.githubusercontent.com/u/113033?v=3","gravatar_id":"","url":"https://api.github.com/users/mpurland","html_url":"https://github.com/mpurland","followers_url":"https://api.github.com/users/mpurland/followers","following_url":"https://api.github.com/users/mpurland/following{/other_user}","gists_url":"https://api.github.com/users/mpurland/gists{/gist_id}","starred_url":"https://api.github.com/users/mpurland/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mpurland/subscriptions","organizations_url":"https://api.github.com/users/mpurland/orgs","repos_url":"https://api.github.com/users/mpurland/repos","events_url":"https://api.github.com/users/mpurland/events{/privacy}","received_events_url":"https://api.github.com/users/mpurland/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Moya/Moya/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-12-16T00:30:25Z","updated_at":"2015-12-28T16:09:54Z","closed_at":null,"body":"I just wanted to start a discussion around patterns on the `TargetType`. The existing solution with Swift `enum` seems to enforce the implementing client code to have huge switch statements based on the endpoint. I don't think this scales well for many endpoints and doesn't really play well with request or response parsing. I have adopted a struct based solution where each target is a struct with parameters to each passed into the init.\r\n\r\nThis is related to #347.\r\n\r\nExample:\r\n```swift\r\nfunc moyaErrorToMyAPIError(moyaError: ReactiveMoya.Error) -> MyAPIError {\r\n    return .MoyaError(moyaError)\r\n}\r\n\r\nprotocol MyTargetMarker {}\r\n\r\nstruct MyTarget: TargetType, MyTargetMarker {\r\n    let targetType: MyTargetType\r\n    \r\n    init(_ targetType: MyTargetType) {\r\n        self.targetType = targetType\r\n    }\r\n    \r\n    var baseURL: NSURL {\r\n        return targetType.baseURL\r\n    }\r\n    \r\n    var path: String {\r\n        return targetType.path\r\n    }\r\n    \r\n    var method: ReactiveMoya.Method {\r\n        return targetType.method\r\n    }\r\n    \r\n    var parameters: [String: AnyObject]? {\r\n        return targetType.parameters\r\n    }\r\n    \r\n    var sampleData: NSData {\r\n        return targetType.sampleData\r\n    }\r\n    \r\n    var parameterEncoding: ReactiveMoya.ParameterEncoding {\r\n        return targetType.parameterEncoding\r\n    }\r\n}\r\n\r\nprotocol MyTargetType: TargetType {}\r\n\r\nextension MyTargetType {\r\n    var target: MyTarget {\r\n        return MyTarget(self)\r\n    }\r\n}\r\n\r\nenum MyAPIError {\r\n    case Custom(ErrorType)\r\n    case MoyaError(ReactiveMoya.Error)\r\n}\r\n\r\nprotocol ProducerTargetType: MyTargetType {\r\n    typealias ResponseType = Response\r\n\r\n    func producer(provider: ReactiveCocoaMoyaProvider) -> SignalProducer<ResponseType, ErrorType>\r\n}\r\n\r\nclass MyAPIProvider: ReactiveCocoaMoyaProvider<MyTarget> {\r\n    func request<T: ProducerTargetType>(token: T) -> SignalProducer<T.ResponseType, MyAPIError> {\r\n        return super.request(token.target).mapError(moyaErrorToMyAPIError).flatMap(.Latest) { response in\r\n            return token.producer(self)\r\n        }\r\n    }\r\n}\r\n\r\n\r\nextension ReactiveCocoaMoyaProvider where Target: MyTargetMarker {\r\n    func requestProducer(token: Target) -> SignalProducer<Response, MyAPIError> {\r\n        let producer: SignalProducer<Response, MyAPIError > = request(token).mapError { MyAPIError.MoyaError($0) }\r\n        return producer\r\n    }\r\n}\r\n\r\nstruct GetUserTarget: MyTargetType, ProducerTargetType {\r\n    typealias ResponseType = MyUser\r\n    \r\n    var path: String {\r\n        return \"/user\"\r\n    }\r\n\r\n    func producer(provider: MyAPIProvider) -> SignalProducer<ResponseType, MyAPIError> {\r\n        return provider.requestProducer(target).flatMap(.Latest) { response -> SignalProducer<ResponseType, MyAPIError> in\r\n            return response.mapJSONValueProducer().toDecodedProducer()\r\n        }\r\n    }\r\n}\r\n\r\nstruct CustomGetSomethingTarget: MyTargetType, ProducerTargetType {\r\n    typealias ResponseType = MyCustomType\r\n    \r\n    var path: String {\r\n        return \"/something\"\r\n    }\r\n\r\n    func producer(provider: MyAPIProvider) -> SignalProducer<ResponseType, MyAPIError> {\r\n        return provider.requestProducer(target).flatMap(.Latest) { response -> SignalProducer<ResponseType, MyAPIError> in\r\n            // custom parsing and processing \r\n        }\r\n    }\r\n}\r\n```\r\n\r\nWith a little boilerplate, this allows each `TargetType` to be defined as a separate object that can be passed to the API provider to initiate a request.\r\n\r\nI would love/appreciate any feedback. "},"comment":{"url":"https://api.github.com/repos/Moya/Moya/issues/comments/167595961","html_url":"https://github.com/Moya/Moya/issues/348#issuecomment-167595961","issue_url":"https://api.github.com/repos/Moya/Moya/issues/348","id":167595961,"user":{"login":"KevinVitale","id":276137,"avatar_url":"https://avatars.githubusercontent.com/u/276137?v=3","gravatar_id":"","url":"https://api.github.com/users/KevinVitale","html_url":"https://github.com/KevinVitale","followers_url":"https://api.github.com/users/KevinVitale/followers","following_url":"https://api.github.com/users/KevinVitale/following{/other_user}","gists_url":"https://api.github.com/users/KevinVitale/gists{/gist_id}","starred_url":"https://api.github.com/users/KevinVitale/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KevinVitale/subscriptions","organizations_url":"https://api.github.com/users/KevinVitale/orgs","repos_url":"https://api.github.com/users/KevinVitale/repos","events_url":"https://api.github.com/users/KevinVitale/events{/privacy}","received_events_url":"https://api.github.com/users/KevinVitale/received_events","type":"User","site_admin":false},"created_at":"2015-12-28T16:09:54Z","updated_at":"2015-12-28T16:09:54Z","body":"@mpurland: thanks again for the thread. After more than a few long nights, I've achieved a setup similar to yours. My goal was to create a system whereby `TargetType` enums could associate with the native types their JSON results should be parsed into, as well as create signal producers from `TargetType` enums without much boilerplate. \r\n\r\nThe result: \r\n\r\n```\r\n// Sends only `Cart` structs\r\nAPITarget<CartAPI>(.Create(treeID: 77))\r\n    .on( next: { print($0.id) } )\r\n    .start()\r\n\r\n// Sends only `Product` structs\r\nAPITarget<ProductsAPI>(.InCategory(treeID: 77, categoryID: 40))\r\n    .on( next: { print($0.name) } )\r\n    .start()\r\n```\r\n<hr/>\r\n\r\nLike you I created a struct, `APITarget`, that mirrors my own `APITargetType: TargetType` protocol. Then, `APITarget` adopts `SignalProducerType`.\r\n\r\nI extended `ReactiveCocoaMoyaProvider` with `mapDecodable()` (similar to `mapJSON()`) for targets which are `APITargetType`, and I'm able to parse results using the associated type on `APITargetType` (`typealias ParsedType`). `APITarget` creates instances of `ReactiveCocoaMoyaProvider` based on the `APITargetType` it's given.\r\n\r\n```\r\npublic struct APITarget<T: APITargetType where T.ParsedType: Decodable, T.ParsedType.DecodedType == T.ParsedType> {\r\n    let provider: ReactiveCocoaMoyaProvider<T> = ReactiveCocoaMoyaProvider()\r\n    let target: T\r\n    public init(_ target: T) {\r\n        self.target = target\r\n    }\r\n}\r\n```\r\n<hr/>\r\n"}}