├── .swift-version ├── .slather.yml ├── codecov.yml ├── Gemfile ├── Dockerfile ├── Sources └── Frisbee │ ├── Entities │ ├── HTTPMethod.swift │ ├── Result.swift │ └── FrisbeeError.swift │ ├── Requestables │ ├── OnComplete.swift │ ├── Putable.swift │ ├── Postable.swift │ ├── Getable.swift │ ├── NetworkGet.swift │ ├── NetworkPut.swift │ └── NetworkPost.swift │ ├── Adapters │ ├── EncodableAdapter.swift │ ├── BodiableAdapter.swift │ ├── Cancellable.swift │ ├── DecodableAdapter.swift │ ├── SerializableAdapter.swift │ ├── URLQueriableAdapter.swift │ ├── SerializerJSONAdapter.swift │ ├── URLSessionTaskAdapter.swift │ ├── DecoderDataAdapter.swift │ ├── EncoderJSONAdapter.swift │ ├── DecoderJSONAdapter.swift │ ├── QueryItemAdapter.swift │ ├── BodyAdapter.swift │ └── URLQueryAdapter.swift │ ├── Factories │ ├── Adapters │ │ ├── EncodableAdapterFactory.swift │ │ ├── URLQueriableAdapterFactory.swift │ │ ├── SerializableAdapterFactory.swift │ │ ├── BodyAdapterFactory.swift │ │ └── DecodableAdapterFactory.swift │ ├── ResultGeneratorFactory.swift │ ├── URLSessionFactory.swift │ └── URLRequestFactory.swift │ └── Interactors │ ├── DataTaskRunner.swift │ └── ResultGenerator.swift ├── Tests ├── FrisbeeTests │ ├── Support │ │ ├── Entities │ │ │ ├── Fake.swift │ │ │ ├── MovieQuery.swift │ │ │ ├── SomeError.swift │ │ │ ├── Empty.swift │ │ │ ├── Movie.swift │ │ │ └── SomeEntity.swift │ │ ├── Doubles │ │ │ ├── EncoderThrowErrorFakeAdapter.swift │ │ │ ├── SerializerThrowErrorFakeAdapter.swift │ │ │ ├── BodyThrowErrorFakeAdapter.swift │ │ │ ├── DecoderThrowErrorFakeAdapter.swift │ │ │ ├── URLWithQueryTrhrowErrorFakeBuildable.swift │ │ │ └── MockURLSession.swift │ │ └── Extensions │ │ │ ├── XCTestCase+AssertContains.swift │ │ │ ├── FrisbeeError+All.swift │ │ │ └── SequenceExtensions.swift │ ├── Adapters │ │ ├── URLSessionTaskAdapterTests.swift │ │ ├── DecoderDataAdapterTests.swift │ │ ├── QueryItemAdapterTests.swift │ │ ├── BodyAdapterTests.swift │ │ └── URLQueryAdapterTests.swift │ ├── Factories │ │ ├── URLSessionFactoryTests.swift │ │ └── URLRequestFactoryTests.swift │ ├── Entities │ │ ├── ResultTests.swift │ │ └── FrisbeeErrorTests.swift │ ├── Requestables │ │ ├── IntegrationNetworkPutTests.swift │ │ ├── IntegrationNetworkPostTests.swift │ │ ├── NetworkGetTests.swift │ │ ├── NetworkPutTests.swift │ │ ├── NetworkPostTests.swift │ │ └── IntegrationNetworkGetTests.swift │ └── Interactors │ │ └── ResultGeneratorTests.swift └── LinuxMain.swift ├── Frisbee.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── Frisbee-Package.xcscheme ├── Frisbee_Info.plist ├── FrisbeeTests_Info.plist └── project.pbxproj ├── Frisbee.plist ├── Package.swift ├── Frisbee.podspec ├── LICENSE ├── .gitignore ├── Gemfile.lock ├── bitrise.yml └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 4.0.3 2 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - Tests/* -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - Tests/FrisbeeTests 3 | - Tests -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "cocoapods", "~> 1.4.0.beta.2" 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:latest 2 | 3 | WORKDIR /package 4 | 5 | COPY . ./ 6 | 7 | CMD swift test --parallel -------------------------------------------------------------------------------- /Sources/Frisbee/Entities/HTTPMethod.swift: -------------------------------------------------------------------------------- 1 | enum HTTPMethod: String { 2 | case GET, POST, PUT 3 | } 4 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/OnComplete.swift: -------------------------------------------------------------------------------- 1 | public typealias OnComplete = (Result) -> Void 2 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/Fake.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Fake: Codable { 4 | let fake: String 5 | } 6 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/MovieQuery.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct MovieQuery: Encodable { 4 | let page: Int 5 | } 6 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/SomeError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum SomeError: String, Error { 4 | case some 5 | case another 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/EncodableAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol EncodableAdapter { 4 | func encode(_ value: T) throws -> Data 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/BodiableAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol BodiableAdapter { 4 | func build(withBody body: T) throws -> [String: Any] 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/Cancellable.swift: -------------------------------------------------------------------------------- 1 | public protocol Cancellable { 2 | func cancel() 3 | } 4 | 5 | final class NilCancellable: Cancellable { 6 | func cancel() {} 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/DecodableAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol DecodableAdapter { 4 | func decode(_ type: T.Type, from data: Data) throws -> T 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/SerializableAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol SerializableAdapter { 4 | func object(with data: Data, options opt: JSONSerialization.ReadingOptions) throws -> Any 5 | } 6 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/Adapters/EncodableAdapterFactory.swift: -------------------------------------------------------------------------------- 1 | final class EncodableAdapterFactory { 2 | 3 | static func make() -> EncodableAdapter { 4 | return EncoderJSONAdapter() 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/Adapters/URLQueriableAdapterFactory.swift: -------------------------------------------------------------------------------- 1 | final class URLQueriableAdapterFactory { 2 | 3 | static func make() -> URLQueriableAdapter { 4 | return URLQueryAdapter() 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/Adapters/SerializableAdapterFactory.swift: -------------------------------------------------------------------------------- 1 | final class SerializableAdapterFactory { 2 | 3 | static func make() -> SerializableAdapter { 4 | return SerializerJSONAdapter() 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/URLQueriableAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol URLQueriableAdapter { 4 | func build(withUrl url: String, query: T) throws -> URL 5 | func build(withUrl url: URL, query: T) throws -> URL 6 | } 7 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/Empty.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Empty: Codable, Equatable { 4 | static let data: Data = "{}".data(using: .utf8)! 5 | 6 | static func == (lhs: Empty, rhs: Empty) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/Movie.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct Movie: Encodable { 4 | let name: String 5 | let releaseYear: Int 6 | 7 | enum CodingKeys: String, CodingKey { 8 | case name, releaseYear = "release_year" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/ResultGeneratorFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class ResultGeneratorFactory { 4 | 5 | static func make() -> ResultGenerator { 6 | return ResultGenerator(decoder: DecodableAdapterFactory.make(T.self)) 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/EncoderThrowErrorFakeAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Frisbee 3 | 4 | class EncoderThrowErrorFakeAdapter: EncodableAdapter { 5 | func encode(_ value: T) throws -> Data { 6 | throw FrisbeeError.invalidEntity 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Entities/SomeEntity.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | struct SomeEntity {} 4 | struct SomeEquatableEntity: Equatable { 5 | static func == (lhs: SomeEquatableEntity, 6 | rhs: SomeEquatableEntity) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/Adapters/BodyAdapterFactory.swift: -------------------------------------------------------------------------------- 1 | final class BodyAdapterFactory { 2 | 3 | static func make() -> BodiableAdapter { 4 | return BodyAdapter(encoder: EncodableAdapterFactory.make(), 5 | serializer: SerializableAdapterFactory.make()) 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Extensions/XCTestCase+AssertContains.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | extension XCTestCase { 4 | func XCTAssertContains(_ array: [T], _ predicate: (T) -> Bool, file: StaticString = #file, line: UInt = #line) { 5 | XCTAssertTrue(array.contains(where: predicate)) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/SerializerJSONAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class SerializerJSONAdapter: SerializableAdapter { 4 | func object(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any { 5 | return try JSONSerialization.jsonObject(with: data, options: opt) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/URLSessionTaskAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class URLSessionTaskAdapter: Cancellable { 4 | private let task: URLSessionTask 5 | 6 | init(task: URLSessionTask) { 7 | self.task = task 8 | } 9 | 10 | func cancel() { 11 | task.cancel() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/Adapters/DecodableAdapterFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class DecodableAdapterFactory { 4 | 5 | static func make(_ type: T.Type) -> DecodableAdapter { 6 | if T.self == Data.self { return DecoderDataAdapter() } 7 | return DecoderJSONAdapter() 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/DecoderDataAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class DecoderDataAdapter: DecodableAdapter { 4 | 5 | func decode(_ type: T.Type, from data: Data) throws -> T { 6 | if let data = data as? T { return data } 7 | throw FrisbeeError.invalidEntity 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/SerializerThrowErrorFakeAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Frisbee 3 | 4 | class SerializerThrowErrorFakeAdapter: SerializableAdapter { 5 | func object(with data: Data, options opt: JSONSerialization.ReadingOptions = []) throws -> Any { 6 | throw FrisbeeError.invalidEntity 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | Frisbee-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Frisbee.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Major 6 | 0 7 | Minor 8 | 2 9 | Patch 10 | 5 11 | 12 | 13 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/BodyThrowErrorFakeAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Frisbee 3 | 4 | struct BodyThrowErrorFakeAdapter: BodiableAdapter { 5 | private let errorToThrow = FrisbeeError.invalidEntity 6 | 7 | func build(withBody body: T) throws -> [String: Any] { 8 | throw errorToThrow 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/DecoderThrowErrorFakeAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Frisbee 3 | 4 | class DecoderThrowErrorFakeAdapter: DecodableAdapter { 5 | private let error = FrisbeeError.invalidEntity 6 | 7 | func decode(_ type: T.Type, from data: Data) throws -> T { 8 | throw error 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/URLSessionFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class URLSessionFactory { 4 | 5 | static func make() -> URLSession { 6 | let configuration = URLSessionConfiguration.default 7 | let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil) 8 | 9 | return session 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Extensions/FrisbeeError+All.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Frisbee 3 | 4 | extension FrisbeeError { 5 | static func all(error: Error) -> [FrisbeeError] { 6 | return [FrisbeeError.invalidUrl, .invalidQuery, .invalidEntity, .noData, .requestCancelled, 7 | .other(localizedDescription: error.localizedDescription)] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/EncoderJSONAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class EncoderJSONAdapter: EncodableAdapter { 4 | 5 | let encoder: JSONEncoder 6 | 7 | init(encoder: JSONEncoder = JSONEncoder()) { 8 | self.encoder = encoder 9 | } 10 | 11 | func encode(_ value: T) throws -> Data { 12 | return try encoder.encode(value) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Frisbee", 7 | products: [ 8 | .library(name: "Frisbee", targets: ["Frisbee"]) 9 | ], 10 | dependencies: [], 11 | targets: [ 12 | .target(name: "Frisbee", dependencies: []), 13 | .testTarget(name: "FrisbeeTests", dependencies: ["Frisbee"]) 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/DecoderJSONAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class DecoderJSONAdapter: DecodableAdapter { 4 | 5 | let decoder: JSONDecoder 6 | 7 | init(decoder: JSONDecoder = JSONDecoder()) { 8 | self.decoder = decoder 9 | } 10 | 11 | func decode(_ type: T.Type, from data: Data) throws -> T { 12 | return try decoder.decode(type, from: data) 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/URLWithQueryTrhrowErrorFakeBuildable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @testable import Frisbee 3 | 4 | class URLWithQueryTrhrowErrorFakeBuildable: URLQueriableAdapter { 5 | var errorToThrow: FrisbeeError! 6 | 7 | func build(withUrl url: String, query: Query) throws -> URL { 8 | throw errorToThrow 9 | } 10 | 11 | func build(withUrl url: URL, query: Query) throws -> URL { 12 | throw errorToThrow 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Frisbee/Factories/URLRequestFactory.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class URLRequestFactory { 4 | 5 | static func make(_ httpMethod: HTTPMethod, _ url: URL) -> URLRequest { 6 | var request = URLRequest(url: url) 7 | request.httpMethod = httpMethod.rawValue 8 | request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type") 9 | request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Accept") 10 | 11 | return request 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Frisbee/Interactors/DataTaskRunner.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class DataTaskRunner { 4 | 5 | static func run(with urlSession: URLSession, request: URLRequest, 6 | onComplete: @escaping OnComplete) -> Cancellable { 7 | let task = urlSession.dataTask(with: request) { data, _, error in 8 | onComplete(ResultGeneratorFactory.make().generate(data: data, error: error)) 9 | } 10 | task.resume() 11 | return URLSessionTaskAdapter(task: task) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Extensions/SequenceExtensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Sequence { 4 | /// Returns the [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of `self` 5 | /// with a given sequence `x` 6 | /// ([0,1], [a,b,c]) -> [(0,a), (0,b), (0,c), (1,a), (1,b), (1,c)] 7 | /// - Parameter x: The other sequence to be combined 8 | func extensiveCombine(_ other: X) -> [(Self.Iterator.Element, X.Iterator.Element)] { 9 | return self.map { one in other.map { (one, $0) } } 10 | .flatMap { $0 } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Adapters/URLSessionTaskAdapterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | #if !os(Linux) 5 | final class URLSessionTaskAdapterTests: XCTestCase { 6 | 7 | func testAdapterWhenCancelThenCancelTask() { 8 | let task = makeTask() 9 | 10 | URLSessionTaskAdapter(task: task).cancel() 11 | 12 | XCTAssert(task.didCallCancel) 13 | } 14 | 15 | private func makeTask() -> MockDataTask { 16 | return MockDataTask(result: .error(MockError.noMockAvailable), 17 | callback: { _, _, _ in}) 18 | } 19 | 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import FrisbeeTests 3 | 4 | var allTests = [ 5 | testCase(IntegrationNetworkGetTests.allTests), 6 | testCase(IntegrationNetworkPostTests.allTests), 7 | testCase(URLSessionFactoryTests.allTests), 8 | testCase(URLRequestFactoryTests.allTests), 9 | testCase(QueryItemAdapterTests.allTests), 10 | testCase(URLQueryAdapterTests.allTests), 11 | testCase(ResultGeneratorTests.allTests), 12 | testCase(FrisbeeErrorTests.allTests), 13 | testCase(BodyAdapterTests.allTests), 14 | testCase(ResultTests.allTests) 15 | ] 16 | 17 | XCTMain(allTests) 18 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/QueryItemAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class QueryItemAdapter { 4 | 5 | static func build(withEntity entity: T) throws -> [URLQueryItem] { 6 | var json: [String: Any] = [:] 7 | 8 | let data = try JSONEncoder().encode(entity) 9 | let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) 10 | if let jsonDictionary = jsonObject as? [String: Any] { json = jsonDictionary } 11 | 12 | return json.map { keyAndValue -> URLQueryItem in 13 | let value = String(describing: keyAndValue.value) 14 | return URLQueryItem(name: keyAndValue.key, value: value) 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Adapters/DecoderDataAdapterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class DecoderDataAdapterTests: XCTestCase { 5 | 6 | func testDecodeWhenEncodableGenericIsDataThenDecodeData() throws { 7 | let data = Data(count: 33) 8 | 9 | let decodedData = try DecoderDataAdapter().decode(Data.self, from: data) 10 | 11 | XCTAssertEqual(decodedData.count, data.count) 12 | } 13 | 14 | func testDecodeWhenNotDataEntityThenThrowsError() throws { 15 | let empty = Empty() 16 | let emptyData = try EncoderJSONAdapter().encode(empty) 17 | 18 | XCTAssertThrowsError(try DecoderDataAdapter().decode(Empty.self, from: emptyData)) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Frisbee.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Frisbee" 3 | s.version = "0.2.5" 4 | s.summary = "Another network wrapper for URLSession" 5 | s.description = "Built to make it easy to create tests for your application's network layer." 6 | s.homepage = "https://github.com/ronanrodrigo/Frisbee" 7 | s.license = {:type => "MIT", :file => "LICENSE"} 8 | s.author = {"Ronan Rodrigo Nunes" => "ronan.nunes@me.com"} 9 | s.social_media_url = "https://twitter.com/ronanrodrigo_" 10 | s.ios.deployment_target = "10.0" 11 | s.osx.deployment_target = "10.13" 12 | s.source = { :git => "https://github.com/ronanrodrigo/Frisbee.git", :tag => "#{s.version}" } 13 | s.source_files = "Sources/**/*.{swift}" 14 | s.exclude_files = "Tests" 15 | end 16 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/BodyAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class BodyAdapter: BodiableAdapter { 4 | 5 | private let encoder: EncodableAdapter 6 | private let serializer: SerializableAdapter 7 | 8 | init(encoder: EncodableAdapter, serializer: SerializableAdapter) { 9 | self.encoder = encoder 10 | self.serializer = serializer 11 | } 12 | 13 | func build(withBody body: T) throws -> [String: Any] { 14 | var json: [String: Any] = [:] 15 | 16 | let data = try encoder.encode(body) 17 | let jsonObject = try serializer.object(with: data, options: []) 18 | if let jsonDictionary = jsonObject as? [String: Any] { json = jsonDictionary } 19 | 20 | return json 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Frisbee/Interactors/ResultGenerator.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class ResultGenerator { 4 | 5 | private let decoder: DecodableAdapter 6 | 7 | init(decoder: DecodableAdapter) { 8 | self.decoder = decoder 9 | } 10 | 11 | func generate(data: Data?, error: Error?) -> Result { 12 | guard let data = data else { 13 | switch error { 14 | case .some(let error): return .fail(FrisbeeError(error)) 15 | case .none: return .fail(FrisbeeError.noData) 16 | } 17 | } 18 | 19 | do { 20 | let entityDecoded = try decoder.decode(T.self, from: data) 21 | return .success(entityDecoded) 22 | } catch { return .fail(.noData) } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Frisbee/Adapters/URLQueryAdapter.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | final class URLQueryAdapter: URLQueriableAdapter { 4 | 5 | func build(withUrl url: String, query: T) throws -> URL { 6 | guard let actualUrl = URL(string: url) else { 7 | throw FrisbeeError.invalidUrl 8 | } 9 | return try build(withUrl: actualUrl, query: query) 10 | } 11 | 12 | func build(withUrl url: URL, query: T) throws -> URL { 13 | var url = url 14 | var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) 15 | urlComponents?.queryItems = try QueryItemAdapter.build(withEntity: query) 16 | 17 | if let componentsUrl = urlComponents?.url { url = componentsUrl } 18 | 19 | return url 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/Frisbee_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/FrisbeeTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Adapters/QueryItemAdapterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class QueryItemAdapterTests: XCTestCase { 5 | 6 | func testBuildWhenValidDictionaryThenReturnQueryItem() throws { 7 | let entity = Movie(name: "Ghostbuster", releaseYear: 1984) 8 | 9 | let queryItems = try QueryItemAdapter.build(withEntity: entity) 10 | 11 | XCTAssertEqual(queryItems.count, 2) 12 | XCTAssertContains(queryItems, { $0.name == "release_year" }) 13 | XCTAssertContains(queryItems, { $0.name == "name" }) 14 | XCTAssertContains(queryItems, { $0.value == "1984" }) 15 | XCTAssertContains(queryItems, { $0.value == "Ghostbuster" }) 16 | } 17 | 18 | static var allTests = [ 19 | ("testBuildWhenValidDictionaryThenReturnQueryItem", 20 | testBuildWhenValidDictionaryThenReturnQueryItem) 21 | ] 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Factories/URLSessionFactoryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class URLSessionFactoryTests: XCTestCase { 5 | 6 | func testMakeWithoutDelegateThenReturnURLSessionWithNilDelegate() { 7 | let urlSession = URLSessionFactory.make() 8 | 9 | XCTAssertNil(urlSession.delegate) 10 | } 11 | 12 | func testMakeWithoutDelegateQueueThenReturnURLSessionWithDefaultDelegateQueue() { 13 | let urlSession = URLSessionFactory.make() 14 | 15 | XCTAssertNotNil(urlSession.delegateQueue) 16 | } 17 | 18 | static var allTests = [ 19 | ("testMakeWithoutDelegateQueueThenReturnURLSessionWithDefaultDelegateQueue", 20 | testMakeWithoutDelegateQueueThenReturnURLSessionWithDefaultDelegateQueue), 21 | ("testMakeWithoutDelegateThenReturnURLSessionWithNilDelegate", 22 | testMakeWithoutDelegateThenReturnURLSessionWithNilDelegate) 23 | ] 24 | 25 | } 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ronan Rodrigo Nunes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/Frisbee/Entities/Result.swift: -------------------------------------------------------------------------------- 1 | public enum Result { 2 | case success(T) 3 | case fail(FrisbeeError) 4 | } 5 | 6 | extension Result { 7 | public var data: T? { 8 | guard case let .success(data) = self else { 9 | return nil 10 | } 11 | return data 12 | } 13 | 14 | public var error: FrisbeeError? { 15 | guard case let .fail(error) = self else { 16 | return nil 17 | } 18 | return error 19 | } 20 | } 21 | 22 | extension Result: Equatable { 23 | public static func == (lhs: Result, rhs: Result) -> Bool { 24 | switch (lhs, rhs) { 25 | case let (.fail(lhs), .fail(rhs)): 26 | return lhs == rhs 27 | default: 28 | return false 29 | } 30 | } 31 | } 32 | 33 | extension Result where T: Equatable { 34 | public static func == (lhs: Result, rhs: Result) -> Bool { 35 | switch (lhs, rhs) { 36 | case let (.success(lhs), .success(rhs)): 37 | return lhs == rhs 38 | case let (.fail(lhs), .fail(rhs)): 39 | return lhs == rhs 40 | default: 41 | return false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/Putable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Putable { 4 | @discardableResult 5 | func put(url: URL, onComplete: @escaping OnComplete) -> Cancellable 6 | @discardableResult 7 | func put(url: URL, body: U, onComplete: @escaping OnComplete) -> Cancellable 8 | } 9 | 10 | extension Putable { 11 | @discardableResult 12 | public func put(url: String, onComplete: @escaping OnComplete) -> Cancellable { 13 | guard let url = URL(string: url) else { 14 | onComplete(.fail(FrisbeeError.invalidUrl)) 15 | return NilCancellable() 16 | } 17 | return put(url: url, onComplete: onComplete) 18 | } 19 | 20 | @discardableResult 21 | public func put(url: String, body: U, 22 | onComplete: @escaping OnComplete) -> Cancellable { 23 | guard let url = URL(string: url) else { 24 | onComplete(.fail(FrisbeeError.invalidUrl)) 25 | return NilCancellable() 26 | } 27 | return put(url: url, body: body, onComplete: onComplete) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Adapters/BodyAdapterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class BodyAdapterTests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | } 9 | 10 | func testBuildWithBodyWhenEncoderThrowsAnErrorThenThrows() { 11 | let adapter = BodyAdapter(encoder: EncoderThrowErrorFakeAdapter(), 12 | serializer: SerializableAdapterFactory.make()) 13 | 14 | XCTAssertThrowsError(try adapter.build(withBody: Fake(fake: ""))) 15 | } 16 | 17 | func testBuildWithBodyWhenSerializerThrowsAnErrorThenThrows() { 18 | let adapter = BodyAdapter(encoder: EncodableAdapterFactory.make(), 19 | serializer: SerializerThrowErrorFakeAdapter()) 20 | 21 | XCTAssertThrowsError(try adapter.build(withBody: Fake(fake: ""))) 22 | } 23 | 24 | static let allTests = [ 25 | ("testBuildWithBodyWhenEncoderThrowsAnErrorThenThrows", 26 | testBuildWithBodyWhenEncoderThrowsAnErrorThenThrows), 27 | ("testBuildWithBodyWhenSerializerThrowsAnErrorThenThrows", 28 | testBuildWithBodyWhenSerializerThrowsAnErrorThenThrows) 29 | ] 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/Postable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Postable { 4 | @discardableResult 5 | func post(url: URL, onComplete: @escaping OnComplete) -> Cancellable 6 | @discardableResult 7 | func post(url: URL, body: U, onComplete: @escaping OnComplete) -> Cancellable 8 | } 9 | 10 | extension Postable { 11 | @discardableResult 12 | public func post(url: String, onComplete: @escaping OnComplete) -> Cancellable { 13 | guard let url = URL(string: url) else { 14 | onComplete(.fail(FrisbeeError.invalidUrl)) 15 | return NilCancellable() 16 | } 17 | return post(url: url, onComplete: onComplete) 18 | } 19 | 20 | @discardableResult 21 | public func post(url: String, body: U, 22 | onComplete: @escaping OnComplete) -> Cancellable { 23 | guard let url = URL(string: url) else { 24 | onComplete(.fail(FrisbeeError.invalidUrl)) 25 | return NilCancellable() 26 | } 27 | return post(url: url, body: body, onComplete: onComplete) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Frisbee/Entities/FrisbeeError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum FrisbeeError: Error { 4 | case invalidUrl 5 | case other(localizedDescription: String) 6 | case noData 7 | case invalidQuery 8 | case invalidEntity 9 | case requestCancelled 10 | 11 | init(_ error: Error) { 12 | if let frisbeeError = error as? FrisbeeError { 13 | self = frisbeeError 14 | return 15 | } 16 | 17 | switch error { 18 | case URLError.cancelled: 19 | self = .requestCancelled 20 | default: 21 | self = .other(localizedDescription: error.localizedDescription) 22 | } 23 | 24 | } 25 | } 26 | 27 | extension FrisbeeError: Equatable { 28 | public static func == (lhs: FrisbeeError, rhs: FrisbeeError) -> Bool { 29 | switch (lhs, rhs) { 30 | case (.invalidUrl, .invalidUrl), 31 | (.noData, .noData), 32 | (.invalidQuery, .invalidQuery), 33 | (.invalidEntity, .invalidEntity), 34 | (.requestCancelled, .requestCancelled): 35 | return true 36 | case let (.other(lhs), .other(rhs)): 37 | return lhs == rhs 38 | default: 39 | return false 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/Getable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public protocol Getable { 4 | @discardableResult 5 | func get(url: URL, 6 | onComplete: @escaping OnComplete) -> Cancellable 7 | @discardableResult 8 | func get(url: URL, query: U, 9 | onComplete: @escaping OnComplete) -> Cancellable 10 | } 11 | 12 | extension Getable { 13 | @discardableResult 14 | public func get(url: String, onComplete: @escaping OnComplete) -> Cancellable { 15 | guard let url = URL(string: url) else { 16 | onComplete(.fail(FrisbeeError.invalidUrl)) 17 | return NilCancellable() 18 | } 19 | return get(url: url, onComplete: onComplete) 20 | } 21 | 22 | @discardableResult 23 | public func get(url: String, query: U, 24 | onComplete: @escaping OnComplete) -> Cancellable { 25 | guard let url = URL(string: url) else { 26 | onComplete(.fail(FrisbeeError.invalidUrl)) 27 | return NilCancellable() 28 | } 29 | return get(url: url, query: query, onComplete: onComplete) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Factories/URLRequestFactoryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class URLRequestFactoryTests: XCTestCase { 5 | 6 | func testMakeWhenGetMethodThenReturnAnURLRequestWithGetVerb() { 7 | let request = URLRequestFactory.make(.GET, URL(string: "http://www.com.br")!) 8 | 9 | XCTAssertEqual(request.httpMethod, HTTPMethod.GET.rawValue) 10 | } 11 | 12 | func testMakeWithURLThenReturnAnURLRequestWithCorrectURL() { 13 | let url = URL(string: "http://www.com.br")! 14 | 15 | let request = URLRequestFactory.make(.GET, url) 16 | 17 | XCTAssertEqual(request.url?.absoluteString, url.absoluteString) 18 | } 19 | 20 | func testMakeWhenPostMethodThenReturnAnURLRequestWithPostVerb() { 21 | let request = URLRequestFactory.make(.POST, URL(string: "http://www.com.br")!) 22 | 23 | XCTAssertEqual(request.httpMethod, HTTPMethod.POST.rawValue) 24 | } 25 | 26 | static var allTests = [ 27 | ("testMakeWhenGetMethodThenReturnAnURLRequestWithGetVerb", 28 | testMakeWhenGetMethodThenReturnAnURLRequestWithGetVerb), 29 | ("testMakeWithURLThenReturnAnURLRequestWithCorrectURL", 30 | testMakeWithURLThenReturnAnURLRequestWithCorrectURL), 31 | ("testMakeWhenPostMethodThenReturnAnURLRequestWithPostVerb", 32 | testMakeWhenPostMethodThenReturnAnURLRequestWithPostVerb) 33 | ] 34 | 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | 67 | # Coverage 68 | *.xccoverage.plist 69 | html/ -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Support/Doubles/MockURLSession.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if !os(Linux) 3 | typealias URLSessionCallback = (Data?, URLResponse?, Error?) -> Void 4 | 5 | enum MockError: Error { 6 | case noMockAvailable 7 | } 8 | 9 | class MockURLSession: URLSession { 10 | // not thread safe, make sure tests do not call this on background 11 | // or implement a locking mechanism 12 | var results: [MockDataTask.Result] 13 | 14 | init(results: [MockDataTask.Result] = []) { 15 | self.results = results 16 | super.init() 17 | } 18 | 19 | override func dataTask(with request: URLRequest, 20 | completionHandler: @escaping URLSessionCallback) -> URLSessionDataTask { 21 | guard !results.isEmpty else { 22 | return MockDataTask(result: .error(MockError.noMockAvailable), 23 | callback: completionHandler) 24 | } 25 | 26 | let result = results.removeFirst() 27 | return MockDataTask(result: result, callback: completionHandler) 28 | } 29 | } 30 | 31 | class MockDataTask: URLSessionDataTask { 32 | enum Result { 33 | case success(Data, URLResponse), error(Error) 34 | } 35 | 36 | let result: Result 37 | let callback: (Data?, URLResponse?, Error?) -> Void 38 | var didCallCancel = false 39 | 40 | init(result: Result, callback: @escaping URLSessionCallback) { 41 | self.result = result 42 | self.callback = callback 43 | super.init() 44 | } 45 | 46 | override func resume() { 47 | switch result { 48 | case let .success(data, response): 49 | callback(data, response, nil) 50 | case let .error(error): 51 | callback(nil, nil, error) 52 | } 53 | } 54 | 55 | override func cancel() { 56 | didCallCancel = true 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Entities/ResultTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Frisbee 3 | import XCTest 4 | 5 | class ResultTests: XCTestCase { 6 | 7 | func testEquatableEnumCasesWhenAssociatedValueAreEquatableThenShouldBeEqual() { 8 | let success = Result.success(SomeEquatableEntity()) 9 | let fail = Result.fail(FrisbeeError.invalidEntity) 10 | 11 | XCTAssertEqual(success.data, success.data) 12 | XCTAssertEqual(success.error, success.error) 13 | XCTAssertNil(success.error) 14 | 15 | XCTAssertEqual(fail.error, fail.error) 16 | XCTAssertEqual(fail.data, fail.data) 17 | XCTAssertNil(fail.data) 18 | } 19 | 20 | func testEquatableEnumCasesWhenAssociatedValueArentEquatableThenShoudBeNotEqual() { 21 | let success = Result.success(SomeEntity()) 22 | let fail = Result.fail(FrisbeeError.invalidEntity) 23 | 24 | XCTAssertNotEqual(success, success) 25 | XCTAssertNotEqual(success, fail) 26 | XCTAssertNotEqual(fail, success) 27 | XCTAssertEqual(fail, fail) 28 | } 29 | 30 | func testEqualityOperatorWhenEquatableEntityThenShoudeBeEqual() { 31 | let success = Result.success(SomeEquatableEntity()) 32 | let fail = Result.fail(FrisbeeError.invalidEntity) 33 | 34 | XCTAssertTrue(success == success) 35 | XCTAssertTrue(fail == fail) 36 | XCTAssertFalse(success == fail) 37 | XCTAssertFalse(fail == success) 38 | } 39 | 40 | static var allTests = [ 41 | ("testEquatableEnumCasesWhenAssociatedValueAreEquatableThenShouldBeEqual", 42 | testEquatableEnumCasesWhenAssociatedValueAreEquatableThenShouldBeEqual), 43 | ("testEquatableEnumCasesWhenAssociatedValueArentEquatableThenShoudBeNotEqual", 44 | testEquatableEnumCasesWhenAssociatedValueArentEquatableThenShoudBeNotEqual), 45 | ("testEqualityOperatorWhenEquatableEntityThenShoudeBeEqual", 46 | testEqualityOperatorWhenEquatableEntityThenShoudeBeEqual) 47 | ] 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/NetworkGet.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | @available(*, deprecated, message: "use NetworkGet") 4 | typealias NetworkGetter = NetworkGet 5 | 6 | public final class NetworkGet: Getable { 7 | 8 | let urlSession: URLSession 9 | private let queryAdapter: URLQueriableAdapter 10 | 11 | public convenience init() { 12 | self.init(queryAdapter: URLQueriableAdapterFactory.make(), urlSession: URLSessionFactory.make()) 13 | } 14 | 15 | public convenience init(urlSession: URLSession) { 16 | self.init(queryAdapter: URLQueriableAdapterFactory.make(), urlSession: urlSession) 17 | } 18 | 19 | init(queryAdapter: URLQueriableAdapter, urlSession: URLSession) { 20 | self.queryAdapter = queryAdapter 21 | self.urlSession = urlSession 22 | } 23 | 24 | @discardableResult 25 | public func get(url: URL, onComplete: @escaping OnComplete) -> Cancellable { 26 | return makeRequest(url: url, onComplete: onComplete) 27 | } 28 | 29 | @discardableResult 30 | public func get(url: URL, query: U, 31 | onComplete: @escaping OnComplete) -> Cancellable { 32 | //we're only doing this way (instead of returning on the do block) to avoid uncoverage curly brace on tests 33 | //similar issue: https://stackoverflow.com/questions/34622082/why-is-a-closing-brace-showing-no-code-coverage 34 | var url = url 35 | do { 36 | url = try queryAdapter.build(withUrl: url, query: query) 37 | } catch { 38 | onComplete(.fail(FrisbeeError(error))) 39 | return NilCancellable() 40 | } 41 | 42 | return makeRequest(url: url, onComplete: onComplete) 43 | } 44 | 45 | private func makeRequest(url: URL, onComplete: @escaping (Result) -> Void) -> Cancellable { 46 | let request = URLRequestFactory.make(.GET, url) 47 | 48 | return DataTaskRunner.run(with: urlSession, request: request, onComplete: onComplete) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.3.6) 5 | activesupport (4.2.10) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | claide (1.0.2) 11 | cocoapods (1.4.0.rc.1) 12 | activesupport (>= 4.0.2, < 5) 13 | claide (>= 1.0.2, < 2.0) 14 | cocoapods-core (= 1.4.0.rc.1) 15 | cocoapods-deintegrate (>= 1.0.1, < 2.0) 16 | cocoapods-downloader (>= 1.1.3, < 2.0) 17 | cocoapods-plugins (>= 1.0.0, < 2.0) 18 | cocoapods-search (>= 1.0.0, < 2.0) 19 | cocoapods-stats (>= 1.0.0, < 2.0) 20 | cocoapods-trunk (>= 1.3.0, < 2.0) 21 | cocoapods-try (>= 1.1.0, < 2.0) 22 | colored2 (~> 3.1) 23 | escape (~> 0.0.4) 24 | fourflusher (~> 2.0.1) 25 | gh_inspector (~> 1.0) 26 | molinillo (~> 0.6.4) 27 | nap (~> 1.0) 28 | ruby-macho (~> 1.1) 29 | xcodeproj (>= 1.5.4, < 2.0) 30 | cocoapods-core (1.4.0.rc.1) 31 | activesupport (>= 4.0.2, < 6) 32 | fuzzy_match (~> 2.0.4) 33 | nap (~> 1.0) 34 | cocoapods-deintegrate (1.0.1) 35 | cocoapods-downloader (1.1.3) 36 | cocoapods-plugins (1.0.0) 37 | nap 38 | cocoapods-search (1.0.0) 39 | cocoapods-stats (1.0.0) 40 | cocoapods-trunk (1.3.0) 41 | nap (>= 0.8, < 2.0) 42 | netrc (~> 0.11) 43 | cocoapods-try (1.1.0) 44 | colored2 (3.1.2) 45 | concurrent-ruby (1.0.5) 46 | escape (0.0.4) 47 | fourflusher (2.0.1) 48 | fuzzy_match (2.0.4) 49 | gh_inspector (1.0.3) 50 | i18n (0.9.1) 51 | concurrent-ruby (~> 1.0) 52 | minitest (5.10.3) 53 | molinillo (0.6.4) 54 | nanaimo (0.2.3) 55 | nap (1.1.0) 56 | netrc (0.11.0) 57 | ruby-macho (1.1.0) 58 | thread_safe (0.3.6) 59 | tzinfo (1.2.4) 60 | thread_safe (~> 0.1) 61 | xcodeproj (1.5.4) 62 | CFPropertyList (~> 2.3.3) 63 | claide (>= 1.0.2, < 2.0) 64 | colored2 (~> 3.1) 65 | nanaimo (~> 0.2.3) 66 | 67 | PLATFORMS 68 | ruby 69 | 70 | DEPENDENCIES 71 | cocoapods (~> 1.4.0.beta.2) 72 | 73 | BUNDLED WITH 74 | 1.16.0 75 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/NetworkPut.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class NetworkPut: Putable { 4 | 5 | let urlSession: URLSession 6 | private let bodyAdapter: BodiableAdapter 7 | 8 | public convenience init() { 9 | self.init(urlSession: URLSessionFactory.make(), bodyAdapter: BodyAdapterFactory.make()) 10 | } 11 | 12 | public convenience init(urlSession: URLSession) { 13 | self.init(urlSession: urlSession, bodyAdapter: BodyAdapterFactory.make()) 14 | } 15 | 16 | init(urlSession: URLSession, bodyAdapter: BodiableAdapter) { 17 | self.urlSession = urlSession 18 | self.bodyAdapter = bodyAdapter 19 | } 20 | 21 | @discardableResult 22 | public func put(url: URL, onComplete: @escaping OnComplete) -> Cancellable { 23 | return makeRequest(url: url, onComplete: onComplete) 24 | } 25 | 26 | @discardableResult 27 | public func put(url: URL, body: U, 28 | onComplete: @escaping OnComplete) -> Cancellable { 29 | return makeRequest(url: url, body: body, onComplete: onComplete) 30 | } 31 | 32 | private func makeRequest(url: URL, onComplete: @escaping OnComplete) -> Cancellable { 33 | let request = URLRequestFactory.make(.PUT, url) 34 | return DataTaskRunner.run(with: urlSession, request: request, onComplete: onComplete) 35 | } 36 | 37 | private func makeRequest(url: URL, body: U, 38 | onComplete: @escaping OnComplete) -> Cancellable { 39 | var request = URLRequestFactory.make(.PUT, url) 40 | do { 41 | let bodyObject = try bodyAdapter.build(withBody: body) 42 | request.httpBody = try JSONSerialization.data(withJSONObject: bodyObject, options: []) 43 | } catch { 44 | onComplete(.fail(FrisbeeError(error))) 45 | return NilCancellable() 46 | } 47 | 48 | return DataTaskRunner.run(with: urlSession, request: request, onComplete: onComplete) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Frisbee/Requestables/NetworkPost.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public final class NetworkPost: Postable { 4 | 5 | let urlSession: URLSession 6 | private let bodyAdapter: BodiableAdapter 7 | 8 | public convenience init() { 9 | self.init(urlSession: URLSessionFactory.make(), bodyAdapter: BodyAdapterFactory.make()) 10 | } 11 | 12 | public convenience init(urlSession: URLSession) { 13 | self.init(urlSession: urlSession, bodyAdapter: BodyAdapterFactory.make()) 14 | } 15 | 16 | init(urlSession: URLSession, bodyAdapter: BodiableAdapter) { 17 | self.urlSession = urlSession 18 | self.bodyAdapter = bodyAdapter 19 | } 20 | 21 | @discardableResult 22 | public func post(url: URL, onComplete: @escaping OnComplete) -> Cancellable { 23 | return makeRequest(url: url, onComplete: onComplete) 24 | } 25 | 26 | @discardableResult 27 | public func post(url: URL, body: U, 28 | onComplete: @escaping OnComplete) -> Cancellable { 29 | return makeRequest(url: url, body: body, onComplete: onComplete) 30 | } 31 | 32 | private func makeRequest(url: URL, onComplete: @escaping OnComplete) -> Cancellable { 33 | let request = URLRequestFactory.make(.POST, url) 34 | return DataTaskRunner.run(with: urlSession, request: request, onComplete: onComplete) 35 | } 36 | 37 | private func makeRequest(url: URL, body: U, 38 | onComplete: @escaping OnComplete) -> Cancellable { 39 | var request = URLRequestFactory.make(.POST, url) 40 | do { 41 | let bodyObject = try bodyAdapter.build(withBody: body) 42 | request.httpBody = try JSONSerialization.data(withJSONObject: bodyObject, options: []) 43 | } catch { 44 | onComplete(.fail(FrisbeeError(error))) 45 | return NilCancellable() 46 | } 47 | 48 | return DataTaskRunner.run(with: urlSession, request: request, onComplete: onComplete) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Entities/FrisbeeErrorTests.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XCTest 3 | @testable import Frisbee 4 | 5 | class FrisbeeErrorTests: XCTestCase { 6 | 7 | func testEquatableFromAllPossibleErrorsThenBeEqual() { 8 | let all = FrisbeeError.all(error: SomeError.some) 9 | 10 | zip(all, all).forEach { lhs, rhs in 11 | XCTAssertEqual(lhs, rhs) 12 | } 13 | } 14 | 15 | func testInequatableFromAllPossibleErrorsThenBeNotEqual() { 16 | let all = FrisbeeError.all(error: SomeError.some).enumerated() 17 | 18 | all.extensiveCombine(all).forEach { lhs, rhs in 19 | if lhs.offset == rhs.offset { //diagonal cases 20 | XCTAssertEqual(lhs.element, rhs.element) 21 | } else { 22 | XCTAssertNotEqual(lhs.element, rhs.element) 23 | } 24 | } 25 | } 26 | 27 | func testInitFromInvalidURLErrorThenCreateFrisbeErrorInvalidURL() { 28 | let error: Error = FrisbeeError.invalidUrl 29 | 30 | let frisbeeError = FrisbeeError(error) 31 | 32 | XCTAssertEqual(frisbeeError, .invalidUrl) 33 | } 34 | 35 | func testInitFromUnkownErrorThenCreateFrisbeErrorOtherWithLocalizedString() { 36 | let error: Error = NSError(domain: "domain", code: 0) 37 | 38 | let frisbeeError = FrisbeeError(error) 39 | 40 | XCTAssertEqual(frisbeeError, .other(localizedDescription: error.localizedDescription)) 41 | } 42 | 43 | func testInitWhenURLErrorCanceledThenCreateRequestCancelled() { 44 | let error = NSError(domain: NSURLErrorDomain, code: URLError.cancelled.rawValue, userInfo: [:]) 45 | 46 | let frisbeeError = FrisbeeError(error) 47 | 48 | XCTAssertEqual(frisbeeError, .requestCancelled) 49 | } 50 | 51 | static var allTests = [ 52 | ("testEquatableFromAllPossibleErrorsThenBeEqual", 53 | testEquatableFromAllPossibleErrorsThenBeEqual), 54 | ("testInequatableFromAllPossibleErrorsThenBeNotEqual", 55 | testInequatableFromAllPossibleErrorsThenBeNotEqual), 56 | ("testInitFromUnkownErrorThenCreateFrisbeErrorOtherWithLocalizedString", 57 | testInitFromUnkownErrorThenCreateFrisbeErrorOtherWithLocalizedString) 58 | ] 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/IntegrationNetworkPutTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Frisbee 3 | 4 | final class IntegrationNetworkPutTests: XCTestCase { 5 | 6 | struct Json: Codable { 7 | let name: String? 8 | let job: String? 9 | } 10 | 11 | struct ReqresId: Codable { 12 | let name: String? 13 | let job: String? 14 | let updatedAt: String? 15 | } 16 | 17 | func testPutWhenHasValidURLThenRequestAndTransformData() { 18 | let url = URL(string: "https://reqres.in/api/users")! 19 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 20 | var returnedData: ReqresId? 21 | 22 | NetworkPut().put(url: url) { (result: Result) in 23 | returnedData = result.data 24 | longRunningExpectation.fulfill() 25 | } 26 | 27 | waitForExpectations(timeout: 20) { expectationError in 28 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 29 | XCTAssertNotNil(returnedData?.updatedAt) 30 | } 31 | } 32 | 33 | func testPutWhenHasValidURLAndWithBodyThenRequestAndTransformData() { 34 | let url = URL(string: "https://reqres.in/api/users/2")! 35 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 36 | var returnedData: ReqresId? 37 | let body = Json(name: "morpheus", job: "zion resident") 38 | 39 | NetworkPut().put(url: url, body: body) { (result: Result) in 40 | returnedData = result.data 41 | longRunningExpectation.fulfill() 42 | } 43 | 44 | waitForExpectations(timeout: 20) { expectationError in 45 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 46 | XCTAssertEqual(returnedData?.name, body.name) 47 | XCTAssertEqual(returnedData?.job, body.job) 48 | } 49 | } 50 | 51 | static var allTests = [ 52 | ("testPutWhenHasValidURLThenRequestAndTransformData", 53 | testPutWhenHasValidURLThenRequestAndTransformData), 54 | ("testPutWhenHasValidURLAndWithBodyThenRequestAndTransformData", 55 | testPutWhenHasValidURLAndWithBodyThenRequestAndTransformData) 56 | ] 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/IntegrationNetworkPostTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Frisbee 3 | 4 | final class IntegrationNetworkPostTests: XCTestCase { 5 | 6 | struct Json: Codable { 7 | let id: Int? // swiftlint:disable:this identifier_name 8 | let content: String? 9 | let url: String? 10 | } 11 | 12 | struct ReqresId: Codable { 13 | let id: String // swiftlint:disable:this identifier_name 14 | } 15 | 16 | func testPostWhenHasValidURLThenRequestAndTransformData() { 17 | let url = URL(string: "https://reqres.in/api/users")! 18 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 19 | var returnedData: ReqresId? 20 | 21 | NetworkPost().post(url: url) { (result: Result) in 22 | returnedData = result.data 23 | longRunningExpectation.fulfill() 24 | } 25 | 26 | waitForExpectations(timeout: 20) { expectationError in 27 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 28 | XCTAssertNotNil(returnedData?.id) 29 | } 30 | } 31 | 32 | func testPostWhenHasValidURLAndWithBodyThenRequestAndTransformData() { 33 | let url = URL(string: "https://reqres.in/api/users")! 34 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 35 | var returnedData: Json? 36 | let body = Json(id: 123, content: #function, url: url.absoluteString) 37 | 38 | NetworkPost().post(url: url, body: body) { (result: Result) in 39 | returnedData = result.data 40 | longRunningExpectation.fulfill() 41 | } 42 | 43 | waitForExpectations(timeout: 20) { expectationError in 44 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 45 | XCTAssertEqual(returnedData?.id, body.id) 46 | XCTAssertEqual(returnedData?.url, body.url) 47 | XCTAssertEqual(returnedData?.content, body.content) 48 | } 49 | } 50 | 51 | static var allTests = [ 52 | ("testPostWhenHasValidURLThenRequestAndTransformData", 53 | testPostWhenHasValidURLThenRequestAndTransformData), 54 | ("testPostWhenHasValidURLAndWithBodyThenRequestAndTransformData", 55 | testPostWhenHasValidURLAndWithBodyThenRequestAndTransformData) 56 | ] 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/xcshareddata/xcschemes/Frisbee-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 58 | 59 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/NetworkGetTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class NetworkGetTests: XCTestCase { 5 | 6 | private let invalidUrlString = "🤷‍♂️" 7 | private let validUrlString = "http://www.com.br" 8 | 9 | #if !os(Linux) 10 | 11 | func testGetWhenThrowsAnErrorAtQueryAdapterThenGenerateFailResult() { 12 | let session = MockURLSession(results: [.error(SomeError.some)]) 13 | let urlQueryAdapter = URLWithQueryTrhrowErrorFakeBuildable() 14 | urlQueryAdapter.errorToThrow = .invalidEntity 15 | let query = Empty() 16 | let getter = NetworkGet(queryAdapter: urlQueryAdapter, urlSession: session) 17 | var generatedResult: Result! 18 | 19 | let cancellabe = getter.get(url: validUrlString, query: query) { generatedResult = $0 } 20 | //run NillCancellable 21 | cancellabe.cancel() 22 | 23 | XCTAssertEqual(generatedResult.error, .invalidEntity) 24 | XCTAssertNil(generatedResult.data) 25 | } 26 | 27 | func testInitWithCustomUrlSessionThenKeepSameReferenceOfUrlSession() { 28 | let urlSession = URLSession(configuration: .default) 29 | let networkGetter = NetworkGet(urlSession: urlSession) 30 | 31 | XCTAssertEqual(urlSession, networkGetter.urlSession) 32 | } 33 | 34 | func testGetWhenURLStringIsInvalidFormatThenExecuteCompletionHandlerWithInvalidURLError() { 35 | var generatedResult: Result! 36 | 37 | NetworkGet().get(url: invalidUrlString) { generatedResult = $0 } 38 | 39 | XCTAssertEqual(generatedResult, Result.fail(.invalidUrl)) 40 | } 41 | 42 | func testGetWhenValidURLThenGenerateSuccessResult() { 43 | let session = MockURLSession(results: [.success(Empty.data, URLResponse())]) 44 | let getter = NetworkGet(urlSession: session) 45 | var generatedResult: Result! 46 | 47 | getter.get(url: validUrlString) { generatedResult = $0 } 48 | 49 | XCTAssertEqual(generatedResult.data, Empty()) 50 | XCTAssertNil(generatedResult.error) 51 | } 52 | 53 | func testGetWhenInvalidURLThenGenerateFailResult() { 54 | let session = MockURLSession(results: []) 55 | let getter = NetworkGet(urlSession: session) 56 | var generatedResult: Result! 57 | 58 | getter.get(url: invalidUrlString) { generatedResult = $0 } 59 | 60 | XCTAssertEqual(generatedResult.error, .invalidUrl) 61 | XCTAssertNil(generatedResult.data) 62 | } 63 | 64 | func testGetWithQueryWhenInvalidURLThenGenerateFailResult() { 65 | let session = MockURLSession(results: []) 66 | let getter = NetworkGet(urlSession: session) 67 | let query = Empty() 68 | var generatedResult: Result! 69 | 70 | getter.get(url: invalidUrlString, query: query) { generatedResult = $0 } 71 | 72 | XCTAssertEqual(generatedResult.error, .invalidUrl) 73 | XCTAssertNil(generatedResult.data) 74 | } 75 | 76 | func testGetWhenValidURLAndRequestFailsThenGenerateFailResult() { 77 | let session = MockURLSession(results: [.error(SomeError.some)]) 78 | let getter = NetworkGet(urlSession: session) 79 | var generatedResult: Result! 80 | 81 | getter.get(url: validUrlString) { generatedResult = $0 } 82 | 83 | XCTAssertEqual(generatedResult.error, .other(localizedDescription: SomeError.some.localizedDescription)) 84 | XCTAssertNil(generatedResult.data) 85 | } 86 | #endif 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/NetworkPutTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | class NetworkPutTests: XCTestCase { 5 | 6 | private let invalidUrlString = "🤷‍♂️" 7 | private let validUrlString = "http://www.com.br" 8 | 9 | #if !os(Linux) 10 | func testInitWithCustomUrlSessionThenKeepSameReferenceOfUrlSession() { 11 | let urlSession = URLSession(configuration: .default) 12 | let networkPut = NetworkPut(urlSession: urlSession) 13 | 14 | XCTAssertEqual(urlSession, networkPut.urlSession) 15 | } 16 | 17 | func testPutWhenURLStringIsInvalidFormatThenExecuteCompletionHandlerWithInvalidURLError() { 18 | var generatedResult: Result! 19 | 20 | NetworkPut().put(url: invalidUrlString) { generatedResult = $0 } 21 | 22 | XCTAssertEqual(generatedResult, Result.fail(.invalidUrl)) 23 | } 24 | 25 | func testPutWhenValidURLThenGenerateSuccessResult() { 26 | let session = MockURLSession(results: [.success(Empty.data, URLResponse())]) 27 | let networkPut = NetworkPut(urlSession: session) 28 | var generatedResult: Result! 29 | 30 | networkPut.put(url: validUrlString) { generatedResult = $0 } 31 | 32 | XCTAssertEqual(generatedResult.data, Empty()) 33 | XCTAssertNil(generatedResult.error) 34 | } 35 | 36 | func testPutWhenValidURLWithBodyThenGenerateSuccessResult() { 37 | let session = MockURLSession(results: [.success(Empty.data, URLResponse())]) 38 | let networkPut = NetworkPut(urlSession: session) 39 | let body = Empty() 40 | var generatedResult: Result! 41 | 42 | networkPut.put(url: validUrlString, body: body) { generatedResult = $0 } 43 | 44 | XCTAssertEqual(generatedResult.data, Empty()) 45 | XCTAssertNil(generatedResult.error) 46 | } 47 | 48 | func testPutWhenInvalidURLThenGenerateFailResult() { 49 | let session = MockURLSession(results: []) 50 | let networkPut = NetworkPut(urlSession: session) 51 | var generatedResult: Result! 52 | 53 | networkPut.put(url: invalidUrlString) { generatedResult = $0 } 54 | 55 | XCTAssertEqual(generatedResult.error, .invalidUrl) 56 | XCTAssertNil(generatedResult.data) 57 | } 58 | 59 | func testPutWithBodyWhenInvalidURLThenGenerateFailResult() { 60 | let session = MockURLSession(results: []) 61 | let networkPut = NetworkPut(urlSession: session) 62 | let body = Empty() 63 | var generatedResult: Result! 64 | 65 | networkPut.put(url: invalidUrlString, body: body) { generatedResult = $0 } 66 | 67 | XCTAssertEqual(generatedResult.error, .invalidUrl) 68 | XCTAssertNil(generatedResult.data) 69 | } 70 | 71 | func testPutWithBodyWhenBodyAdapterThrowAnErrorThenGenerateFailResult() { 72 | let session = MockURLSession(results: []) 73 | let networkPut = NetworkPut(urlSession: session, bodyAdapter: BodyThrowErrorFakeAdapter()) 74 | let body = Empty() 75 | var generatedResult: Result! 76 | 77 | networkPut.put(url: validUrlString, body: body) { generatedResult = $0 } 78 | 79 | XCTAssertEqual(generatedResult.error, .invalidEntity) 80 | XCTAssertNil(generatedResult.data) 81 | } 82 | 83 | func testPutWhenValidURLAndRequestFailsThenGenerateFailResult() { 84 | let session = MockURLSession(results: [.error(SomeError.some)]) 85 | let networkPut = NetworkPut(urlSession: session) 86 | var generatedResult: Result! 87 | 88 | networkPut.put(url: validUrlString) { generatedResult = $0 } 89 | 90 | XCTAssertEqual(generatedResult.error, .other(localizedDescription: SomeError.some.localizedDescription)) 91 | XCTAssertNil(generatedResult.data) 92 | } 93 | #endif 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/NetworkPostTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class NetworkPostTests: XCTestCase { 5 | 6 | private let invalidUrlString = "🤷‍♂️" 7 | private let validUrlString = "http://www.com.br" 8 | 9 | #if !os(Linux) 10 | func testInitWithCustomUrlSessionThenKeepSameReferenceOfUrlSession() { 11 | let urlSession = URLSession(configuration: .default) 12 | let networkPost = NetworkPost(urlSession: urlSession) 13 | 14 | XCTAssertEqual(urlSession, networkPost.urlSession) 15 | } 16 | 17 | func testPostWhenURLStringIsInvalidFormatThenExecuteCompletionHandlerWithInvalidURLError() { 18 | var generatedResult: Result! 19 | 20 | NetworkPost().post(url: invalidUrlString) { generatedResult = $0 } 21 | 22 | XCTAssertEqual(generatedResult, Result.fail(.invalidUrl)) 23 | } 24 | 25 | func testPostWhenValidURLThenGenerateSuccessResult() { 26 | let session = MockURLSession(results: [.success(Empty.data, URLResponse())]) 27 | let networkPost = NetworkPost(urlSession: session) 28 | var generatedResult: Result! 29 | 30 | networkPost.post(url: validUrlString) { generatedResult = $0 } 31 | 32 | XCTAssertEqual(generatedResult.data, Empty()) 33 | XCTAssertNil(generatedResult.error) 34 | } 35 | 36 | func testPostWhenValidURLWithBodyThenGenerateSuccessResult() { 37 | let session = MockURLSession(results: [.success(Empty.data, URLResponse())]) 38 | let networkPost = NetworkPost(urlSession: session) 39 | let body = Empty() 40 | var generatedResult: Result! 41 | 42 | networkPost.post(url: validUrlString, body: body) { generatedResult = $0 } 43 | 44 | XCTAssertEqual(generatedResult.data, Empty()) 45 | XCTAssertNil(generatedResult.error) 46 | } 47 | 48 | func testPostWhenInvalidURLThenGenerateFailResult() { 49 | let session = MockURLSession(results: []) 50 | let networkPost = NetworkPost(urlSession: session) 51 | var generatedResult: Result! 52 | 53 | networkPost.post(url: invalidUrlString) { generatedResult = $0 } 54 | 55 | XCTAssertEqual(generatedResult.error, .invalidUrl) 56 | XCTAssertNil(generatedResult.data) 57 | } 58 | 59 | func testPostWithBodyWhenInvalidURLThenGenerateFailResult() { 60 | let session = MockURLSession(results: []) 61 | let networkPost = NetworkPost(urlSession: session) 62 | let body = Empty() 63 | var generatedResult: Result! 64 | 65 | networkPost.post(url: invalidUrlString, body: body) { generatedResult = $0 } 66 | 67 | XCTAssertEqual(generatedResult.error, .invalidUrl) 68 | XCTAssertNil(generatedResult.data) 69 | } 70 | 71 | func testPostWithBodyWhenBodyAdapterThrowAnErrorThenGenerateFailResult() { 72 | let session = MockURLSession(results: []) 73 | let networkPost = NetworkPost(urlSession: session, bodyAdapter: BodyThrowErrorFakeAdapter()) 74 | let body = Empty() 75 | var generatedResult: Result! 76 | 77 | networkPost.post(url: validUrlString, body: body) { generatedResult = $0 } 78 | 79 | XCTAssertEqual(generatedResult.error, .invalidEntity) 80 | XCTAssertNil(generatedResult.data) 81 | } 82 | 83 | func testPostWhenValidURLAndRequestFailsThenGenerateFailResult() { 84 | let session = MockURLSession(results: [.error(SomeError.some)]) 85 | let networkPost = NetworkPost(urlSession: session) 86 | var generatedResult: Result! 87 | 88 | networkPost.post(url: validUrlString) { generatedResult = $0 } 89 | 90 | XCTAssertEqual(generatedResult.error, .other(localizedDescription: SomeError.some.localizedDescription)) 91 | XCTAssertNil(generatedResult.data) 92 | } 93 | #endif 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Interactors/ResultGeneratorTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class ResultGeneratorTests: XCTestCase { 5 | 6 | private let someError = NSError(domain: "Some error", code: 33, userInfo: nil) 7 | private let fakeString = "Fake Fake" 8 | 9 | func testGenerateResultWhenEncoderTrhowAnerrorThenGenerateFailResult() { 10 | let data = try? JSONEncoder().encode(Fake(fake: fakeString)) 11 | let resultGenerator = ResultGenerator(decoder: DecoderThrowErrorFakeAdapter()) 12 | 13 | let result = resultGenerator.generate(data: data, error: nil) 14 | 15 | XCTAssertEqual(result.error, .noData) 16 | } 17 | 18 | func testGenerateResultWhenInvalidDataThenGenerateSuccessResult() { 19 | let noDataError = FrisbeeError.noData 20 | let data = try? JSONEncoder().encode(Data()) 21 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 22 | 23 | let result = resultGenerator.generate(data: data, error: nil) 24 | 25 | XCTAssertEqual(result.error, noDataError) 26 | } 27 | 28 | func testGenerateResultWhenHasDataThenGenerateSuccessResult() { 29 | let data = try? JSONEncoder().encode(Fake(fake: fakeString)) 30 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 31 | 32 | let result = resultGenerator.generate(data: data, error: nil) 33 | 34 | XCTAssertEqual(result.data?.fake, fakeString) 35 | } 36 | 37 | func testGenerateResultWhenHasDataThenResultFailErrorIsNil() { 38 | let data = try? JSONEncoder().encode(Fake(fake: fakeString)) 39 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 40 | 41 | let result = resultGenerator.generate(data: data, error: nil) 42 | 43 | XCTAssertNil(result.error) 44 | } 45 | 46 | func testGenerateResultWhenHasNilDataThenGenerateNoDataErrorResult() { 47 | let noDataError = Result.fail(FrisbeeError.noData) 48 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 49 | 50 | let result = resultGenerator.generate(data: nil, error: nil) 51 | 52 | XCTAssertEqual(result, noDataError) 53 | } 54 | 55 | func testGenerateResultWhenHasNilDataThenResultSuccessDataIsNil() { 56 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 57 | 58 | let result = resultGenerator.generate(data: nil, error: nil) 59 | 60 | XCTAssertNil(result.data) 61 | } 62 | 63 | func testGenerateResultWhenHasErrorThenGenerateErrorResult() { 64 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 65 | let noDataError = FrisbeeError.other(localizedDescription: someError.localizedDescription) 66 | 67 | let result = resultGenerator.generate(data: nil, error: someError) 68 | 69 | XCTAssertEqual(result.error, noDataError) 70 | } 71 | 72 | func testGenerateResultWhenHasErrorThenResultSuccessDataIsNil() { 73 | let resultGenerator: ResultGenerator = ResultGeneratorFactory.make() 74 | 75 | let result = resultGenerator.generate(data: nil, error: someError) 76 | 77 | XCTAssertNil(result.data) 78 | } 79 | 80 | static var allTests = [ 81 | ("testGenerateResultWhenEncoderTrhowAnerrorThenGenerateFailResult", 82 | testGenerateResultWhenEncoderTrhowAnerrorThenGenerateFailResult), 83 | ("testGenerateResultWhenHasDataThenGenerateSuccessResult", 84 | testGenerateResultWhenHasDataThenGenerateSuccessResult), 85 | ("testGenerateResultWhenHasDataThenResultFailErrorIsNil", 86 | testGenerateResultWhenHasDataThenResultFailErrorIsNil), 87 | ("testGenerateResultWhenHasNilDataThenGenerateNoDataErrorResult", 88 | testGenerateResultWhenHasNilDataThenGenerateNoDataErrorResult), 89 | ("testGenerateResultWhenHasNilDataThenResultSuccessDataIsNil", 90 | testGenerateResultWhenHasNilDataThenResultSuccessDataIsNil), 91 | ("testGenerateResultWhenHasErrorThenGenerateErrorResult", 92 | testGenerateResultWhenHasErrorThenGenerateErrorResult), 93 | ("testGenerateResultWhenHasErrorThenResultSuccessDataIsNil", 94 | testGenerateResultWhenHasErrorThenResultSuccessDataIsNil) 95 | ] 96 | 97 | } 98 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Requestables/IntegrationNetworkGetTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | final class IntegrationNetworkGetTests: XCTestCase { 5 | 6 | private let url = "https://gist.githubusercontent.com/ronanrodrigo" + 7 | "/fbb32cee20e43f0f9972c9a3230ef93d/raw/2cb87fd40e65fadb25fcc30f643d385bccdb5f7c/movie.json" 8 | 9 | struct Movie: Decodable { 10 | let name: String 11 | } 12 | 13 | func testGetWhenHasValidURLWithValidEntityThenRequestAndTransformData() { 14 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 15 | let expectedMovieName = "Ghostbusters" 16 | var returnedData: Movie? 17 | 18 | NetworkGet().get(url: url) { (result: Result) in 19 | returnedData = result.data 20 | longRunningExpectation.fulfill() 21 | } 22 | 23 | waitForExpectations(timeout: 20) { expectationError in 24 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 25 | XCTAssertEqual(returnedData?.name, expectedMovieName) 26 | } 27 | } 28 | 29 | func testGetWhenHasQueryParamentersAndValidURLWithValidEntityThenRequestAndTransformData() { 30 | let longRunningExpectation = expectation(description: "RequestMoviesWithSuccess") 31 | let expectedMovieName = "Ghostbusters" 32 | var returnedData: Movie? 33 | let query = MovieQuery(page: 10) 34 | 35 | NetworkGet().get(url: url, query: query) { (result: Result) in 36 | returnedData = result.data 37 | longRunningExpectation.fulfill() 38 | } 39 | 40 | waitForExpectations(timeout: 20) { expectationError in 41 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 42 | XCTAssertEqual(returnedData?.name, expectedMovieName) 43 | } 44 | } 45 | 46 | func testGetURLWhenHasValidURLWithValidEntityThenRequestAndTransformData() { 47 | let expectation = self.expectation(description: "RequestMoviesWithSuccess") 48 | let expectedMovieName = "Ghostbusters" 49 | var returnedData: Movie? 50 | 51 | guard let url = URL(string: self.url) else { 52 | return XCTFail("Could not create URL") 53 | } 54 | 55 | NetworkGet().get(url: url) { (result: Result) in 56 | returnedData = result.data 57 | expectation.fulfill() 58 | } 59 | 60 | waitForExpectations(timeout: 20) { expectationError in 61 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 62 | XCTAssertEqual(returnedData?.name, expectedMovieName) 63 | } 64 | } 65 | 66 | func testGetURLWhenHasQueryParametersAndValidURLWithValidEntityThenRequestAndTransformData() { 67 | let expectation = self.expectation(description: "RequestMoviesWithSuccess") 68 | let expectedMovieName = "Ghostbusters" 69 | var returnedData: Movie? 70 | let query = MovieQuery(page: 10) 71 | 72 | guard let url = URL(string: self.url) else { 73 | return XCTFail("Could not create URL") 74 | } 75 | 76 | NetworkGet().get(url: url, query: query) { (result: Result) in 77 | returnedData = result.data 78 | expectation.fulfill() 79 | } 80 | 81 | waitForExpectations(timeout: 20) { expectationError in 82 | XCTAssertNil(expectationError, expectationError!.localizedDescription) 83 | XCTAssertEqual(returnedData?.name, expectedMovieName) 84 | } 85 | } 86 | 87 | static var allTests = [ 88 | ("testGetWhenHasValidURLWithValidEntityThenRequestAndTransformData", 89 | testGetWhenHasValidURLWithValidEntityThenRequestAndTransformData), 90 | ("testGetWhenHasQueryParamentersAndValidURLWithValidEntityThenRequestAndTransformData", 91 | testGetWhenHasQueryParamentersAndValidURLWithValidEntityThenRequestAndTransformData), 92 | ("testGetURLWhenHasValidURLWithValidEntityThenRequestAndTransformData", 93 | testGetURLWhenHasValidURLWithValidEntityThenRequestAndTransformData), 94 | ("testGetURLWhenHasQueryParametersAndValidURLWithValidEntityThenRequestAndTransformData", 95 | testGetURLWhenHasQueryParametersAndValidURLWithValidEntityThenRequestAndTransformData) 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /Tests/FrisbeeTests/Adapters/URLQueryAdapterTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Frisbee 3 | 4 | class URLQueryAdapterTests: XCTestCase { 5 | 6 | private var urlWithQueryAdapter: URLQueriableAdapter! 7 | 8 | override func setUp() { 9 | super.setUp() 10 | urlWithQueryAdapter = URLQueriableAdapterFactory.make() 11 | } 12 | 13 | struct MovieQuery: Encodable { 14 | let page: Int 15 | let keyAccess: String 16 | let optionalInt: Int? 17 | 18 | // swiftlint:disable:next nesting 19 | enum CodingKeys: String, CodingKey { 20 | case page, keyAccess = "key_access", optionalInt = "optional_int" 21 | } 22 | } 23 | 24 | func testBuildURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings() { 25 | let query = MovieQuery(page: 1, keyAccess: "a1d13so979", optionalInt: nil) 26 | guard let url = URL(string: "http://www.com.br") else { 27 | return XCTFail("Invalid URL") 28 | } 29 | 30 | let builtUrl = try? urlWithQueryAdapter.build(withUrl: url, query: query) 31 | 32 | XCTAssertTrue(builtUrl?.absoluteString.starts(with: url.absoluteString) ?? false) 33 | XCTAssertEqual("\(query.page)", getQueryValue(builtUrl?.absoluteString, "page")) 34 | XCTAssertEqual("\(query.keyAccess)", getQueryValue(builtUrl?.absoluteString, "key_access")) 35 | XCTAssertNil(getQueryValue(builtUrl?.absoluteString, "optional_int")) 36 | } 37 | 38 | func testBuildStringURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings() { 39 | let query = MovieQuery(page: 1, keyAccess: "a1d13so979", optionalInt: nil) 40 | let url = "http://www.com.br" 41 | 42 | let builtUrl = try? urlWithQueryAdapter.build(withUrl: url, query: query) 43 | 44 | XCTAssertTrue(builtUrl?.absoluteString.starts(with: url) ?? false) 45 | XCTAssertEqual("\(query.page)", getQueryValue(builtUrl?.absoluteString, "page")) 46 | XCTAssertEqual("\(query.keyAccess)", getQueryValue(builtUrl?.absoluteString, "key_access")) 47 | XCTAssertNil(getQueryValue(builtUrl?.absoluteString, "optional_int")) 48 | } 49 | 50 | func testBuildURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings() { 51 | let query = MovieQuery(page: 1, keyAccess: "a1d13so979", optionalInt: 10) 52 | guard let url = URL(string: "http://www.com.br") else { 53 | return XCTFail("Invalid URL") 54 | } 55 | 56 | let builtUrl = try? urlWithQueryAdapter.build(withUrl: url, query: query) 57 | 58 | XCTAssertTrue(builtUrl?.absoluteString.starts(with: url.absoluteString) ?? false) 59 | XCTAssertEqual("\(query.page)", getQueryValue(builtUrl?.absoluteString, "page")) 60 | XCTAssertEqual("\(query.keyAccess)", getQueryValue(builtUrl?.absoluteString, "key_access")) 61 | XCTAssertEqual("\(query.optionalInt ?? 0)", getQueryValue(builtUrl?.absoluteString, "optional_int")) 62 | } 63 | 64 | func testBuildStringURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings() { 65 | let query = MovieQuery(page: 1, keyAccess: "a1d13so979", optionalInt: 10) 66 | let url = "http://www.com.br" 67 | 68 | let builtUrl = try? urlWithQueryAdapter.build(withUrl: url, query: query) 69 | 70 | XCTAssertTrue(builtUrl?.absoluteString.starts(with: url) ?? false) 71 | XCTAssertEqual("\(query.page)", getQueryValue(builtUrl?.absoluteString, "page")) 72 | XCTAssertEqual("\(query.keyAccess)", getQueryValue(builtUrl?.absoluteString, "key_access")) 73 | XCTAssertEqual("\(query.optionalInt ?? 0)", getQueryValue(builtUrl?.absoluteString, "optional_int")) 74 | } 75 | 76 | func testBuildStringURLWhenHasInvalidUrlThenThrowInvalidURLError() { 77 | let query = MovieQuery(page: 1, keyAccess: "a1d13so979", optionalInt: 10) 78 | let url = "http://www.çøµ.∫®" 79 | 80 | XCTAssertThrowsError(try urlWithQueryAdapter.build(withUrl: url, query: query)) { 81 | XCTAssertEqual($0 as? FrisbeeError, FrisbeeError.invalidUrl) 82 | } 83 | } 84 | 85 | private func getQueryValue(_ urlString: String?, _ param: String) -> String? { 86 | guard let urlString = urlString, let url = URLComponents(string: urlString) else { return nil } 87 | return url.queryItems?.first(where: { $0.name == param })?.value 88 | } 89 | 90 | static var allTests = [ 91 | ("testBuildURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings", 92 | testBuildURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings), 93 | ("testBuildStringURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings", 94 | testBuildStringURLWhenHasCorrectQueryWithNilValueThenGenerateURLWithQueryStrings), 95 | ("testBuildURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings", 96 | testBuildURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings), 97 | ("testBuildStringURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings", 98 | testBuildStringURLWhenHasCorrectQueryWithoutNilValueThenGenerateURLWithQueryStrings), 99 | ("testBuildStringURLWhenHasInvalidUrlThenThrowInvalidURLError", 100 | testBuildStringURLWhenHasInvalidUrlThenThrowInvalidURLError) 101 | ] 102 | 103 | } 104 | -------------------------------------------------------------------------------- /bitrise.yml: -------------------------------------------------------------------------------- 1 | format_version: "4" 2 | default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git 3 | project_type: macos 4 | app: 5 | envs: 6 | - BITRISE_PROJECT_PATH: Frisbee.xcodeproj 7 | opts: 8 | is_expand: false 9 | - BITRISE_SCHEME: Frisbee-Package 10 | opts: 11 | is_expand: false 12 | - BITRISE_EXPORT_METHOD: none 13 | opts: 14 | is_expand: false 15 | trigger_map: 16 | - push_branch: '*' 17 | workflow: primary 18 | - pull_request_source_branch: '*' 19 | workflow: primary 20 | workflows: 21 | _start: 22 | steps: 23 | - script: 24 | title: Swift Tests - Without Coverage! 25 | inputs: 26 | - content: swift test --parallel 27 | _submit: 28 | steps: 29 | - script@1.1.5: 30 | title: Commit Frisbee.plist 31 | inputs: 32 | - content: |- 33 | git add Frisbee.plist 34 | git commit -m "Update Frisbee.plist to $FRISBEE_VERSION [skip ci]" 35 | - script@1.1.5: 36 | title: Update README.md 37 | inputs: 38 | - content: |- 39 | file=README.md 40 | 41 | old_value="[0-9]+\.[0-9]+\.[0-9]+" 42 | 43 | sed -E "s/$old_value/$FRISBEE_VERSION/g" "$file" > "$file.new" && mv "$file.new" "$file" 44 | 45 | git add README.md 46 | git commit -m "Update README.md to reference $FRISBEE_VERSION [skip ci]" 47 | - script: 48 | title: Create Release 49 | inputs: 50 | - is_debug: "yes" 51 | - content: |- 52 | curl -X "POST" "https://api.github.com/repos/ronanrodrigo/Frisbee/releases" \ 53 | -H 'Content-Type: application/json' \ 54 | -u "ronanrodrigo:${GITHUB_OAUTH_TOKEN}" \ 55 | -d "{\"body\": \"Description of the release\", \"draft\": false, \"tag_name\": \"${FRISBEE_VERSION}\", \"target_commitish\": \"master\", \"name\": \"${FRISBEE_VERSION}\", \"prerelease\": false}" 56 | - cache-pull: {} 57 | - script: 58 | title: Submit Cocoapod 59 | inputs: 60 | - content: |- 61 | cat > Frisbee.podspec <<- EOM 62 | Pod::Spec.new do |s| 63 | s.name = "Frisbee" 64 | s.version = "$FRISBEE_VERSION" 65 | s.summary = "Another network wrapper for URLSession" 66 | s.description = "Built to make it easy to create tests for your application's network layer." 67 | s.homepage = "https://github.com/ronanrodrigo/Frisbee" 68 | s.license = {:type => "MIT", :file => "LICENSE"} 69 | s.author = {"Ronan Rodrigo Nunes" => "ronan.nunes@me.com"} 70 | s.social_media_url = "https://twitter.com/ronanrodrigo_" 71 | s.ios.deployment_target = "10.0" 72 | s.osx.deployment_target = "10.13" 73 | s.source = { :git => "https://github.com/ronanrodrigo/Frisbee.git", :tag => "#{s.version}" } 74 | s.source_files = "Sources/**/*.{swift}" 75 | s.exclude_files = "Tests" 76 | end 77 | EOM 78 | 79 | bundle install 80 | 81 | bundle exec pod trunk push Frisbee.podspec 82 | 83 | git add Frisbee.podspec 84 | git commit -m "Update Frisbee.podspec to $FRISBEE_VERSION [skip ci]" 85 | - script: 86 | title: Set GEM_HOME env var 87 | inputs: 88 | - content: |- 89 | #!/bin/bash 90 | set -ev 91 | envman add --key GEM_HOME --value "$(gem environment gemdir)" 92 | - cache-push: 93 | inputs: 94 | - cache_paths: $GEM_HOME 95 | - script: 96 | title: Push to Master 97 | inputs: 98 | - content: git push origin master 99 | - script: 100 | title: Push Notification 101 | inputs: 102 | - content: 'curl -X POST -H "Content-Type: application/json" -d "{\"value1\":\"${FRISBEE_VERSION}\"}" 103 | https://maker.ifttt.com/trigger/frisbee_deployed/with/key/$IFTTT_TOKEN' 104 | deploy-major: 105 | before_run: 106 | - _start 107 | after_run: 108 | - _submit 109 | steps: 110 | - script: 111 | title: Update version Frisbee.plist 112 | inputs: 113 | - content: |- 114 | minor_version=0 115 | patch_version=0 116 | 117 | current_major_version=$( /usr/libexec/PlistBuddy -c "Print :Major" Frisbee.plist ) 118 | major_version=$(( $current_major_version + 1 )) 119 | /usr/libexec/PlistBuddy -c "Set :Major ${major_version}" Frisbee.plist 120 | /usr/libexec/PlistBuddy -c "Set :Minor ${minor_version}" Frisbee.plist 121 | /usr/libexec/PlistBuddy -c "Set :Patch ${patch_version}" Frisbee.plist 122 | 123 | envman add --key FRISBEE_VERSION --value "${major_version}.${minor_version}.${patch_version}" 124 | deploy-minor: 125 | before_run: 126 | - _start 127 | after_run: 128 | - _submit 129 | steps: 130 | - script: 131 | title: Update version Frisbee.plist 132 | inputs: 133 | - content: |- 134 | major_version=$( /usr/libexec/PlistBuddy -c "Print :Major" Frisbee.plist ) 135 | patch_version=0 136 | 137 | current_minor_version=$( /usr/libexec/PlistBuddy -c "Print :Minor" Frisbee.plist ) 138 | minor_version=$(( $current_minor_version + 1 )) 139 | /usr/libexec/PlistBuddy -c "Set :Minor ${minor_version}" Frisbee.plist 140 | /usr/libexec/PlistBuddy -c "Set :Patch ${patch_version}" Frisbee.plist 141 | 142 | envman add --key FRISBEE_VERSION --value "${major_version}.${minor_version}.${patch_version}" 143 | deploy-patch: 144 | before_run: 145 | - _start 146 | after_run: 147 | - _submit 148 | steps: 149 | - script: 150 | title: Update version Frisbee.plist 151 | inputs: 152 | - content: |- 153 | major_version=$( /usr/libexec/PlistBuddy -c "Print :Major" Frisbee.plist ) 154 | minor_version=$( /usr/libexec/PlistBuddy -c "Print :Minor" Frisbee.plist ) 155 | 156 | current_patch_version=$( /usr/libexec/PlistBuddy -c "Print :Patch" Frisbee.plist ) 157 | patch_version=$(( $current_patch_version + 1 )) 158 | /usr/libexec/PlistBuddy -c "Set :Patch ${patch_version}" Frisbee.plist 159 | 160 | envman add --key FRISBEE_VERSION --value "${major_version}.${minor_version}.${patch_version}" 161 | primary: 162 | steps: 163 | - xcode-test-mac: 164 | inputs: 165 | - project_path: $BITRISE_PROJECT_PATH 166 | - scheme: $BITRISE_SCHEME 167 | - codecov: 168 | inputs: 169 | - CODECOV_TOKEN: $CODECOV_TOKEN 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://i.imgur.com/67a4vkG.png) 2 | 3 | [![Build Status](https://app.bitrise.io/app/27a5e39dc511ba7c/status.svg?token=HZCmnpdBTIy3rOQdUv6HOg&branch=master)](https://app.bitrise.io/app/27a5e39dc511ba7c) [![CocoaPods](https://img.shields.io/cocoapods/v/Frisbee.svg)]() [![CocoaPods](https://img.shields.io/cocoapods/p/Frisbee.svg)]() [![Carthage](https://img.shields.io/badge/carthage-compatible-brightgreen.svg)]() [![codecov](https://codecov.io/gh/ronanrodrigo/frisbee/branch/master/graph/badge.svg)](https://codecov.io/gh/ronanrodrigo/frisbee) [![codebeat badge](https://codebeat.co/badges/f5cf675c-2fca-4689-a42e-a7029a984fe3)](https://codebeat.co/projects/github-com-ronanrodrigo-frisbee-master) [![Join at Telegram](https://img.shields.io/badge/telegram-join-319FD7.svg)](https://t.me/FrisbeeLib) [![Linux Compatible](https://img.shields.io/badge/linux-compatible-brightgreen.svg)]() 4 | 5 | --- 6 | 7 | Another network wrapper for URLSession. Built to be simple, small and easy to create tests at the network layer of your application. 8 | 9 | - [Install](#install) 10 | - [Carthage](#carthage) 11 | - [CocoaPods](#cocoapods) 12 | - [Swift Package Manager](#swift-package-manager) 13 | - [Usage](#usage) 14 | - [GET Request](#get-request) 15 | - [POST Request](#post-request) 16 | - [Usage in Tests](#usage-in-tests) 17 | - [Frisbee Next Teatures](#frisbee-next-features) 18 | - [Telegram](#telegram) 19 | 20 | ## Install 21 | #### Carthage 22 | To integrate Frisbee into your Xcode project using Carthage, specify it in your Cartfile: 23 | 24 | ``` 25 | github "ronanrodrigo/Frisbee" ~> 0.2.5 26 | ``` 27 | 28 | Run carthage update to build the framework and drag the built Frisbee.framework into your Xcode project. 29 | 30 | #### CocoaPods 31 | To integrate Frisbee into your Xcode project using CocoaPods, specify it in your `Podfile`: 32 | 33 | ```ruby 34 | source 'https://github.com/CocoaPods/Specs.git' 35 | platform :ios, '10.0' 36 | use_frameworks! 37 | 38 | target '' do 39 | pod 'Frisbee', '0.2.5' 40 | end 41 | ``` 42 | 43 | Then, run the following command: 44 | 45 | ```bash 46 | $ pod install 47 | ``` 48 | 49 | #### Swift Package Manager 50 | To integrate Frisbee into your Swift Package Manager project, set the dependencies in your `Package.swift`: 51 | 52 | ```swift 53 | // swift-tools-version:4.0 54 | 55 | import PackageDescription 56 | 57 | let package = Package( 58 | name: "", 59 | dependencies: [ 60 | .package(url: "https://github.com/ronanrodrigo/Frisbee.git", from: "0.2.5") 61 | ], 62 | targets: [ 63 | .target(name: "", dependencies: ["Frisbee"]) 64 | ] 65 | ) 66 | ``` 67 | 68 | ## Usage 69 | 70 | ### GET Request 71 | 72 | #### Decodable Entity 73 | A `Response` of a `Request` made in Frisbee will return an enum of `Result`. Where `T` must be a decodable entity. In this guide it will be used a `Movie` entity like bellow. 74 | 75 | ```swift 76 | struct Movie: Decodable { 77 | let name: String 78 | } 79 | ``` 80 | 81 | #### Making a Request 82 | You could abstract Frisbee usage in some class and inject an object that conforms to `Getable` protocol. So, in production ready code you will use an instance of `NetworkGet` object. 83 | 84 | ```swift 85 | class MoviesController { 86 | private let getRequest: Getable 87 | 88 | // Expect something that conforms to Getable 89 | init(getRequest: Getable) { 90 | self.getRequest = getRequest 91 | } 92 | 93 | func listMovies() { 94 | getRequest.get(url: someUrl) { moviesResult: Result<[Movie]> in 95 | switch moviesResult { 96 | case let .success(movies): print(movies[0].name) 97 | case let .fail(error): print(error) 98 | } 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | ```swift 105 | // Who will call the MoviesController must inject a NetworkGet instance 106 | MoviesController(getRequest: NetworkGet()) 107 | ``` 108 | 109 | #### Query Parameters 110 | It is easy to use query ~~strings~~ paramenters. Just create an `Encodable` struct and use it in `get(url:query:onComplete:)` method. 111 | 112 | ```swift 113 | struct MovieQuery: Encodable { 114 | let page: Int 115 | } 116 | ``` 117 | 118 | ```swift 119 | let query = MovieQuery(page: 10) 120 | NetworkGet().get(url: url, query: query) { (result: Result) in 121 | // ... 122 | } 123 | ``` 124 | 125 | ### POST Request 126 | Same way as GET request, Frisbee has a `Postable` protocol. And in prodution ready code you will use an instance of `NetworkPost`. 127 | 128 | #### Making Request 129 | It is the same logic as GET request. 130 | 131 | ```swift 132 | class MoviesController { 133 | private let postRequest: Postable 134 | 135 | // Expect something that conforms to Postable 136 | init(postRequest: Postable) { 137 | self.postRequest = postRequest 138 | } 139 | 140 | func createMovie() { 141 | postRequest.post(url: someUrl) { moviesResult: Result<[Movie]> in 142 | switch moviesResult { 143 | case let .success(movies): print(movies[0].name) 144 | case let .fail(error): print(error) 145 | } 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | #### Body Arguments 152 | It is easy to use body paramenters. Just create an `Encodable` struct and use it in `post(url:body:onComplete:)` method. 153 | 154 | ```swift 155 | struct MovieBody: Encodable { 156 | let name: String 157 | } 158 | ``` 159 | 160 | ```swift 161 | let body = MovieBody(name: "A New Movie") 162 | NetworkPost().post(url: url, body: body) { (result: Result) in 163 | // ... 164 | } 165 | ``` 166 | 167 | 168 | ## Usage in Tests 169 | 170 | In test target code you can create your own `Getable` (or `Postable` as you needed) mock. 171 | 172 | ```swift 173 | public class MockGet: Getable { 174 | var decodableMock: Decodable! 175 | 176 | public func get(url: URL, completionHandler: @escaping (Result) -> Void) { 177 | get(url: url.absoluteString, completionHandler: completionHandler) 178 | } 179 | 180 | public func get(url: String, completionHandler: @escaping (Result) -> Void) { 181 | if let decodableMock = decodableMock as? Entity { 182 | completionHandler(.success(decodableMock)) 183 | } 184 | } 185 | 186 | } 187 | 188 | ``` 189 | 190 | And instead `NetworkGet` you will use to test the `MockGet` on `MoviesController` 191 | 192 | ```swift 193 | 194 | class MoviesControllerTests: XCTestCase { 195 | func testDidTouchAtListMoviesWhenHasMoviesThenPresentAllMovies() { 196 | let mockGet = MockGet() 197 | let movies = [Movie(name: "Star Wars")] 198 | mockGet.decodableMock = movies 199 | let controller = MoviesController(getRequest: mockGet) 200 | 201 | controller.didTouchAtListMovies() 202 | 203 | XCTAssertEqual(controller.moviesQuantity, movies.count) 204 | } 205 | } 206 | ``` 207 | 208 | # Frisbee Next Features 209 | - [x] Get request 210 | - [x] Create Carthage distribution 211 | - [x] Create Cocoapod distribution 212 | - [x] Query parameter builder 213 | - [ ] [Issue # 8](https://github.com/ronanrodrigo/Frisbee/issues/8) Implement other HTTP verbs 214 | - [ ] [Issue # 7](https://github.com/ronanrodrigo/Frisbee/issues/7) Ready for use mock 215 | - [ ] [Issue # 2](https://github.com/ronanrodrigo/Frisbee/issues/2) Propagate Swift Errors 216 | 217 | # Telegram 218 | To collaborate, resolve questions and find out what's new about the Frisbee library, join the group on Telegram: https://t.me/FrisbeeLib 219 | -------------------------------------------------------------------------------- /Frisbee.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "Frisbee::FrisbeePackageTests::ProductTarget" /* FrisbeePackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_142 /* Build configuration list for PBXAggregateTarget "FrisbeePackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_145 /* PBXTargetDependency */, 17 | ); 18 | name = FrisbeePackageTests; 19 | productName = FrisbeePackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | OBJ_100 /* BodiableAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* BodiableAdapter.swift */; }; 25 | OBJ_101 /* BodyAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* BodyAdapter.swift */; }; 26 | OBJ_102 /* Cancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Cancellable.swift */; }; 27 | OBJ_103 /* DecodableAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* DecodableAdapter.swift */; }; 28 | OBJ_104 /* DecoderDataAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* DecoderDataAdapter.swift */; }; 29 | OBJ_105 /* DecoderJSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* DecoderJSONAdapter.swift */; }; 30 | OBJ_106 /* EncodableAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* EncodableAdapter.swift */; }; 31 | OBJ_107 /* EncoderJSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* EncoderJSONAdapter.swift */; }; 32 | OBJ_108 /* QueryItemAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* QueryItemAdapter.swift */; }; 33 | OBJ_109 /* SerializableAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* SerializableAdapter.swift */; }; 34 | OBJ_110 /* SerializerJSONAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* SerializerJSONAdapter.swift */; }; 35 | OBJ_111 /* URLQueriableAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* URLQueriableAdapter.swift */; }; 36 | OBJ_112 /* URLQueryAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* URLQueryAdapter.swift */; }; 37 | OBJ_113 /* URLSessionTaskAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* URLSessionTaskAdapter.swift */; }; 38 | OBJ_114 /* FrisbeeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* FrisbeeError.swift */; }; 39 | OBJ_115 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* HTTPMethod.swift */; }; 40 | OBJ_116 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* Result.swift */; }; 41 | OBJ_117 /* BodyAdapterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* BodyAdapterFactory.swift */; }; 42 | OBJ_118 /* DecodableAdapterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* DecodableAdapterFactory.swift */; }; 43 | OBJ_119 /* EncodableAdapterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* EncodableAdapterFactory.swift */; }; 44 | OBJ_120 /* SerializableAdapterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* SerializableAdapterFactory.swift */; }; 45 | OBJ_121 /* URLQueriableAdapterFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* URLQueriableAdapterFactory.swift */; }; 46 | OBJ_122 /* ResultGeneratorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* ResultGeneratorFactory.swift */; }; 47 | OBJ_123 /* URLRequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* URLRequestFactory.swift */; }; 48 | OBJ_124 /* URLSessionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* URLSessionFactory.swift */; }; 49 | OBJ_125 /* DataTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* DataTaskRunner.swift */; }; 50 | OBJ_126 /* ResultGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_40 /* ResultGenerator.swift */; }; 51 | OBJ_127 /* Getable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* Getable.swift */; }; 52 | OBJ_128 /* NetworkGet.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_43 /* NetworkGet.swift */; }; 53 | OBJ_129 /* NetworkPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* NetworkPost.swift */; }; 54 | OBJ_130 /* NetworkPut.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_45 /* NetworkPut.swift */; }; 55 | OBJ_131 /* OnComplete.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_46 /* OnComplete.swift */; }; 56 | OBJ_132 /* Postable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_47 /* Postable.swift */; }; 57 | OBJ_133 /* Putable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_48 /* Putable.swift */; }; 58 | OBJ_140 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 59 | OBJ_151 /* BodyAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_52 /* BodyAdapterTests.swift */; }; 60 | OBJ_152 /* DecoderDataAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_53 /* DecoderDataAdapterTests.swift */; }; 61 | OBJ_153 /* QueryItemAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_54 /* QueryItemAdapterTests.swift */; }; 62 | OBJ_154 /* URLQueryAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_55 /* URLQueryAdapterTests.swift */; }; 63 | OBJ_155 /* URLSessionTaskAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_56 /* URLSessionTaskAdapterTests.swift */; }; 64 | OBJ_156 /* FrisbeeErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_58 /* FrisbeeErrorTests.swift */; }; 65 | OBJ_157 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_59 /* ResultTests.swift */; }; 66 | OBJ_158 /* URLRequestFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_61 /* URLRequestFactoryTests.swift */; }; 67 | OBJ_159 /* URLSessionFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_62 /* URLSessionFactoryTests.swift */; }; 68 | OBJ_160 /* ResultGeneratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_64 /* ResultGeneratorTests.swift */; }; 69 | OBJ_161 /* IntegrationNetworkGetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_66 /* IntegrationNetworkGetTests.swift */; }; 70 | OBJ_162 /* IntegrationNetworkPostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_67 /* IntegrationNetworkPostTests.swift */; }; 71 | OBJ_163 /* IntegrationNetworkPutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_68 /* IntegrationNetworkPutTests.swift */; }; 72 | OBJ_164 /* NetworkGetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_69 /* NetworkGetTests.swift */; }; 73 | OBJ_165 /* NetworkPostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_70 /* NetworkPostTests.swift */; }; 74 | OBJ_166 /* NetworkPutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_71 /* NetworkPutTests.swift */; }; 75 | OBJ_167 /* BodyThrowErrorFakeAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_74 /* BodyThrowErrorFakeAdapter.swift */; }; 76 | OBJ_168 /* DecoderThrowErrorFakeAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_75 /* DecoderThrowErrorFakeAdapter.swift */; }; 77 | OBJ_169 /* EncoderThrowErrorFakeAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_76 /* EncoderThrowErrorFakeAdapter.swift */; }; 78 | OBJ_170 /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_77 /* MockURLSession.swift */; }; 79 | OBJ_171 /* SerializerThrowErrorFakeAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_78 /* SerializerThrowErrorFakeAdapter.swift */; }; 80 | OBJ_172 /* URLWithQueryTrhrowErrorFakeBuildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_79 /* URLWithQueryTrhrowErrorFakeBuildable.swift */; }; 81 | OBJ_173 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_81 /* Empty.swift */; }; 82 | OBJ_174 /* Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_82 /* Fake.swift */; }; 83 | OBJ_175 /* Movie.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_83 /* Movie.swift */; }; 84 | OBJ_176 /* MovieQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_84 /* MovieQuery.swift */; }; 85 | OBJ_177 /* SomeEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_85 /* SomeEntity.swift */; }; 86 | OBJ_178 /* SomeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_86 /* SomeError.swift */; }; 87 | OBJ_179 /* FrisbeeError+All.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_88 /* FrisbeeError+All.swift */; }; 88 | OBJ_180 /* SequenceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_89 /* SequenceExtensions.swift */; }; 89 | OBJ_181 /* XCTestCase+AssertContains.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_90 /* XCTestCase+AssertContains.swift */; }; 90 | OBJ_183 /* Frisbee.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Frisbee::Frisbee::Product" /* Frisbee.framework */; }; 91 | /* End PBXBuildFile section */ 92 | 93 | /* Begin PBXContainerItemProxy section */ 94 | 347AE580215E978300EF0623 /* PBXContainerItemProxy */ = { 95 | isa = PBXContainerItemProxy; 96 | containerPortal = OBJ_1 /* Project object */; 97 | proxyType = 1; 98 | remoteGlobalIDString = "Frisbee::Frisbee"; 99 | remoteInfo = Frisbee; 100 | }; 101 | 347AE581215E978400EF0623 /* PBXContainerItemProxy */ = { 102 | isa = PBXContainerItemProxy; 103 | containerPortal = OBJ_1 /* Project object */; 104 | proxyType = 1; 105 | remoteGlobalIDString = "Frisbee::FrisbeeTests"; 106 | remoteInfo = FrisbeeTests; 107 | }; 108 | /* End PBXContainerItemProxy section */ 109 | 110 | /* Begin PBXFileReference section */ 111 | "Frisbee::Frisbee::Product" /* Frisbee.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Frisbee.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 112 | "Frisbee::FrisbeeTests::Product" /* FrisbeeTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = FrisbeeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 113 | OBJ_10 /* BodiableAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodiableAdapter.swift; sourceTree = ""; }; 114 | OBJ_11 /* BodyAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyAdapter.swift; sourceTree = ""; }; 115 | OBJ_12 /* Cancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cancellable.swift; sourceTree = ""; }; 116 | OBJ_13 /* DecodableAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableAdapter.swift; sourceTree = ""; }; 117 | OBJ_14 /* DecoderDataAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderDataAdapter.swift; sourceTree = ""; }; 118 | OBJ_15 /* DecoderJSONAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderJSONAdapter.swift; sourceTree = ""; }; 119 | OBJ_16 /* EncodableAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableAdapter.swift; sourceTree = ""; }; 120 | OBJ_17 /* EncoderJSONAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncoderJSONAdapter.swift; sourceTree = ""; }; 121 | OBJ_18 /* QueryItemAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryItemAdapter.swift; sourceTree = ""; }; 122 | OBJ_19 /* SerializableAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializableAdapter.swift; sourceTree = ""; }; 123 | OBJ_20 /* SerializerJSONAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializerJSONAdapter.swift; sourceTree = ""; }; 124 | OBJ_21 /* URLQueriableAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueriableAdapter.swift; sourceTree = ""; }; 125 | OBJ_22 /* URLQueryAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueryAdapter.swift; sourceTree = ""; }; 126 | OBJ_23 /* URLSessionTaskAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionTaskAdapter.swift; sourceTree = ""; }; 127 | OBJ_25 /* FrisbeeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrisbeeError.swift; sourceTree = ""; }; 128 | OBJ_26 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; 129 | OBJ_27 /* Result.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; 130 | OBJ_30 /* BodyAdapterFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyAdapterFactory.swift; sourceTree = ""; }; 131 | OBJ_31 /* DecodableAdapterFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableAdapterFactory.swift; sourceTree = ""; }; 132 | OBJ_32 /* EncodableAdapterFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableAdapterFactory.swift; sourceTree = ""; }; 133 | OBJ_33 /* SerializableAdapterFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializableAdapterFactory.swift; sourceTree = ""; }; 134 | OBJ_34 /* URLQueriableAdapterFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueriableAdapterFactory.swift; sourceTree = ""; }; 135 | OBJ_35 /* ResultGeneratorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultGeneratorFactory.swift; sourceTree = ""; }; 136 | OBJ_36 /* URLRequestFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestFactory.swift; sourceTree = ""; }; 137 | OBJ_37 /* URLSessionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionFactory.swift; sourceTree = ""; }; 138 | OBJ_39 /* DataTaskRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTaskRunner.swift; sourceTree = ""; }; 139 | OBJ_40 /* ResultGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultGenerator.swift; sourceTree = ""; }; 140 | OBJ_42 /* Getable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Getable.swift; sourceTree = ""; }; 141 | OBJ_43 /* NetworkGet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkGet.swift; sourceTree = ""; }; 142 | OBJ_44 /* NetworkPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPost.swift; sourceTree = ""; }; 143 | OBJ_45 /* NetworkPut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPut.swift; sourceTree = ""; }; 144 | OBJ_46 /* OnComplete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnComplete.swift; sourceTree = ""; }; 145 | OBJ_47 /* Postable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Postable.swift; sourceTree = ""; }; 146 | OBJ_48 /* Putable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Putable.swift; sourceTree = ""; }; 147 | OBJ_52 /* BodyAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyAdapterTests.swift; sourceTree = ""; }; 148 | OBJ_53 /* DecoderDataAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderDataAdapterTests.swift; sourceTree = ""; }; 149 | OBJ_54 /* QueryItemAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueryItemAdapterTests.swift; sourceTree = ""; }; 150 | OBJ_55 /* URLQueryAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueryAdapterTests.swift; sourceTree = ""; }; 151 | OBJ_56 /* URLSessionTaskAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionTaskAdapterTests.swift; sourceTree = ""; }; 152 | OBJ_58 /* FrisbeeErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrisbeeErrorTests.swift; sourceTree = ""; }; 153 | OBJ_59 /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = ""; }; 154 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 155 | OBJ_61 /* URLRequestFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestFactoryTests.swift; sourceTree = ""; }; 156 | OBJ_62 /* URLSessionFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionFactoryTests.swift; sourceTree = ""; }; 157 | OBJ_64 /* ResultGeneratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultGeneratorTests.swift; sourceTree = ""; }; 158 | OBJ_66 /* IntegrationNetworkGetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationNetworkGetTests.swift; sourceTree = ""; }; 159 | OBJ_67 /* IntegrationNetworkPostTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationNetworkPostTests.swift; sourceTree = ""; }; 160 | OBJ_68 /* IntegrationNetworkPutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationNetworkPutTests.swift; sourceTree = ""; }; 161 | OBJ_69 /* NetworkGetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkGetTests.swift; sourceTree = ""; }; 162 | OBJ_70 /* NetworkPostTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPostTests.swift; sourceTree = ""; }; 163 | OBJ_71 /* NetworkPutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPutTests.swift; sourceTree = ""; }; 164 | OBJ_74 /* BodyThrowErrorFakeAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyThrowErrorFakeAdapter.swift; sourceTree = ""; }; 165 | OBJ_75 /* DecoderThrowErrorFakeAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecoderThrowErrorFakeAdapter.swift; sourceTree = ""; }; 166 | OBJ_76 /* EncoderThrowErrorFakeAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncoderThrowErrorFakeAdapter.swift; sourceTree = ""; }; 167 | OBJ_77 /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = ""; }; 168 | OBJ_78 /* SerializerThrowErrorFakeAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerializerThrowErrorFakeAdapter.swift; sourceTree = ""; }; 169 | OBJ_79 /* URLWithQueryTrhrowErrorFakeBuildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLWithQueryTrhrowErrorFakeBuildable.swift; sourceTree = ""; }; 170 | OBJ_81 /* Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = ""; }; 171 | OBJ_82 /* Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fake.swift; sourceTree = ""; }; 172 | OBJ_83 /* Movie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Movie.swift; sourceTree = ""; }; 173 | OBJ_84 /* MovieQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieQuery.swift; sourceTree = ""; }; 174 | OBJ_85 /* SomeEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SomeEntity.swift; sourceTree = ""; }; 175 | OBJ_86 /* SomeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SomeError.swift; sourceTree = ""; }; 176 | OBJ_88 /* FrisbeeError+All.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FrisbeeError+All.swift"; sourceTree = ""; }; 177 | OBJ_89 /* SequenceExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceExtensions.swift; sourceTree = ""; }; 178 | OBJ_90 /* XCTestCase+AssertContains.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTestCase+AssertContains.swift"; sourceTree = ""; }; 179 | OBJ_91 /* build */ = {isa = PBXFileReference; lastKnownFileType = folder; path = build; sourceTree = SOURCE_ROOT; }; 180 | /* End PBXFileReference section */ 181 | 182 | /* Begin PBXFrameworksBuildPhase section */ 183 | OBJ_134 /* Frameworks */ = { 184 | isa = PBXFrameworksBuildPhase; 185 | buildActionMask = 0; 186 | files = ( 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | OBJ_182 /* Frameworks */ = { 191 | isa = PBXFrameworksBuildPhase; 192 | buildActionMask = 0; 193 | files = ( 194 | OBJ_183 /* Frisbee.framework in Frameworks */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXFrameworksBuildPhase section */ 199 | 200 | /* Begin PBXGroup section */ 201 | OBJ_24 /* Entities */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | OBJ_25 /* FrisbeeError.swift */, 205 | OBJ_26 /* HTTPMethod.swift */, 206 | OBJ_27 /* Result.swift */, 207 | ); 208 | path = Entities; 209 | sourceTree = ""; 210 | }; 211 | OBJ_28 /* Factories */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | OBJ_29 /* Adapters */, 215 | OBJ_35 /* ResultGeneratorFactory.swift */, 216 | OBJ_36 /* URLRequestFactory.swift */, 217 | OBJ_37 /* URLSessionFactory.swift */, 218 | ); 219 | path = Factories; 220 | sourceTree = ""; 221 | }; 222 | OBJ_29 /* Adapters */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | OBJ_30 /* BodyAdapterFactory.swift */, 226 | OBJ_31 /* DecodableAdapterFactory.swift */, 227 | OBJ_32 /* EncodableAdapterFactory.swift */, 228 | OBJ_33 /* SerializableAdapterFactory.swift */, 229 | OBJ_34 /* URLQueriableAdapterFactory.swift */, 230 | ); 231 | path = Adapters; 232 | sourceTree = ""; 233 | }; 234 | OBJ_38 /* Interactors */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | OBJ_39 /* DataTaskRunner.swift */, 238 | OBJ_40 /* ResultGenerator.swift */, 239 | ); 240 | path = Interactors; 241 | sourceTree = ""; 242 | }; 243 | OBJ_41 /* Requestables */ = { 244 | isa = PBXGroup; 245 | children = ( 246 | OBJ_42 /* Getable.swift */, 247 | OBJ_43 /* NetworkGet.swift */, 248 | OBJ_48 /* Putable.swift */, 249 | OBJ_45 /* NetworkPut.swift */, 250 | OBJ_47 /* Postable.swift */, 251 | OBJ_44 /* NetworkPost.swift */, 252 | OBJ_46 /* OnComplete.swift */, 253 | ); 254 | path = Requestables; 255 | sourceTree = ""; 256 | }; 257 | OBJ_49 /* Tests */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | OBJ_50 /* FrisbeeTests */, 261 | ); 262 | name = Tests; 263 | sourceTree = SOURCE_ROOT; 264 | }; 265 | OBJ_5 /* */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | OBJ_6 /* Package.swift */, 269 | OBJ_7 /* Sources */, 270 | OBJ_49 /* Tests */, 271 | OBJ_91 /* build */, 272 | OBJ_92 /* Products */, 273 | ); 274 | name = ""; 275 | sourceTree = ""; 276 | }; 277 | OBJ_50 /* FrisbeeTests */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | OBJ_51 /* Adapters */, 281 | OBJ_57 /* Entities */, 282 | OBJ_60 /* Factories */, 283 | OBJ_63 /* Interactors */, 284 | OBJ_65 /* Requestables */, 285 | OBJ_72 /* Support */, 286 | ); 287 | name = FrisbeeTests; 288 | path = Tests/FrisbeeTests; 289 | sourceTree = SOURCE_ROOT; 290 | }; 291 | OBJ_51 /* Adapters */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | OBJ_52 /* BodyAdapterTests.swift */, 295 | OBJ_53 /* DecoderDataAdapterTests.swift */, 296 | OBJ_54 /* QueryItemAdapterTests.swift */, 297 | OBJ_55 /* URLQueryAdapterTests.swift */, 298 | OBJ_56 /* URLSessionTaskAdapterTests.swift */, 299 | ); 300 | path = Adapters; 301 | sourceTree = ""; 302 | }; 303 | OBJ_57 /* Entities */ = { 304 | isa = PBXGroup; 305 | children = ( 306 | OBJ_58 /* FrisbeeErrorTests.swift */, 307 | OBJ_59 /* ResultTests.swift */, 308 | ); 309 | path = Entities; 310 | sourceTree = ""; 311 | }; 312 | OBJ_60 /* Factories */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | OBJ_61 /* URLRequestFactoryTests.swift */, 316 | OBJ_62 /* URLSessionFactoryTests.swift */, 317 | ); 318 | path = Factories; 319 | sourceTree = ""; 320 | }; 321 | OBJ_63 /* Interactors */ = { 322 | isa = PBXGroup; 323 | children = ( 324 | OBJ_64 /* ResultGeneratorTests.swift */, 325 | ); 326 | path = Interactors; 327 | sourceTree = ""; 328 | }; 329 | OBJ_65 /* Requestables */ = { 330 | isa = PBXGroup; 331 | children = ( 332 | OBJ_66 /* IntegrationNetworkGetTests.swift */, 333 | OBJ_67 /* IntegrationNetworkPostTests.swift */, 334 | OBJ_68 /* IntegrationNetworkPutTests.swift */, 335 | OBJ_69 /* NetworkGetTests.swift */, 336 | OBJ_70 /* NetworkPostTests.swift */, 337 | OBJ_71 /* NetworkPutTests.swift */, 338 | ); 339 | path = Requestables; 340 | sourceTree = ""; 341 | }; 342 | OBJ_7 /* Sources */ = { 343 | isa = PBXGroup; 344 | children = ( 345 | OBJ_8 /* Frisbee */, 346 | ); 347 | name = Sources; 348 | sourceTree = SOURCE_ROOT; 349 | }; 350 | OBJ_72 /* Support */ = { 351 | isa = PBXGroup; 352 | children = ( 353 | OBJ_73 /* Doubles */, 354 | OBJ_80 /* Entities */, 355 | OBJ_87 /* Extensions */, 356 | ); 357 | path = Support; 358 | sourceTree = ""; 359 | }; 360 | OBJ_73 /* Doubles */ = { 361 | isa = PBXGroup; 362 | children = ( 363 | OBJ_74 /* BodyThrowErrorFakeAdapter.swift */, 364 | OBJ_75 /* DecoderThrowErrorFakeAdapter.swift */, 365 | OBJ_76 /* EncoderThrowErrorFakeAdapter.swift */, 366 | OBJ_77 /* MockURLSession.swift */, 367 | OBJ_78 /* SerializerThrowErrorFakeAdapter.swift */, 368 | OBJ_79 /* URLWithQueryTrhrowErrorFakeBuildable.swift */, 369 | ); 370 | path = Doubles; 371 | sourceTree = ""; 372 | }; 373 | OBJ_8 /* Frisbee */ = { 374 | isa = PBXGroup; 375 | children = ( 376 | OBJ_9 /* Adapters */, 377 | OBJ_24 /* Entities */, 378 | OBJ_28 /* Factories */, 379 | OBJ_38 /* Interactors */, 380 | OBJ_41 /* Requestables */, 381 | ); 382 | name = Frisbee; 383 | path = Sources/Frisbee; 384 | sourceTree = SOURCE_ROOT; 385 | }; 386 | OBJ_80 /* Entities */ = { 387 | isa = PBXGroup; 388 | children = ( 389 | OBJ_81 /* Empty.swift */, 390 | OBJ_82 /* Fake.swift */, 391 | OBJ_83 /* Movie.swift */, 392 | OBJ_84 /* MovieQuery.swift */, 393 | OBJ_85 /* SomeEntity.swift */, 394 | OBJ_86 /* SomeError.swift */, 395 | ); 396 | path = Entities; 397 | sourceTree = ""; 398 | }; 399 | OBJ_87 /* Extensions */ = { 400 | isa = PBXGroup; 401 | children = ( 402 | OBJ_88 /* FrisbeeError+All.swift */, 403 | OBJ_89 /* SequenceExtensions.swift */, 404 | OBJ_90 /* XCTestCase+AssertContains.swift */, 405 | ); 406 | path = Extensions; 407 | sourceTree = ""; 408 | }; 409 | OBJ_9 /* Adapters */ = { 410 | isa = PBXGroup; 411 | children = ( 412 | OBJ_10 /* BodiableAdapter.swift */, 413 | OBJ_11 /* BodyAdapter.swift */, 414 | OBJ_12 /* Cancellable.swift */, 415 | OBJ_13 /* DecodableAdapter.swift */, 416 | OBJ_14 /* DecoderDataAdapter.swift */, 417 | OBJ_15 /* DecoderJSONAdapter.swift */, 418 | OBJ_16 /* EncodableAdapter.swift */, 419 | OBJ_17 /* EncoderJSONAdapter.swift */, 420 | OBJ_18 /* QueryItemAdapter.swift */, 421 | OBJ_19 /* SerializableAdapter.swift */, 422 | OBJ_20 /* SerializerJSONAdapter.swift */, 423 | OBJ_21 /* URLQueriableAdapter.swift */, 424 | OBJ_22 /* URLQueryAdapter.swift */, 425 | OBJ_23 /* URLSessionTaskAdapter.swift */, 426 | ); 427 | path = Adapters; 428 | sourceTree = ""; 429 | }; 430 | OBJ_92 /* Products */ = { 431 | isa = PBXGroup; 432 | children = ( 433 | "Frisbee::Frisbee::Product" /* Frisbee.framework */, 434 | "Frisbee::FrisbeeTests::Product" /* FrisbeeTests.xctest */, 435 | ); 436 | name = Products; 437 | sourceTree = BUILT_PRODUCTS_DIR; 438 | }; 439 | /* End PBXGroup section */ 440 | 441 | /* Begin PBXNativeTarget section */ 442 | "Frisbee::Frisbee" /* Frisbee */ = { 443 | isa = PBXNativeTarget; 444 | buildConfigurationList = OBJ_96 /* Build configuration list for PBXNativeTarget "Frisbee" */; 445 | buildPhases = ( 446 | OBJ_99 /* Sources */, 447 | OBJ_134 /* Frameworks */, 448 | ); 449 | buildRules = ( 450 | ); 451 | dependencies = ( 452 | ); 453 | name = Frisbee; 454 | productName = Frisbee; 455 | productReference = "Frisbee::Frisbee::Product" /* Frisbee.framework */; 456 | productType = "com.apple.product-type.framework"; 457 | }; 458 | "Frisbee::FrisbeeTests" /* FrisbeeTests */ = { 459 | isa = PBXNativeTarget; 460 | buildConfigurationList = OBJ_147 /* Build configuration list for PBXNativeTarget "FrisbeeTests" */; 461 | buildPhases = ( 462 | OBJ_150 /* Sources */, 463 | OBJ_182 /* Frameworks */, 464 | ); 465 | buildRules = ( 466 | ); 467 | dependencies = ( 468 | OBJ_184 /* PBXTargetDependency */, 469 | ); 470 | name = FrisbeeTests; 471 | productName = FrisbeeTests; 472 | productReference = "Frisbee::FrisbeeTests::Product" /* FrisbeeTests.xctest */; 473 | productType = "com.apple.product-type.bundle.unit-test"; 474 | }; 475 | "Frisbee::SwiftPMPackageDescription" /* FrisbeePackageDescription */ = { 476 | isa = PBXNativeTarget; 477 | buildConfigurationList = OBJ_136 /* Build configuration list for PBXNativeTarget "FrisbeePackageDescription" */; 478 | buildPhases = ( 479 | OBJ_139 /* Sources */, 480 | ); 481 | buildRules = ( 482 | ); 483 | dependencies = ( 484 | ); 485 | name = FrisbeePackageDescription; 486 | productName = FrisbeePackageDescription; 487 | productType = "com.apple.product-type.framework"; 488 | }; 489 | /* End PBXNativeTarget section */ 490 | 491 | /* Begin PBXProject section */ 492 | OBJ_1 /* Project object */ = { 493 | isa = PBXProject; 494 | attributes = { 495 | LastUpgradeCheck = 9999; 496 | }; 497 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Frisbee" */; 498 | compatibilityVersion = "Xcode 3.2"; 499 | developmentRegion = English; 500 | hasScannedForEncodings = 0; 501 | knownRegions = ( 502 | en, 503 | ); 504 | mainGroup = OBJ_5 /* */; 505 | productRefGroup = OBJ_92 /* Products */; 506 | projectDirPath = ""; 507 | projectRoot = ""; 508 | targets = ( 509 | "Frisbee::Frisbee" /* Frisbee */, 510 | "Frisbee::SwiftPMPackageDescription" /* FrisbeePackageDescription */, 511 | "Frisbee::FrisbeePackageTests::ProductTarget" /* FrisbeePackageTests */, 512 | "Frisbee::FrisbeeTests" /* FrisbeeTests */, 513 | ); 514 | }; 515 | /* End PBXProject section */ 516 | 517 | /* Begin PBXSourcesBuildPhase section */ 518 | OBJ_139 /* Sources */ = { 519 | isa = PBXSourcesBuildPhase; 520 | buildActionMask = 0; 521 | files = ( 522 | OBJ_140 /* Package.swift in Sources */, 523 | ); 524 | runOnlyForDeploymentPostprocessing = 0; 525 | }; 526 | OBJ_150 /* Sources */ = { 527 | isa = PBXSourcesBuildPhase; 528 | buildActionMask = 0; 529 | files = ( 530 | OBJ_151 /* BodyAdapterTests.swift in Sources */, 531 | OBJ_152 /* DecoderDataAdapterTests.swift in Sources */, 532 | OBJ_153 /* QueryItemAdapterTests.swift in Sources */, 533 | OBJ_154 /* URLQueryAdapterTests.swift in Sources */, 534 | OBJ_155 /* URLSessionTaskAdapterTests.swift in Sources */, 535 | OBJ_156 /* FrisbeeErrorTests.swift in Sources */, 536 | OBJ_157 /* ResultTests.swift in Sources */, 537 | OBJ_158 /* URLRequestFactoryTests.swift in Sources */, 538 | OBJ_159 /* URLSessionFactoryTests.swift in Sources */, 539 | OBJ_160 /* ResultGeneratorTests.swift in Sources */, 540 | OBJ_161 /* IntegrationNetworkGetTests.swift in Sources */, 541 | OBJ_162 /* IntegrationNetworkPostTests.swift in Sources */, 542 | OBJ_163 /* IntegrationNetworkPutTests.swift in Sources */, 543 | OBJ_164 /* NetworkGetTests.swift in Sources */, 544 | OBJ_165 /* NetworkPostTests.swift in Sources */, 545 | OBJ_166 /* NetworkPutTests.swift in Sources */, 546 | OBJ_167 /* BodyThrowErrorFakeAdapter.swift in Sources */, 547 | OBJ_168 /* DecoderThrowErrorFakeAdapter.swift in Sources */, 548 | OBJ_169 /* EncoderThrowErrorFakeAdapter.swift in Sources */, 549 | OBJ_170 /* MockURLSession.swift in Sources */, 550 | OBJ_171 /* SerializerThrowErrorFakeAdapter.swift in Sources */, 551 | OBJ_172 /* URLWithQueryTrhrowErrorFakeBuildable.swift in Sources */, 552 | OBJ_173 /* Empty.swift in Sources */, 553 | OBJ_174 /* Fake.swift in Sources */, 554 | OBJ_175 /* Movie.swift in Sources */, 555 | OBJ_176 /* MovieQuery.swift in Sources */, 556 | OBJ_177 /* SomeEntity.swift in Sources */, 557 | OBJ_178 /* SomeError.swift in Sources */, 558 | OBJ_179 /* FrisbeeError+All.swift in Sources */, 559 | OBJ_180 /* SequenceExtensions.swift in Sources */, 560 | OBJ_181 /* XCTestCase+AssertContains.swift in Sources */, 561 | ); 562 | runOnlyForDeploymentPostprocessing = 0; 563 | }; 564 | OBJ_99 /* Sources */ = { 565 | isa = PBXSourcesBuildPhase; 566 | buildActionMask = 0; 567 | files = ( 568 | OBJ_100 /* BodiableAdapter.swift in Sources */, 569 | OBJ_101 /* BodyAdapter.swift in Sources */, 570 | OBJ_102 /* Cancellable.swift in Sources */, 571 | OBJ_103 /* DecodableAdapter.swift in Sources */, 572 | OBJ_104 /* DecoderDataAdapter.swift in Sources */, 573 | OBJ_105 /* DecoderJSONAdapter.swift in Sources */, 574 | OBJ_106 /* EncodableAdapter.swift in Sources */, 575 | OBJ_107 /* EncoderJSONAdapter.swift in Sources */, 576 | OBJ_108 /* QueryItemAdapter.swift in Sources */, 577 | OBJ_109 /* SerializableAdapter.swift in Sources */, 578 | OBJ_110 /* SerializerJSONAdapter.swift in Sources */, 579 | OBJ_111 /* URLQueriableAdapter.swift in Sources */, 580 | OBJ_112 /* URLQueryAdapter.swift in Sources */, 581 | OBJ_113 /* URLSessionTaskAdapter.swift in Sources */, 582 | OBJ_114 /* FrisbeeError.swift in Sources */, 583 | OBJ_115 /* HTTPMethod.swift in Sources */, 584 | OBJ_116 /* Result.swift in Sources */, 585 | OBJ_117 /* BodyAdapterFactory.swift in Sources */, 586 | OBJ_118 /* DecodableAdapterFactory.swift in Sources */, 587 | OBJ_119 /* EncodableAdapterFactory.swift in Sources */, 588 | OBJ_120 /* SerializableAdapterFactory.swift in Sources */, 589 | OBJ_121 /* URLQueriableAdapterFactory.swift in Sources */, 590 | OBJ_122 /* ResultGeneratorFactory.swift in Sources */, 591 | OBJ_123 /* URLRequestFactory.swift in Sources */, 592 | OBJ_124 /* URLSessionFactory.swift in Sources */, 593 | OBJ_125 /* DataTaskRunner.swift in Sources */, 594 | OBJ_126 /* ResultGenerator.swift in Sources */, 595 | OBJ_127 /* Getable.swift in Sources */, 596 | OBJ_128 /* NetworkGet.swift in Sources */, 597 | OBJ_129 /* NetworkPost.swift in Sources */, 598 | OBJ_130 /* NetworkPut.swift in Sources */, 599 | OBJ_131 /* OnComplete.swift in Sources */, 600 | OBJ_132 /* Postable.swift in Sources */, 601 | OBJ_133 /* Putable.swift in Sources */, 602 | ); 603 | runOnlyForDeploymentPostprocessing = 0; 604 | }; 605 | /* End PBXSourcesBuildPhase section */ 606 | 607 | /* Begin PBXTargetDependency section */ 608 | OBJ_145 /* PBXTargetDependency */ = { 609 | isa = PBXTargetDependency; 610 | target = "Frisbee::FrisbeeTests" /* FrisbeeTests */; 611 | targetProxy = 347AE581215E978400EF0623 /* PBXContainerItemProxy */; 612 | }; 613 | OBJ_184 /* PBXTargetDependency */ = { 614 | isa = PBXTargetDependency; 615 | target = "Frisbee::Frisbee" /* Frisbee */; 616 | targetProxy = 347AE580215E978300EF0623 /* PBXContainerItemProxy */; 617 | }; 618 | /* End PBXTargetDependency section */ 619 | 620 | /* Begin XCBuildConfiguration section */ 621 | OBJ_137 /* Debug */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | LD = /usr/bin/true; 625 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 626 | SWIFT_VERSION = 4.0; 627 | }; 628 | name = Debug; 629 | }; 630 | OBJ_138 /* Release */ = { 631 | isa = XCBuildConfiguration; 632 | buildSettings = { 633 | LD = /usr/bin/true; 634 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 635 | SWIFT_VERSION = 4.0; 636 | }; 637 | name = Release; 638 | }; 639 | OBJ_143 /* Debug */ = { 640 | isa = XCBuildConfiguration; 641 | buildSettings = { 642 | }; 643 | name = Debug; 644 | }; 645 | OBJ_144 /* Release */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | }; 649 | name = Release; 650 | }; 651 | OBJ_148 /* Debug */ = { 652 | isa = XCBuildConfiguration; 653 | buildSettings = { 654 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 655 | FRAMEWORK_SEARCH_PATHS = ( 656 | "$(inherited)", 657 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 658 | ); 659 | HEADER_SEARCH_PATHS = "$(inherited)"; 660 | INFOPLIST_FILE = Frisbee.xcodeproj/FrisbeeTests_Info.plist; 661 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 662 | OTHER_CFLAGS = "$(inherited)"; 663 | OTHER_LDFLAGS = "$(inherited)"; 664 | OTHER_SWIFT_FLAGS = "$(inherited)"; 665 | SWIFT_VERSION = 4.0; 666 | TARGET_NAME = FrisbeeTests; 667 | }; 668 | name = Debug; 669 | }; 670 | OBJ_149 /* Release */ = { 671 | isa = XCBuildConfiguration; 672 | buildSettings = { 673 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 674 | FRAMEWORK_SEARCH_PATHS = ( 675 | "$(inherited)", 676 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 677 | ); 678 | HEADER_SEARCH_PATHS = "$(inherited)"; 679 | INFOPLIST_FILE = Frisbee.xcodeproj/FrisbeeTests_Info.plist; 680 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 681 | OTHER_CFLAGS = "$(inherited)"; 682 | OTHER_LDFLAGS = "$(inherited)"; 683 | OTHER_SWIFT_FLAGS = "$(inherited)"; 684 | SWIFT_VERSION = 4.0; 685 | TARGET_NAME = FrisbeeTests; 686 | }; 687 | name = Release; 688 | }; 689 | OBJ_3 /* Debug */ = { 690 | isa = XCBuildConfiguration; 691 | buildSettings = { 692 | CLANG_ENABLE_OBJC_ARC = YES; 693 | COMBINE_HIDPI_IMAGES = YES; 694 | COPY_PHASE_STRIP = NO; 695 | DEBUG_INFORMATION_FORMAT = dwarf; 696 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 697 | ENABLE_NS_ASSERTIONS = YES; 698 | GCC_OPTIMIZATION_LEVEL = 0; 699 | MACOSX_DEPLOYMENT_TARGET = 10.10; 700 | ONLY_ACTIVE_ARCH = YES; 701 | OTHER_SWIFT_FLAGS = "-DXcode"; 702 | PRODUCT_NAME = "$(TARGET_NAME)"; 703 | SDKROOT = macosx; 704 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 705 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 706 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 707 | USE_HEADERMAP = NO; 708 | }; 709 | name = Debug; 710 | }; 711 | OBJ_4 /* Release */ = { 712 | isa = XCBuildConfiguration; 713 | buildSettings = { 714 | CLANG_ENABLE_OBJC_ARC = YES; 715 | COMBINE_HIDPI_IMAGES = YES; 716 | COPY_PHASE_STRIP = YES; 717 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 718 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 719 | GCC_OPTIMIZATION_LEVEL = s; 720 | MACOSX_DEPLOYMENT_TARGET = 10.10; 721 | OTHER_SWIFT_FLAGS = "-DXcode"; 722 | PRODUCT_NAME = "$(TARGET_NAME)"; 723 | SDKROOT = macosx; 724 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 725 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 726 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 727 | USE_HEADERMAP = NO; 728 | }; 729 | name = Release; 730 | }; 731 | OBJ_97 /* Debug */ = { 732 | isa = XCBuildConfiguration; 733 | buildSettings = { 734 | ENABLE_TESTABILITY = YES; 735 | FRAMEWORK_SEARCH_PATHS = ( 736 | "$(inherited)", 737 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 738 | ); 739 | HEADER_SEARCH_PATHS = "$(inherited)"; 740 | INFOPLIST_FILE = Frisbee.xcodeproj/Frisbee_Info.plist; 741 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 742 | OTHER_CFLAGS = "$(inherited)"; 743 | OTHER_LDFLAGS = "$(inherited)"; 744 | OTHER_SWIFT_FLAGS = "$(inherited)"; 745 | PRODUCT_BUNDLE_IDENTIFIER = Frisbee; 746 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 747 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 748 | SKIP_INSTALL = YES; 749 | SWIFT_VERSION = 4.0; 750 | TARGET_NAME = Frisbee; 751 | }; 752 | name = Debug; 753 | }; 754 | OBJ_98 /* Release */ = { 755 | isa = XCBuildConfiguration; 756 | buildSettings = { 757 | ENABLE_TESTABILITY = YES; 758 | FRAMEWORK_SEARCH_PATHS = ( 759 | "$(inherited)", 760 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 761 | ); 762 | HEADER_SEARCH_PATHS = "$(inherited)"; 763 | INFOPLIST_FILE = Frisbee.xcodeproj/Frisbee_Info.plist; 764 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 765 | OTHER_CFLAGS = "$(inherited)"; 766 | OTHER_LDFLAGS = "$(inherited)"; 767 | OTHER_SWIFT_FLAGS = "$(inherited)"; 768 | PRODUCT_BUNDLE_IDENTIFIER = Frisbee; 769 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 770 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 771 | SKIP_INSTALL = YES; 772 | SWIFT_VERSION = 4.0; 773 | TARGET_NAME = Frisbee; 774 | }; 775 | name = Release; 776 | }; 777 | /* End XCBuildConfiguration section */ 778 | 779 | /* Begin XCConfigurationList section */ 780 | OBJ_136 /* Build configuration list for PBXNativeTarget "FrisbeePackageDescription" */ = { 781 | isa = XCConfigurationList; 782 | buildConfigurations = ( 783 | OBJ_137 /* Debug */, 784 | OBJ_138 /* Release */, 785 | ); 786 | defaultConfigurationIsVisible = 0; 787 | defaultConfigurationName = Release; 788 | }; 789 | OBJ_142 /* Build configuration list for PBXAggregateTarget "FrisbeePackageTests" */ = { 790 | isa = XCConfigurationList; 791 | buildConfigurations = ( 792 | OBJ_143 /* Debug */, 793 | OBJ_144 /* Release */, 794 | ); 795 | defaultConfigurationIsVisible = 0; 796 | defaultConfigurationName = Release; 797 | }; 798 | OBJ_147 /* Build configuration list for PBXNativeTarget "FrisbeeTests" */ = { 799 | isa = XCConfigurationList; 800 | buildConfigurations = ( 801 | OBJ_148 /* Debug */, 802 | OBJ_149 /* Release */, 803 | ); 804 | defaultConfigurationIsVisible = 0; 805 | defaultConfigurationName = Release; 806 | }; 807 | OBJ_2 /* Build configuration list for PBXProject "Frisbee" */ = { 808 | isa = XCConfigurationList; 809 | buildConfigurations = ( 810 | OBJ_3 /* Debug */, 811 | OBJ_4 /* Release */, 812 | ); 813 | defaultConfigurationIsVisible = 0; 814 | defaultConfigurationName = Release; 815 | }; 816 | OBJ_96 /* Build configuration list for PBXNativeTarget "Frisbee" */ = { 817 | isa = XCConfigurationList; 818 | buildConfigurations = ( 819 | OBJ_97 /* Debug */, 820 | OBJ_98 /* Release */, 821 | ); 822 | defaultConfigurationIsVisible = 0; 823 | defaultConfigurationName = Release; 824 | }; 825 | /* End XCConfigurationList section */ 826 | }; 827 | rootObject = OBJ_1 /* Project object */; 828 | } 829 | --------------------------------------------------------------------------------