├── .swift-version
├── Cartfile.private
├── Cartfile
├── Feathers.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ ├── Feathers-iOSTests.xcscheme
│ │ ├── Feathers-tvOSTests.xcscheme
│ │ ├── Feathers-macOSTests.xcscheme
│ │ ├── Feathers-watchOS.xcscheme
│ │ ├── Feathers-tvOS.xcscheme
│ │ ├── Feathers-macOS.xcscheme
│ │ └── Feathers-iOS.xcscheme
└── project.pbxproj
├── Cartfile.resolved
├── Feathers
├── Core
│ ├── SignalProducer+Convenience.swift
│ ├── LoggerHooks.swift
│ ├── AuthenticationStorage.swift
│ ├── AuthenticationConfiguration.swift
│ ├── Endpoint.swift
│ ├── Provider.swift
│ ├── ProviderService.swift
│ ├── Response.swift
│ ├── ServiceType.swift
│ ├── Hooks.swift
│ ├── Feathers.swift
│ ├── FeathersError.swift
│ ├── ServiceWrapper.swift
│ ├── Service.swift
│ └── Query.swift
├── Feathers.h
└── Info.plist
├── FeathersTests
├── Info.plist
├── StubProvider.swift
├── Hooks.swift
├── FeathersSpec.swift
├── QuerySpec.swift
└── ServiceSpec.swift
├── .gitignore
├── .github
└── stale.yml
├── LICENSE
├── script
└── build
├── Feathers.podspec
├── .travis.yml
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/Cartfile.private:
--------------------------------------------------------------------------------
1 | github "jspahrsummers/xcconfigs" "3d9d996"
2 | github "Quick/Quick"
3 | github "Quick/Nimble"
4 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "ReactiveCocoa/ReactiveSwift"
2 | github "marketplacer/keychain-swift"
3 | github "antitypical/Result"
4 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Quick/Nimble" "v8.0.1"
2 | github "Quick/Quick" "v2.1.0"
3 | github "ReactiveCocoa/ReactiveSwift" "6.0.0"
4 | github "antitypical/Result" "5.0.0"
5 | github "jspahrsummers/xcconfigs" "3d9d99634cae6d586e272543d527681283b33eb0"
6 | github "marketplacer/keychain-swift" "16.0.1"
7 |
--------------------------------------------------------------------------------
/Feathers/Core/SignalProducer+Convenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignalProducer+Convenience.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/22/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 |
12 | public extension SignalProducer {
13 |
14 | /// Sends only an interrupted event.
15 | static var interrupted: SignalProducer {
16 | return SignalProducer { observer, _ in
17 | observer.sendInterrupted()
18 | }
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Feathers/Feathers.h:
--------------------------------------------------------------------------------
1 | //
2 | // Feathers.h
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/15/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Feathers.
12 | FOUNDATION_EXPORT double FeathersVersionNumber;
13 |
14 | //! Project version string for Feathers.
15 | FOUNDATION_EXPORT const unsigned char FeathersVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Feathers/Core/LoggerHooks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoggerHooks.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/15/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 |
12 | /// Simple request logger
13 | public struct RequestLoggerHook: Hook {
14 |
15 | public func run(with hookObject: HookObject) -> SignalProducer {
16 | print("request to \(hookObject.service.path) for method \(hookObject.method)")
17 | return SignalProducer(value: hookObject)
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/FeathersTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Feathers/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 5.4.2
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | Example/Pods/*
23 | Example/Pods/Target\ Support\ Files/*
24 |
25 | # Bundler
26 | .bundle
27 |
28 | Carthage
29 | # We recommend against adding the Pods directory to your .gitignore. However
30 | # you should judge for yourself, the pros and cons are mentioned at:
31 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
32 | #
33 | # Note: if you ignore the Pods directory, make sure to uncomment
34 | # `pod install` in .travis.yml
35 | #
36 | # Pods/
37 | /.env
38 | /Pods
39 |
40 | Podfile.lock
41 |
--------------------------------------------------------------------------------
/Feathers/Core/AuthenticationStorage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthenticationStorage.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/3/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import KeychainSwift
11 |
12 | /// Authentication storage protocol.
13 | public protocol AuthenticationStorage: class {
14 |
15 | init(storageKey: String)
16 | var accessToken: String? { get set }
17 |
18 | }
19 |
20 | /// An encrypted authentication store. Uses the keychain to store a token.
21 | public final class EncryptedAuthenticationStore: AuthenticationStorage {
22 |
23 | private let keychain = KeychainSwift()
24 | private let storageKey: String
25 |
26 | public var accessToken: String? {
27 | get { return keychain.get(storageKey) }
28 | set {
29 | guard let value = newValue else { return }
30 | keychain.set(value, forKey: storageKey)
31 | }
32 | }
33 |
34 | public init(storageKey: String = "feathers-jwt") {
35 | self.storageKey = storageKey
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 84
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - greenkeeper
8 | - bug
9 | - security
10 | - enhancement
11 | # Label to use when marking an issue as stale
12 | staleLabel: wontfix
13 | # Comment to post when marking an issue as stale. Set to `false` to disable
14 | markComment: >
15 | This issue has been automatically marked as stale because it has not had
16 | recent activity. It will be closed if no further activity occurs.
17 | Apologies if the issue could not be resolved. FeathersJS ecosystem
18 | modules are community maintained so there may be a chance that there isn't anybody
19 | available to address the issue at the moment.
20 | For other ways to get help [see here](https://docs.feathersjs.com/help/readme.html).
21 | # Comment to post when closing a stale issue. Set to `false` to disable
22 | closeComment: false
23 | # Only close stale issues
24 | only: issues
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ## MIT License
2 |
3 | Copyright (C) 2015 [Feathers contributors](https://github.com/feathersjs/feathers/graphs/contributors)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/script/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | BUILD_DIRECTORY="build"
4 | CONFIGURATION=Release
5 |
6 | if [[ -z $TRAVIS_XCODE_PROJECT ]]; then
7 | echo "Error: \$TRAVIS_XCODE_PROJECT is not set."
8 | exit 1
9 | fi
10 |
11 | if [[ -z $TRAVIS_XCODE_SCHEME ]]; then
12 | echo "Error: \$TRAVIS_XCODE_SCHEME is not set!"
13 | exit 1
14 | fi
15 |
16 | if [[ -z $XCODE_ACTION ]]; then
17 | echo "Error: \$XCODE_ACTION is not set!"
18 | exit 1
19 | fi
20 |
21 | if [[ -z $XCODE_SDK ]]; then
22 | echo "Error: \$XCODE_SDK is not set!"
23 | exit 1
24 | fi
25 |
26 | if [[ -z $XCODE_DESTINATION ]]; then
27 | echo "Error: \$XCODE_DESTINATION is not set!"
28 | exit 1
29 | fi
30 |
31 | set -o pipefail
32 | xcodebuild $XCODE_ACTION \
33 | -project "$TRAVIS_XCODE_PROJECT" \
34 | -scheme "$TRAVIS_XCODE_SCHEME" \
35 | -sdk "$XCODE_SDK" \
36 | -destination "$XCODE_DESTINATION" \
37 | -derivedDataPath "${BUILD_DIRECTORY}" \
38 | -configuration $CONFIGURATION \
39 | ENABLE_TESTABILITY=YES \
40 | GCC_GENERATE_DEBUGGING_SYMBOLS=NO \
41 | RUN_CLANG_STATIC_ANALYZER=NO | xcpretty
42 | result=$?
43 |
44 | if [ "$result" -ne 0 ]; then
45 | exit $result
46 | fi
47 |
--------------------------------------------------------------------------------
/Feathers/Core/AuthenticationConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthenticationConfiguration.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/16/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct AuthenticationConfiguration {
12 |
13 | /// Authorization header field in requests.
14 | public let header: String
15 |
16 | /// Path for authentication service.
17 | public let path: String
18 |
19 | /// Strategy name for jwt authentication.
20 | public let jwtStrategy: String
21 |
22 | /// The entity you are authenticating.
23 | public let entity: String
24 |
25 | /// The service to look up the entity
26 | public let service: String
27 |
28 | // The key to store the accessToken with.
29 | public let storageKey: String
30 |
31 | public init(
32 | header: String = "Authorization",
33 | path: String = "/authentication",
34 | jwtStrategy: String = "jwt",
35 | entity: String = "user",
36 | service: String = "users",
37 | storageKey: String = "feathers-jwt") {
38 | self.header = header
39 | self.path = path
40 | self.jwtStrategy = jwtStrategy
41 | self.entity = entity
42 | self.service = service
43 | self.storageKey = storageKey
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Feathers.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Feathers"
3 | # Version goes here and will be used to access the git tag later on, once we have a first release.
4 | s.version = "5.4.1"
5 | s.summary = "Swift framework for interacting with Featherjs apis"
6 | s.description = <<-DESC
7 | Swift library for connecting to a FeathersJS backend.
8 |
9 | ReactiveSwift and RxSwift extensions are available.
10 | DESC
11 | s.homepage = "https://github.com/feathersjs-ecosystem/feathers-swift"
12 | s.license = { :type => "MIT", :file => "LICENSE" }
13 | s.author = "startupthekid"
14 |
15 | s.swift_version = "5.0"
16 | s.ios.deployment_target = "8.0"
17 | s.osx.deployment_target = "10.10"
18 | s.tvos.deployment_target = "9.0"
19 | s.watchos.deployment_target = "2.0"
20 | s.source = { :git => "https://github.com/feathersjs-ecosystem/feathers-swift.git", :tag => "#{s.version}" }
21 |
22 | s.default_subspec = "Core"
23 |
24 | s.subspec "Core" do |ss|
25 | ss.source_files = "Feathers/Core/*.{swift}"
26 | ss.framework = "Foundation"
27 | ss.dependency 'KeychainSwift'
28 | ss.dependency 'Result'
29 | ss.dependency "ReactiveSwift"
30 | end
31 |
32 | s.pod_target_xcconfig = {"OTHER_SWIFT_FLAGS[config=Release]" => "-suppress-warnings" }
33 | end
34 |
--------------------------------------------------------------------------------
/Feathers/Core/Endpoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Endpoint.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/7/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Represents an endpoint on the server.
12 | public final class Endpoint {
13 |
14 | /// Base url.
15 | public let baseURL: URL
16 |
17 | /// Endpoint path.
18 | public let path: String
19 |
20 | /// Service method.
21 | public let method: Service.Method
22 |
23 | /// Possible authentication token that can be attached to requests.
24 | public let accessToken: String?
25 |
26 | /// Application authentication configuration.
27 | public let authenticationConfiguration: AuthenticationConfiguration
28 |
29 | /// Creates an endpoint.
30 | ///
31 | /// - Parameters:
32 | /// - baseURL: Base url.
33 | /// - path: Endpoint path.
34 | /// - method: Service method.
35 | /// - accessToken: Authentication token.
36 | /// - authenticationConfiguration: Authentication configuration.
37 | internal init(baseURL: URL, path: String, method: Service.Method, accessToken: String?, authenticationConfiguration: AuthenticationConfiguration) {
38 | self.baseURL = baseURL
39 | self.path = path
40 | self.method = method
41 | self.accessToken = accessToken
42 | self.authenticationConfiguration = authenticationConfiguration
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/FeathersTests/StubProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StubProvider.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/14/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Feathers
10 | import Foundation
11 | import ReactiveSwift
12 | import enum Result.NoError
13 |
14 | class StubProvider: Provider {
15 |
16 | private let stubbedData: [String: Any]
17 |
18 | var supportsRealtimeEvents: Bool {
19 | return false
20 | }
21 |
22 | var baseURL: URL {
23 | return URL(string: "https://myserver.com")!
24 | }
25 |
26 | init(data: [String: Any]) {
27 | stubbedData = data
28 | }
29 |
30 | func setup(app: Feathers) {
31 | // no-op
32 | }
33 |
34 | func request(endpoint: Endpoint) -> SignalProducer {
35 | return SignalProducer(value: Response(pagination: nil, data: .object(stubbedData)))
36 | }
37 |
38 | func authenticate(_ path: String, credentials: [String : Any]) -> SignalProducer {
39 | return SignalProducer(value: Response(pagination: nil, data: .object(["accessToken":"some_token"])))
40 | }
41 |
42 | func logout(path: String) -> SignalProducer {
43 | return SignalProducer(value: Response(pagination: nil, data: .object([:])))
44 | }
45 |
46 | public func on(event: String) -> Signal<[String: Any], NoError> {
47 | // no-op
48 | return .empty
49 | }
50 |
51 | public func once(event: String) -> Signal<[String: Any], NoError> {
52 | return .empty
53 | }
54 |
55 | public func off(event: String) {
56 | // no-op
57 | }
58 |
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/FeathersTests/Hooks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Hooks.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/14/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Feathers
10 | import Foundation
11 | import ReactiveSwift
12 | import Result
13 |
14 | /// Hook that always errors
15 | struct ErrorHook: Hook {
16 |
17 | let error: FeathersError
18 |
19 | init(error: FeathersError) {
20 | self.error = error
21 | }
22 |
23 | func run(with hookObject: HookObject) -> SignalProducer {
24 | return SignalProducer(error: AnyFeathersError(error))
25 | }
26 |
27 | }
28 |
29 | // Hook that doesn't reject, just modifies the error
30 | struct ModifyErrorHook: Hook {
31 | let error: FeathersError
32 |
33 | init(error: FeathersError) {
34 | self.error = error
35 | }
36 |
37 | func run(with hookObject: HookObject) -> SignalProducer {
38 | var object = hookObject
39 | object.error = error
40 | return SignalProducer(value: object)
41 | }
42 |
43 | }
44 |
45 | struct StubHook: Hook {
46 |
47 | let data: ResponseData
48 |
49 | init(data: ResponseData) {
50 | self.data = data
51 | }
52 |
53 | func run(with hookObject: HookObject) -> SignalProducer {
54 | var object = hookObject
55 | object.result = Response(pagination: nil, data: data)
56 | return SignalProducer(value: object)
57 | }
58 |
59 | }
60 |
61 |
62 | struct PopuplateDataAfterHook: Hook {
63 |
64 | let data: [String: Any]
65 |
66 | init(data: [String: Any]) {
67 | self.data = data
68 | }
69 |
70 | func run(with hookObject: HookObject) -> SignalProducer {
71 | var object = hookObject
72 | object.result = Response(pagination: nil, data: .object(data))
73 | return SignalProducer(value: object)
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-tvOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-macOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Feathers/Core/Provider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Provider.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/15/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 | import Result
12 |
13 | /// Abstract interface for a provider.
14 | public protocol Provider: class {
15 |
16 | /// Provider's base url.
17 | var baseURL: URL { get }
18 |
19 | /// Whether or not the provider supports real-time events.
20 | var supportsRealtimeEvents: Bool { get }
21 |
22 | /// Used for any extra setup a provider needs. Called by the `Feathers` application.
23 | ///
24 | /// - Parameters:
25 | /// - app: Feathers application object.
26 | func setup(app: Feathers)
27 |
28 | /// Send a request to the server.
29 | ///
30 | /// - Parameters:
31 | /// - endpoint: Endpoint to hit.
32 | /// - completion: Completion block.
33 | func request(endpoint: Endpoint) -> SignalProducer
34 |
35 | /// Authenticate the provider.
36 | ///
37 | /// - Parameters:
38 | /// - path: Authentication path.
39 | /// - credentials: Credentials object for authentication.
40 | /// - completion: Completion block.
41 | func authenticate(_ path: String, credentials: [String: Any]) -> SignalProducer
42 |
43 | /// Logout the provider.
44 | ///
45 | /// - Parameter path: Logout path.
46 | /// - Parameter completion: Completion block.
47 | func logout(path: String) -> SignalProducer
48 |
49 | /// Register to listen for an event.
50 | ///
51 | /// - Parameters:
52 | /// - event: Event name.
53 | /// - callback: Event callback. Called every time an event sends.
54 | ///
55 | /// - warning: Events will continue to emit until `off` is called.
56 | func on(event: String) -> Signal<[String: Any], NoError>
57 |
58 | /// Register for single-use handler for the event.
59 | ///
60 | /// - Parameters:
61 | /// - event: Event name.
62 | /// - callback: Event callback, only called once.
63 | func once(event: String) -> Signal<[String: Any], NoError>
64 |
65 | /// Unregister for an event. Must be called to end the stream.
66 | ///
67 | /// - Parameter event: Event name.
68 | func off(event: String)
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode10.2
3 |
4 | cache:
5 | - directories:
6 | - Carthage
7 |
8 | before_install: true
9 | install:
10 | - rvm get stable || true
11 | - brew update || true
12 | - brew install carthage || true
13 | - brew outdated carthage || brew upgrade carthage
14 | before_script:
15 | - chmod +x ./script/build
16 | xcode_project: Feathers.xcodeproj
17 | matrix:
18 | include:
19 | - script:
20 | - carthage bootstrap --platform mac
21 | - script/build
22 | xcode_scheme: Feathers-macOS
23 | env:
24 | - XCODE_SDK=macosx
25 | - XCODE_ACTION="build test"
26 | - XCODE_DESTINATION="arch=x86_64"
27 | - script:
28 | - carthage bootstrap --platform iOS
29 | - script/build
30 | xcode_scheme: Feathers-iOS
31 | env:
32 | - XCODE_SDK=iphonesimulator
33 | - XCODE_ACTION="build-for-testing test-without-building"
34 | - XCODE_DESTINATION="platform=iOS Simulator,name=iPhone 7 Plus"
35 | - script:
36 | - carthage bootstrap --platform tvOS
37 | - script/build
38 | xcode_scheme: Feathers-tvOS
39 | env:
40 | - XCODE_SDK=appletvsimulator
41 | - XCODE_ACTION="build-for-testing test-without-building"
42 | - XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV"
43 | - script:
44 | - carthage bootstrap --platform watchOS
45 | - script/build
46 | xcode_scheme: Feathers-watchOS
47 | env:
48 | - XCODE_SDK=watchsimulator
49 | - XCODE_ACTION=build
50 | - XCODE_DESTINATION="platform=watchOS Simulator,name=Apple Watch - 38mm,OS=4.2"
51 | - script:
52 | - carthage checkout
53 | - carthage build --no-skip-current --platform mac
54 | env:
55 | - JOB=CARTHAGE-macOS
56 | - script:
57 | - carthage checkout
58 | - carthage build --no-skip-current --platform iOS
59 | env:
60 | - JOB=CARTHAGE-iOS
61 | - script:
62 | - carthage checkout
63 | - carthage build --no-skip-current --platform tvOS
64 | env:
65 | - JOB=CARTHAGE-tvOS
66 | - script:
67 | - carthage checkout
68 | - carthage build --no-skip-current --platform watchOS
69 | env:
70 | - JOB=CARTHAGE-watchOS
71 | # - script:
72 | # - pod repo update
73 | # - travis_wait 30 pod lib lint Feathers.podspec
74 | # env:
75 | # - JOB=PODSPEC
76 |
--------------------------------------------------------------------------------
/FeathersTests/FeathersSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeathersSpec.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/20/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import ReactiveSwift
12 | import Feathers
13 |
14 | class FeathersSpec: QuickSpec {
15 | override func spec() {
16 | describe("Feathers") {
17 |
18 | var app: Feathers!
19 |
20 | beforeEach {
21 | app = Feathers(provider: StubProvider(data: ["name": "Bob"]))
22 | }
23 |
24 | it("should authenticte successfully") {
25 | var didDispose = false
26 | var didReceiveValue = false
27 | var didComplete = false
28 | var didInterrupt = false
29 | app.authenticate([:]).on(completed: {
30 | didComplete = true
31 | }, interrupted: {
32 | didInterrupt = true
33 | }, disposed: {
34 | didDispose = true
35 | }, value: { value in
36 | didReceiveValue = true
37 | })
38 | .start()
39 | expect(didDispose).toEventually(beTrue())
40 | expect(didInterrupt).toEventually(beFalse())
41 | expect(didReceiveValue).toEventually(beTrue())
42 | expect(didComplete).toEventually(beTrue())
43 | }
44 |
45 | it("should logout successfully") {
46 | var didDispose = false
47 | var didReceiveValue = false
48 | var didComplete = false
49 | var didInterrupt = false
50 | app.logout().on(completed: {
51 | didComplete = true
52 | }, interrupted: {
53 | didInterrupt = true
54 | }, disposed: {
55 | didDispose = true
56 | }, value: { value in
57 | didReceiveValue = true
58 | })
59 | .start()
60 | expect(didDispose).toEventually(beTrue())
61 | expect(didInterrupt).toEventually(beFalse())
62 | expect(didReceiveValue).toEventually(beTrue())
63 | expect(didComplete).toEventually(beTrue())
64 | }
65 |
66 | }
67 |
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/Feathers/Core/ProviderService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProviderService.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/21/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 | import Result
12 |
13 | /// Service that wraps a transport provider.
14 | public class ProviderService: Service {
15 |
16 | private let provider: Provider
17 |
18 | public init(provider: Provider) {
19 | self.provider = provider
20 | super.init()
21 | }
22 |
23 | public override func request(_ method: Service.Method) -> SignalProducer {
24 | let endpoint = constructEndpoint(from: method)
25 | return provider.request(endpoint: endpoint)
26 | }
27 |
28 | override public func on(event: String) -> Signal<[String: Any], NoError> {
29 | return app?.provider.on(event: "\(path) \(event)") ?? .never
30 | }
31 |
32 | override public func once(event: String) -> Signal<[String: Any], NoError> {
33 | return app?.provider.once(event: "\(path) \(event)") ?? .never
34 | }
35 |
36 | override public func off(event: String) {
37 | app?.provider.off(event: event)
38 | }
39 |
40 | override public var supportsRealtimeEvents: Bool {
41 | return provider.supportsRealtimeEvents
42 | }
43 |
44 | // MARK: - Helpers
45 |
46 | /// Given a service method, construct an endpoint.
47 | ///
48 | /// - Parameter method: Service method.
49 | /// - Returns: `Endpoint` object.
50 | private func constructEndpoint(from method: Service.Method) -> Endpoint {
51 | guard let provider = app?.provider else { fatalError("provider must be given to the service before making requests") }
52 | var endpoint = Endpoint(baseURL: provider.baseURL, path: path, method: method, accessToken: nil, authenticationConfiguration: app?.authenticationConfiguration ?? AuthenticationConfiguration())
53 | if let storage = app?.authenticationStorage,
54 | let accessToken = storage.accessToken {
55 | endpoint = Endpoint(baseURL: provider.baseURL,
56 | path: path,
57 | method: method,
58 | accessToken: accessToken,
59 | authenticationConfiguration: app?.authenticationConfiguration ?? AuthenticationConfiguration())
60 | }
61 | return endpoint
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/Feathers/Core/Response.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Response.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/16/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Describes a request's pagination properties.
12 | public struct Pagination {
13 | let total: Int
14 | let limit: Int
15 | let skip: Int
16 |
17 | public init(total: Int, limit: Int, skip: Int) {
18 | self.total = total
19 | self.limit = limit
20 | self.skip = skip
21 | }
22 | }
23 |
24 | /// Encapsulation around the kinds of response data we can receive.
25 | ///
26 | /// - list: Data is in list format (non-paginated).
27 | /// - object: Data is a JSON object (paginated or single entity request i.e. `get`, `update`, `patch`, or `remove`.
28 | public enum ResponseData: CustomDebugStringConvertible, CustomStringConvertible, Equatable {
29 | case list([Any])
30 | case object(Any)
31 |
32 | public var value: Any {
33 | switch self {
34 | case .object(let data): return data
35 | case .list(let data): return data
36 | }
37 | }
38 |
39 | public var description: String {
40 | switch self {
41 | case .list(let data):
42 | return data.reduce("") { $0 + "\n\($1)\n" }
43 | case .object(let object):
44 | return "\(object)"
45 | }
46 | }
47 |
48 | public var debugDescription: String {
49 | return description
50 | }
51 | }
52 |
53 | // Only to be used for testing, does not actually compare equality of the json data
54 | public func == (lhs: ResponseData, rhs: ResponseData) -> Bool {
55 | if case .object = lhs, case .object = rhs {
56 | return true
57 | } else if case .list = lhs, case .list = rhs {
58 | return true
59 | }
60 | return false
61 | }
62 |
63 | /// Encapsulates a successful response from the service.
64 | public struct Response: CustomDebugStringConvertible, CustomStringConvertible {
65 |
66 | /// Pagination information.
67 | public let pagination: Pagination?
68 |
69 | /// The response data.
70 | public let data: ResponseData
71 |
72 | public var description: String {
73 | guard let page = pagination else {
74 | return "Data: \(data)"
75 | }
76 | return "Total: \(page.total)\nLimit: \(page.limit),Skip: \(page.skip)\nData: \(data)"
77 |
78 | }
79 |
80 | public init(pagination: Pagination?, data: ResponseData) {
81 | self.pagination = pagination
82 | self.data = data
83 | }
84 |
85 | public var debugDescription: String {
86 | return description
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/Feathers/Core/ServiceType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceType.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/21/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 | import Result
12 |
13 | /// Interface that all services must conform to.
14 | public protocol ServiceType {
15 |
16 | /// The service's path, unmodifable once created.
17 | var path: String { get }
18 |
19 | /// If the service supports real time events or not.
20 | var supportsRealtimeEvents: Bool { get }
21 |
22 | /// Setup up the service with the necessary dependencies to execute requests.
23 | ///
24 | /// - Parameters:
25 | /// - app: Main feathers app. Services maintain a weak reference.
26 | /// - path: The service path.
27 | func setup(app: Feathers, path: String)
28 |
29 | /// Request data using one of the service methods.
30 | ///
31 | /// - Parameter method: Service method.
32 | /// - Returns: `SignalProducer` that emits a response or errors.
33 | func request(_ method: Service.Method) -> SignalProducer
34 |
35 | /// Register before hooks with the service.
36 | ///
37 | /// - Parameter hooks: Before hooks to register.
38 | func before(_ hooks: Service.Hooks)
39 |
40 | /// Register after hooks with the service.
41 | ///
42 | /// - Parameter hooks: After hooks to register.
43 | func after(_ hooks: Service.Hooks)
44 |
45 | /// Register error hooks with the service.
46 | ///
47 | /// - Parameter hooks: Error hooks to register.
48 | func error(_ hooks: Service.Hooks)
49 |
50 | /// Fetch a service's hooks.
51 | ///
52 | /// - Parameter kind: The kind of hook.
53 | /// - Returns: Hooks registered for `kind`, if any exist.
54 | func hooks(for kind: HookObject.Kind) -> Service.Hooks?
55 |
56 | /// Register for a real-time event to listen for changing data.
57 | /// Signal will continue to emit until disposed.
58 | ///
59 | /// - Parameter event: Real-time event to listen for.
60 | /// - Returns: `Signal` that emits any data. Never errors.
61 | func on(event: String) -> Signal<[String: Any], NoError>
62 |
63 | /// Register for a real-time event but only one time. Signal will emit once
64 | /// then dispose.
65 | ///
66 | /// - Parameter event: Real-time event to listen for.
67 | /// - Returns: Signal that emits a value once, if ever.
68 | func once(event: String) -> Signal<[String: Any], NoError>
69 |
70 | /// Unregister for a real-time event.
71 | ///
72 | /// - Parameter event: Event to unregister for.
73 | func off(event: String)
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/Feathers/Core/Hooks.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Hooks.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/13/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Result
11 | import ReactiveSwift
12 |
13 | /// Hook object that gets passed through hook functions
14 | public struct HookObject {
15 |
16 | /// Represents the kind of hook.
17 | ///
18 | /// - before: Hook is run before the request is made.
19 | /// - after: Hook is run after the request is made.
20 | /// - error: Runs when there's an error.
21 | public enum Kind {
22 | case before, after, error
23 | }
24 |
25 | /// The kind of hook.
26 | public let type: Kind
27 |
28 | /// Feathers application, used to retrieve other services.
29 | public let app: Feathers
30 |
31 | /// The service this hook currently runs on.
32 | public let service: ServiceType
33 |
34 | /// The service method.
35 | public var method: Service.Method
36 |
37 | public var result: Response?
38 |
39 | public var error: FeathersError?
40 |
41 | public init(
42 | type: Kind,
43 | app: Feathers,
44 | service: ServiceType,
45 | method: Service.Method) {
46 | self.type = type
47 | self.app = app
48 | self.service = service
49 | self.method = method
50 | }
51 |
52 | }
53 |
54 | public extension HookObject {
55 |
56 | /// Modify the hook object by adding a result.
57 | ///
58 | /// - Parameter result: Result to add.
59 | /// - Returns: Modified hook object.
60 | func objectByAdding(result: Response) -> HookObject {
61 | var object = self
62 | object.result = result
63 | return object
64 | }
65 |
66 | /// Modify the hook object by attaching an error.
67 | ///
68 | /// - Parameter error: Error to attach.
69 | /// - Returns: Modified hook object.
70 | func objectByAdding(error: FeathersError) -> HookObject {
71 | var object = self
72 | object.error = error
73 | return object
74 | }
75 |
76 | /// Create a new hook object with a new type.
77 | ///
78 | /// - Parameter type: New type.
79 | /// - Returns: A new hook object with copied over properties.
80 | func object(with type: Kind) -> HookObject {
81 | var object = HookObject(type: type, app: app, service: service, method: method)
82 | object.result = result
83 | return object
84 | }
85 |
86 | }
87 |
88 | /// Hook protocol.
89 | public protocol Hook {
90 |
91 | /// Function that's called by the middleware system to run the hook.
92 | ///
93 | /// In order to modify the hook, a copy of it has to be made because
94 | /// Swift function parameters are `let` by default.
95 | ///
96 | /// - Parameters:
97 | /// - hookObject: Hook object.
98 | /// - Returns: `SignalProducer` that emits the modified hook object or errors.
99 | func run(with hookObject: HookObject) -> SignalProducer
100 | }
101 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-watchOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
84 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
84 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/Feathers/Core/Feathers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Feathers.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/15/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 |
12 | /// Main application object. Creates services and provides an interface for authentication.
13 | public final class Feathers {
14 |
15 | /// Transport provider.
16 | public let provider: Provider
17 |
18 | /// Authentication store.
19 | private(set) public var authenticationStorage: AuthenticationStorage = EncryptedAuthenticationStore()
20 |
21 | /// Authentication configuration.
22 | private(set) public var authenticationConfiguration = AuthenticationConfiguration()
23 |
24 | private var services: [String: ServiceType] = [:]
25 |
26 | /// Feather's initializer.
27 | ///
28 | /// - Parameter provider: Transport provider.
29 | public init(provider: Provider) {
30 | self.provider = provider
31 | provider.setup(app: self)
32 | }
33 |
34 | /// Create a service for the given path.
35 | ///
36 | /// This method uses some fancy indirection and wraps every service in a `ServiceWrapper` instance
37 | /// that's responsible for running the hook chain and proxying methods to the wrapped service.
38 | ///
39 | /// - Parameter path: Service path.
40 | /// - Returns: Service object.
41 | public func service(path: String) -> ServiceType {
42 | let servicePath = path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
43 | guard let service = services[servicePath] else {
44 | // If no service has been registered or requested for at this path,
45 | // create one around the transport provider.
46 | let providerService = ProviderService(provider: provider)
47 | providerService.setup(app: self, path: servicePath)
48 | // Store it so the service is retained and hooks can be registered.
49 | services[servicePath] = providerService
50 | // Create the wrapper
51 | let wrapper = ServiceWrapper(service: providerService)
52 | wrapper.setup(app: self, path: servicePath)
53 | return wrapper
54 | }
55 | let wrapper = ServiceWrapper(service: service)
56 | wrapper.setup(app: self, path: servicePath)
57 | return wrapper
58 | }
59 |
60 | public func use(path: String, service: ServiceType) {
61 | let servicePath = path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
62 | service.setup(app: self, path: servicePath)
63 | services[servicePath] = service
64 | }
65 |
66 | /// Configure any authentication options.
67 | ///
68 | /// - Parameter configuration: Authentication configuration object.
69 | public func configure(auth configuration: AuthenticationConfiguration) {
70 | authenticationConfiguration = configuration
71 | authenticationStorage = EncryptedAuthenticationStore(storageKey: configuration.storageKey)
72 | }
73 |
74 | /// Authenticate the application.
75 | ///
76 | /// - Parameters:
77 | /// - credentials: Credentials to authenticate with.
78 | /// - Returns: Promise that emits an access token.
79 | public func authenticate(_ credentials: [String: Any]) -> SignalProducer<[String: Any], AnyFeathersError> {
80 | return provider.authenticate(authenticationConfiguration.path, credentials: credentials)
81 | .flatMap(.latest) { response -> SignalProducer<[String: Any], AnyFeathersError> in
82 | if case let .object(object) = response.data,
83 | let json = object as? [String: Any] {
84 | return SignalProducer(value: json)
85 | }
86 | return SignalProducer(error: AnyFeathersError(FeathersNetworkError.unknown))
87 | }.on(failed: { [weak self] _ in
88 | self?.authenticationStorage.accessToken = nil
89 | }, value: { [weak self] value in
90 | guard let token = value["accessToken"] as? String else { return }
91 | self?.authenticationStorage.accessToken = token
92 | })
93 | }
94 |
95 | /// Log out the application.
96 | ///
97 | /// - Returns: Promise that emits a response.
98 | public func logout() -> SignalProducer {
99 | return provider.logout(path: authenticationConfiguration.path)
100 | .on(value: { [weak self] _ in
101 | self?.authenticationStorage.accessToken = nil
102 | })
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/xcshareddata/xcschemes/Feathers-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
46 |
47 |
49 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
81 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
100 |
106 |
107 |
108 |
109 |
111 |
112 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/Feathers/Core/FeathersError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FeathersError.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/16/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol FeathersError: Swift.Error {
12 |
13 | var description: String { get }
14 | }
15 |
16 | /// Type erase any errors
17 | public struct AnyFeathersError: Error {
18 | /// The underlying error.
19 | public let error: FeathersError
20 |
21 | public init(_ error: FeathersError) {
22 | if let anyError = error as? AnyFeathersError {
23 | self = anyError
24 | } else {
25 | self.error = error
26 | }
27 | }
28 | }
29 |
30 | public extension FeathersError {
31 |
32 | var description: String {
33 | if let networkError = self as? FeathersNetworkError {
34 | return networkError.errorMessage
35 | } else if let serviceError = self as? JuneFeathersError {
36 | return serviceError.message
37 | } else {
38 | return self.localizedDescription
39 | }
40 | }
41 | }
42 |
43 | public enum JuneFeathersError: FeathersError, Equatable {
44 |
45 | case illegal
46 |
47 | init() {
48 | self = .illegal
49 | }
50 |
51 | }
52 |
53 | extension JuneFeathersError {
54 |
55 | var message: String {
56 | return "Your requests can't be processed at this time"
57 | }
58 | }
59 |
60 | public enum FeathersNetworkError: FeathersError, Equatable {
61 |
62 | case badRequest
63 | case notAuthenticated
64 | case paymentError
65 | case forbidden
66 | case notFound
67 | case methodNotAllowed
68 | case notAcceptable
69 | case timeout
70 | case conflict
71 | case lengthRequired
72 | case unprocessable
73 | case tooManyRequests
74 | case general
75 | case notImplemented
76 | case badGateway
77 | case unavailable
78 | case underlying(Error)
79 | case unknown
80 |
81 | public init?(statusCode: Int) {
82 | switch statusCode {
83 | case 400: self = .badRequest
84 | case 401: self = .notAuthenticated
85 | case 403: self = .forbidden
86 | case 404: self = .notFound
87 | case 405: self = .methodNotAllowed
88 | case 406: self = .notAcceptable
89 | case 408: self = .timeout
90 | case 409: self = .conflict
91 | case 411: self = .lengthRequired
92 | case 422: self = .unprocessable
93 | case 429: self = .tooManyRequests
94 | case 500: self = .general
95 | case 501: self = .notImplemented
96 | case 502: self = .badGateway
97 | case 503: self = .unavailable
98 | default: return nil
99 | }
100 | }
101 |
102 | public init(error: Error) {
103 | self = .underlying(error)
104 | }
105 |
106 | }
107 |
108 | extension FeathersNetworkError {
109 |
110 | var errorMessage: String {
111 | switch self {
112 | case .badRequest: return "Error #400. Failded to execute your requests."
113 | case .notAuthenticated: return "Error #401. Not Authenticated. Please Log In."
114 | case .forbidden: return "Error #403. Failed to execute your request."
115 | case .notFound: return "Error #404. Failed to execute your request."
116 | case .methodNotAllowed: return "Error #405. Failed to execute your request."
117 | case .notAcceptable: return "Error #406. Failed to execute your request."
118 | case .timeout: return "Error #408. Timed out. Try again later."
119 | case .conflict: return "Error #409. Failed to execute your requests."
120 | case .lengthRequired: return "Error #411. Failed to execute your requests."
121 | case .unprocessable: return "Error #422. Failed to execute your requests."
122 | case .tooManyRequests: return "Error #429. You made too many requests. Try again later."
123 | case .general: return "Error #500. Internal Server Error. Try again later."
124 | case .notImplemented: return "Error #501. Internal Server Error."
125 | case .badGateway: return "Error #502. Internal Server Error."
126 | case .unavailable: return "Error #503. Server temporary unavailable. Try again later."
127 | default: return "Unspecified error occured."
128 | }
129 | }
130 |
131 | }
132 |
133 | public func == (lhs: FeathersNetworkError, rhs: FeathersNetworkError) -> Bool {
134 | switch (lhs, rhs) {
135 | case (.badRequest, .badRequest): return true
136 | case (.notAuthenticated, .notAuthenticated): return true
137 | case (.paymentError, .paymentError): return true
138 | case (.forbidden, .forbidden): return true
139 | case (.notFound, .notFound): return true
140 | case (.methodNotAllowed, .methodNotAllowed): return true
141 | case (.notAcceptable, .notAcceptable): return true
142 | case (.timeout, .timeout): return true
143 | case (.conflict, .conflict): return true
144 | case (.lengthRequired, .lengthRequired): return true
145 | case (.unprocessable, .unprocessable): return true
146 | case (.tooManyRequests, .tooManyRequests): return true
147 | case (.general, .general): return true
148 | case (.notImplemented, .notImplemented): return true
149 | case (.badGateway, .badGateway): return true
150 | case (.unavailable, .unavailable): return true
151 | case (.underlying(_), .underlying(_)): return true
152 | case (.unknown, .unknown): return true
153 | default: return false
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/Feathers/Core/ServiceWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceWrapper.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/21/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 | import enum Result.NoError
12 |
13 | /// Wraps a given service and executes all hooks and the service request.
14 | /// This class exists so that users can write their own services and
15 | /// not have to worry about running before, after, and error hooks themselves.
16 | /// By wrapping the given service, the service wrapper can manage all
17 | /// request execution. Also serves as a proxy to the underlying service.
18 | final public class ServiceWrapper: ServiceType {
19 |
20 | /// Weak reference to main feathers app.
21 | final private weak var app: Feathers?
22 |
23 | /// Internally wrapped service.
24 | final private let service: ServiceType
25 |
26 | // MARK: - Initialization
27 |
28 | internal init(service: ServiceType) {
29 | self.service = service
30 | }
31 |
32 | // MARK: - ServiceType
33 |
34 | final public func setup(app: Feathers, path: String) {
35 | self.app = app
36 | }
37 |
38 | final public var path: String {
39 | return service.path
40 | }
41 |
42 | final public var supportsRealtimeEvents: Bool {
43 | return service.supportsRealtimeEvents
44 | }
45 |
46 | /// Execute a request against the wrapped service and runs all hooks registered.
47 | ///
48 | /// The execution flow of this method is as follows:
49 | ///
50 | /// before hooks -> service request -> after hooks
51 | ///
52 | /// If at any point an error is emitted or `hookObject.error` is set manually, the chain
53 | /// immediately stops and runs the registered error hooks.
54 | ///
55 | /// If `hookObject.result` is set, the service request will be skipped and after hooks will run.
56 | /// - Parameter method: Service method to execute.
57 | /// - Returns: `SignalProducer` that emits a response.
58 | final public func request(_ method: Service.Method) -> SignalProducer {
59 | guard let application = app else {
60 | print("feathers: no application exists on the service. `setup` was not called.")
61 | return .interrupted
62 | }
63 | // Closure that maps all registered hooks into a single producer.
64 | let reduceHooksClosure: (SignalProducer, Hook) -> SignalProducer = { acc, current in
65 | return acc.flatMap(.concat) { value in
66 | return current.run(with: value)
67 | }
68 | }
69 | let beforeHookObject = HookObject(type: .before, app: application, service: service, method: method)
70 | // Get all the hooks
71 | let beforeHooks = service.hooks(for: .before)?.hooks(for: method) ?? []
72 | let afterHooks = service.hooks(for: .after)?.hooks(for: method) ?? []
73 | let errorHooks = service.hooks(for: .error)?.hooks(for: method) ?? []
74 | // Build up the before chains
75 | let beforeChain = beforeHooks.reduce(SignalProducer(value: beforeHookObject), reduceHooksClosure)
76 |
77 | let chain = beforeChain.flatMap(.concat) { [weak self] hook -> SignalProducer in
78 | guard let vSelf = self else {
79 | return .interrupted
80 | }
81 | // If the result is set, skip the service request.
82 | if let _ = hook.result {
83 | let afterHookObject = hook.object(with: .after)
84 | let afterChain = afterHooks.reduce(SignalProducer(value: afterHookObject), reduceHooksClosure)
85 | return afterChain.flatMap(.concat) {
86 | return $0.result != nil ? SignalProducer(value: $0.result!) : SignalProducer(error: AnyFeathersError(FeathersNetworkError.unknown))
87 | }
88 | // If the error is set, error out.
89 | } else if let error = hook.error {
90 | return SignalProducer(error: AnyFeathersError(error))
91 | } else {
92 | // Otherwise, execute the service request.
93 | return vSelf.service.request(hook.method)
94 | // When the service request has completed and emitted a response,
95 | // run all the after hooks.
96 | .flatMap(.latest) { response -> SignalProducer in
97 | let afterHookObject = hook.object(with: .after).objectByAdding(result: response)
98 | let afterChain = afterHooks.reduce(SignalProducer(value: afterHookObject), reduceHooksClosure)
99 | return afterChain.flatMap(.concat) { value in
100 | return value.result != nil ? SignalProducer(value: value.result!) : SignalProducer(error: AnyFeathersError(FeathersNetworkError.unknown))
101 | }
102 | }
103 | }
104 | }
105 | // If at any point along the chain an error is emitted, recover by running all the error hooks.
106 | return chain.flatMapError { [weak self] error -> SignalProducer in
107 | guard let vSelf = self else { return SignalProducer(error: AnyFeathersError(FeathersNetworkError.unknown)) }
108 | let errorHookObject = HookObject(type: .error, app: application, service: vSelf, method: method).objectByAdding(error: error.error)
109 | let errorChain = errorHooks.reduce(SignalProducer(value: errorHookObject), reduceHooksClosure)
110 | return errorChain.flatMap(.concat) { hookObject -> SignalProducer in
111 | // If the hook error exists, send that to the user, otherwise emit the original error that caused error hooks to run.
112 | return hookObject.error != nil ? SignalProducer(error: AnyFeathersError(hookObject.error!)) : SignalProducer(error: AnyFeathersError(error.error))
113 | }
114 | }
115 | }
116 |
117 | final public func before(_ hooks: Service.Hooks) {
118 | service.before(hooks)
119 | }
120 |
121 | final public func after(_ hooks: Service.Hooks) {
122 | service.after(hooks)
123 | }
124 |
125 | final public func error(_ hooks: Service.Hooks) {
126 | service.error(hooks)
127 | }
128 |
129 | final public func hooks(for kind: HookObject.Kind) -> Service.Hooks? {
130 | return service.hooks(for: kind)
131 | }
132 |
133 | final public func on(event: String) -> Signal<[String: Any], NoError> {
134 | return service.on(event: event)
135 | }
136 |
137 | final public func once(event: String) -> Signal<[String: Any], NoError> {
138 | return service.once(event: event)
139 | }
140 |
141 | final public func off(event: String) {
142 | service.off(event: event)
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/Feathers/Core/Service.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Service.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 4/15/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactiveSwift
11 | import Result
12 |
13 | /// Represents a subclassable Feather's service. `Service` is not to be
14 | /// used by itself; it's an abstract base class for all other service to inherit from.
15 | /// To use a service, see `ProviderService` or create your own!
16 | open class Service: ServiceType {
17 |
18 | /// Service methods.
19 | ///
20 | /// - find: Retrieves a list of all resources from the service, can be filtered.
21 | /// - get: Retrieves a single resource with the given id from the service.
22 | /// - create: Creates a new resource with data.
23 | /// - update: Replaces the resource identified by id with data.
24 | /// `id` may be nil when updating a list, indicating multiple entities with query parameters.
25 | /// - patch: Merges the existing data of the resource identified by id with the new data.
26 | /// `id` may be nil when patching a list, indicating multiple entities with query parameters.
27 | /// - remove: Removes the resource with id. `id` may be nil when deleting a list,
28 | /// indicating multiple entities with query parameters.
29 | public enum Method {
30 |
31 | case find(query: Query?)
32 | case get(id: String, query: Query?)
33 | case create(data: [String: Any], query: Query?)
34 | case update(id: String?, data: [String: Any], query: Query?)
35 | case patch(id: String?, data: [String: Any], query: Query?)
36 | case remove(id: String?, query: Query?)
37 |
38 | }
39 |
40 | // MARK: - Hooks
41 |
42 | /// Service hooks that can be registered with the service.
43 | public struct Hooks {
44 |
45 | /// Hooks for all service methods.
46 | public let all: [Hook]
47 |
48 | /// Hooks for `.find` requests.
49 | public let find: [Hook]
50 |
51 | /// Hooks for `.get` requests.
52 | public let get: [Hook]
53 |
54 | /// Hooks for `.create` requests.
55 | public let create: [Hook]
56 |
57 | /// Hooks for `.update` requests.
58 | public let update: [Hook]
59 |
60 | /// Hooks for `.patch` requests.
61 | public let patch: [Hook]
62 |
63 | /// Hooks for `.remove` requests.
64 | public let remove: [Hook]
65 |
66 | /// True if the object contains any hooks, false otherwise.
67 | public var isEmpty: Bool {
68 | return all.isEmpty
69 | && find.isEmpty
70 | && get.isEmpty
71 | && create.isEmpty
72 | && update.isEmpty
73 | && patch.isEmpty
74 | && remove.isEmpty
75 | }
76 |
77 | /// Service hooks initializer.
78 | ///
79 | /// - Parameters:
80 | /// - kind: The kind of hooks.
81 | /// - all: Hooks to run on all service methods.
82 | /// - find: Find hooks.
83 | /// - get: Get hooks.
84 | /// - create: Create hooks.
85 | /// - update: Update hooks.
86 | /// - patch: Patch hooks.
87 | /// - remove: Remove hooks.
88 | public init(
89 | all: [Hook] = [],
90 | find: [Hook] = [],
91 | get: [Hook] = [],
92 | create: [Hook] = [],
93 | update: [Hook] = [],
94 | patch: [Hook] = [],
95 | remove: [Hook] = []) {
96 | self.all = all
97 | self.find = find
98 | self.get = get
99 | self.create = create
100 | self.update = update
101 | self.patch = patch
102 | self.remove = remove
103 | }
104 |
105 | /// Create a new hook object by merging hooks together.
106 | ///
107 | /// - Parameter hooks: Hooks to add.
108 | /// - Returns: New `Hooks` object.
109 | public func add(hooks: Hooks) -> Hooks {
110 | return Hooks(
111 | all: all + hooks.all,
112 | find: find + hooks.find,
113 | get: get + hooks.get,
114 | create: create + hooks.create,
115 | update: update + hooks.update,
116 | patch: patch + hooks.patch,
117 | remove: remove + hooks.remove)
118 | }
119 |
120 | }
121 |
122 | /// Service before hooks.
123 | private var beforeHooks = Hooks()
124 |
125 | /// Servie after hooks.
126 | private var afterHooks = Hooks()
127 |
128 | /// Service error hooks.
129 | private var errorHooks = Hooks()
130 |
131 | /// Weak reference to the main feathers app.
132 | private(set) public weak var app: Feathers?
133 |
134 | // MARK: - Initialization
135 |
136 | public init() {}
137 |
138 | // MARK: - ServiceType
139 |
140 | /// The service path.
141 | private(set) public var path: String = ""
142 |
143 | open func setup(app: Feathers, path: String) {
144 | self.app = app
145 | self.path = path
146 | }
147 |
148 | open func request(_ method: Service.Method) -> SignalProducer {
149 | fatalError("Must be overriden by a subclass")
150 | }
151 |
152 | final public func before(_ hooks: Hooks) {
153 | beforeHooks = beforeHooks.add(hooks: hooks)
154 | }
155 |
156 | final public func after(_ hooks: Hooks) {
157 | afterHooks = afterHooks.add(hooks: hooks)
158 | }
159 |
160 | final public func error(_ hooks: Hooks) {
161 | errorHooks = errorHooks.add(hooks: hooks)
162 | }
163 |
164 | final public func hooks(for kind: HookObject.Kind) -> Service.Hooks? {
165 | switch kind {
166 | case .before: return beforeHooks.isEmpty ? nil : beforeHooks
167 | case .after: return afterHooks.isEmpty ? nil : afterHooks
168 | case .error: return errorHooks.isEmpty ? nil : errorHooks
169 | }
170 | }
171 |
172 | public func on(event: String) -> Signal<[String: Any], NoError> {
173 | // no-op
174 | return .empty
175 | }
176 |
177 | public func once(event: String) -> Signal<[String: Any], NoError> {
178 | return .empty
179 | }
180 |
181 | public func off(event: String) {
182 | // no-op
183 | }
184 |
185 | public var supportsRealtimeEvents: Bool {
186 | return false
187 | }
188 |
189 | }
190 |
191 | internal extension Service.Hooks {
192 |
193 | /// Internal extension for grabbing all the hooks for a given method.
194 | ///
195 | /// - Parameter method: Service method.
196 | /// - Returns: A list of hooks registered for that service method.
197 | func hooks(for method: Service.Method) -> [Hook] {
198 | switch method {
199 | case .find: return all + find
200 | case .get: return all + get
201 | case .create: return all + create
202 | case .update: return all + update
203 | case .patch: return all + patch
204 | case .remove: return all + remove
205 | }
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/FeathersTests/QuerySpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QuerySpec.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/25/17.
6 | // Copyright © 2017 Swoopy Studios. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import Foundation
12 | import Feathers
13 |
14 | class QuerySpec: QuickSpec {
15 |
16 | override func spec() {
17 |
18 | describe("Query") {
19 |
20 | it("should serialize a limit query") {
21 | let query = Query().limit(5)
22 | let serialized = query.serialize()
23 | expect(NSDictionary(dictionary: serialized).isEqual(to: ["$limit": 5])).to(beTrue())
24 | }
25 |
26 | it("should serialize a skip query") {
27 | let query = Query().skip(5)
28 | let serialized = query.serialize()
29 | expect(NSDictionary(dictionary: serialized).isEqual(to: ["$skip": 5])).to(beTrue())
30 | }
31 |
32 | it("should serialize a single sort") {
33 | let query = Query().sort(property: "name", ordering: .orderedAscending)
34 | let serialized = query.serialize()
35 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
36 | "$sort": [
37 | "name": 1
38 | ]
39 | ])).to(beTrue())
40 | }
41 |
42 | it("should serialize a multiple sorts") {
43 | let query = Query().sort(property: "name", ordering: .orderedAscending).sort(property: "age", ordering: .orderedDescending)
44 | let serialized = query.serialize()
45 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
46 | "$sort": [
47 | "name": 1,
48 | "age": -1
49 | ]
50 | ])).to(beTrue())
51 | }
52 |
53 | it("should serialize a gt query") {
54 | let query = Query().gt(property: "age", value: 5)
55 | let serialized = query.serialize()
56 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
57 | "age": [
58 | "$gt": 5
59 | ]
60 | ])).to(beTrue())
61 | }
62 |
63 | it("should serialize a gte query") {
64 | let query = Query().gte(property: "age", value: 5)
65 | let serialized = query.serialize()
66 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
67 | "age": [
68 | "$gte": 5
69 | ]
70 | ])).to(beTrue())
71 | }
72 |
73 | it("should serialize a lt query") {
74 | let query = Query().lt(property: "age", value: 5)
75 | let serialized = query.serialize()
76 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
77 | "age": [
78 | "$lt": 5
79 | ]
80 | ])).to(beTrue())
81 | }
82 |
83 | it("should serialize a lte query") {
84 | let query = Query().lte(property: "age", value: 5)
85 | let serialized = query.serialize()
86 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
87 | "age": [
88 | "$lte": 5
89 | ]
90 | ])).to(beTrue())
91 | }
92 |
93 | it("should serialize an in query") {
94 | let query = Query().in(property: "age", values: [5, 10, 15])
95 | let serialized = query.serialize()
96 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
97 | "age": [
98 | "$in": [5, 10, 15]
99 | ]
100 | ])).to(beTrue())
101 | }
102 |
103 | it("should serialize a nin query") {
104 | let query = Query().nin(property: "age", values: [5, 10, 15])
105 | let serialized = query.serialize()
106 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
107 | "age": [
108 | "$nin": [5, 10, 15]
109 | ]
110 | ])).to(beTrue())
111 | }
112 |
113 | it("should serialize a ne query") {
114 | let query = Query().ne(property: "age", value: 10)
115 | let serialized = query.serialize()
116 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
117 | "age": [
118 | "$ne": 10
119 | ]
120 | ])).to(beTrue())
121 | }
122 |
123 | it("should serialize an eq query") {
124 | let query = Query().eq(property: "age", value: 10)
125 | let serialized = query.serialize()
126 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
127 | "age": 10
128 | ])).to(beTrue())
129 | }
130 |
131 | it("should serialize a select query") {
132 | let query = Query().select(property: "name")
133 | let serialized = query.serialize()
134 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
135 | "$select": ["name"]
136 | ])).to(beTrue())
137 | }
138 |
139 | it("should serialize a select query with multiple fields") {
140 | let query = Query().select(properties: ["name", "age"])
141 | let serialized = query.serialize()
142 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
143 | "$select": ["name", "age"]
144 | ])).to(beTrue())
145 | }
146 |
147 | it("should serialize an or query") {
148 | let query = Query().or(subqueries: [
149 | "name":.ne("bob"),
150 | "age": .`in`([18, 42])
151 | ])
152 | let serialized = query.serialize()
153 | print(serialized)
154 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
155 | "$or": [
156 | [
157 | "name": [
158 | "$ne": "bob"
159 | ]
160 | ],
161 | [
162 | "age": [
163 | "$in": [18, 42]
164 | ]
165 | ]
166 | ]
167 | ])).to(beTrue())
168 | }
169 |
170 | it("should serialize multiple queries") {
171 | let query = Query().limit(5).gt(property: "name", value: "bob").lt(property: "age", value: 5)
172 | let serialized = query.serialize()
173 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
174 | "$limit": 5,
175 | "name": [
176 | "$gt": "bob"
177 | ],
178 | "age": [
179 | "$lt": 5
180 | ]
181 | ])).to(beTrue())
182 | }
183 |
184 | it("should serialize multiple subqueries on the same property") {
185 | let query = Query().gt(property: "age", value: 18).lt(property: "age", value: 100)
186 | let serialized = query.serialize()
187 | expect(NSDictionary(dictionary: serialized).isEqual(to: [
188 | "age": [
189 | "$gt": 18,
190 | "$lt": 100
191 | ]
192 | ])).to(beTrue())
193 | }
194 |
195 |
196 | }
197 |
198 | }
199 |
200 | }
201 |
--------------------------------------------------------------------------------
/Feathers/Core/Query.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Query.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/25/17.
6 | // Copyright © 2017 Swoopy Studios. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Query {
12 |
13 | public struct Sort {
14 | let property: String
15 | let ordering: ComparisonResult
16 | }
17 |
18 | public enum PropertySubquery {
19 | case gt(Any)
20 | case gte(Any)
21 | case lt(Any)
22 | case lte(Any)
23 | case `in`([Any])
24 | case nin([Any])
25 | case ne(Any)
26 | case eq(Any)
27 | }
28 |
29 | public let limit: Int?
30 | public let skip: Int?
31 | public let sorts: [Sort]
32 | public let propertyQueries: [String: [PropertySubquery]]
33 | public let selected: [String]
34 | public let orQuery: [String: PropertySubquery]
35 |
36 | public init() {
37 | self.limit = nil
38 | self.skip = nil
39 | self.sorts = []
40 | self.propertyQueries = [:]
41 | self.selected = []
42 | self.orQuery = [:]
43 | }
44 |
45 | private init(
46 | limit: Int? = nil,
47 | skip: Int? = nil,
48 | sorts: [Sort] = [],
49 | propertyQueries: [String: [PropertySubquery]] = [:],
50 | selected: [String] = [],
51 | orQuery: [String: PropertySubquery] = [:]) {
52 | self.limit = limit
53 | self.skip = skip
54 | self.sorts = sorts
55 | self.propertyQueries = propertyQueries
56 | self.selected = selected
57 | self.orQuery = orQuery
58 | }
59 |
60 | public func limit(_ newLimit: Int) -> Query {
61 | return Query(limit: newLimit, skip: skip, sorts: sorts, propertyQueries: propertyQueries, selected: selected, orQuery: orQuery)
62 | }
63 |
64 | public func skip(_ newSkip: Int) -> Query {
65 | return Query(limit: limit, skip: newSkip, sorts: sorts, propertyQueries: propertyQueries, selected: selected, orQuery: orQuery)
66 | }
67 |
68 | public func gt(property: String, value: Any) -> Query {
69 | var queries = propertyQueries
70 | queries[property] = queries[property] == nil ? [] : queries[property]
71 | queries[property]?.append(.gt(value))
72 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
73 | }
74 |
75 | public func gte(property: String, value: Any) -> Query {
76 | var queries = propertyQueries
77 | queries[property] = queries[property] == nil ? [] : queries[property]
78 | queries[property]?.append(.gte(value))
79 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
80 | }
81 |
82 | public func lt(property: String, value: Any) -> Query {
83 | var queries = propertyQueries
84 | queries[property] = queries[property] == nil ? [] : queries[property]
85 | queries[property]?.append(.lt(value))
86 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
87 | }
88 |
89 | public func lte(property: String, value: Any) -> Query {
90 | var queries = propertyQueries
91 | queries[property] = queries[property] == nil ? [] : queries[property]
92 | queries[property]?.append(.lte(value))
93 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
94 | }
95 |
96 | public func `in`(property: String, values: [Any]) -> Query {
97 | var queries = propertyQueries
98 | queries[property] = queries[property] == nil ? [] : queries[property]
99 | queries[property]?.append(.`in`(values))
100 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
101 | }
102 |
103 | public func nin(property: String, values: [Any]) -> Query {
104 | var queries = propertyQueries
105 | queries[property] = queries[property] == nil ? [] : queries[property]
106 | queries[property]?.append(.nin(values))
107 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
108 | }
109 |
110 | public func eq(property: String, value: Any) -> Query {
111 | var queries = propertyQueries
112 | queries[property] = queries[property] == nil ? [] : queries[property]
113 | queries[property]?.append(.eq(value))
114 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
115 | }
116 |
117 | public func ne(property: String, value: Any) -> Query {
118 | var queries = propertyQueries
119 | queries[property] = queries[property] == nil ? [] : queries[property]
120 | queries[property]?.append(.ne(value))
121 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: queries, selected: selected, orQuery: orQuery)
122 | }
123 |
124 | public func sort(property: String, ordering: ComparisonResult) -> Query {
125 | var sortList = sorts
126 | sortList.append(Sort(property: property, ordering: ordering))
127 | return Query(limit: limit, skip: skip, sorts: sortList, propertyQueries: propertyQueries, selected: selected, orQuery: orQuery)
128 | }
129 |
130 | public func select(property: String) -> Query {
131 | var selectedFields = selected
132 | selectedFields.append(property)
133 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: propertyQueries, selected: selectedFields, orQuery: orQuery)
134 | }
135 |
136 | public func select(properties: [String]) -> Query {
137 | var selectedFields = selected
138 | selectedFields += properties
139 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: propertyQueries, selected: selectedFields, orQuery: orQuery)
140 | }
141 |
142 | public func or(subqueries: [String: PropertySubquery]) -> Query {
143 | return Query(limit: limit, skip: skip, sorts: sorts, propertyQueries: propertyQueries, selected: selected, orQuery: subqueries)
144 | }
145 |
146 | public func serialize() -> [String: Any] {
147 | var dictionary: [String: Any] = [:]
148 | // Add any limit
149 | if let limit = limit {
150 | dictionary["$limit"] = limit
151 | }
152 | // Add any skip
153 | if let skip = skip {
154 | dictionary["$skip"] = skip
155 | }
156 | // Seralize sorts
157 | for sort in sorts {
158 | var previousSorts = dictionary["$sort"] as? [String: Int] ?? [:]
159 | switch sort.ordering {
160 | case .orderedAscending:
161 | previousSorts[sort.property] = 1
162 | case .orderedDescending:
163 | previousSorts[sort.property] = -1
164 | case .orderedSame:
165 | previousSorts[sort.property] = 0
166 | }
167 | dictionary["$sort"] = previousSorts
168 | }
169 | for (property, subqueries) in propertyQueries {
170 | var propertyQueries = dictionary[property] as? [String: Any] ?? [:]
171 | var isSubquery = true
172 | for subquery in subqueries {
173 | switch subquery {
174 | case let .gt(value):
175 | propertyQueries["$gt"] = value
176 | case let .gte(value):
177 | propertyQueries["$gte"] = value
178 | case let .lt(value):
179 | propertyQueries["$lt"] = value
180 | case let .lte(value):
181 | propertyQueries["$lte"] = value
182 | case let .`in`(values):
183 | propertyQueries["$in"] = values
184 | case let .nin(values):
185 | propertyQueries["$nin"] = values
186 | case let .ne(value):
187 | propertyQueries["$ne"] = value
188 | case let .eq(value):
189 | dictionary[property] = value
190 | isSubquery = false
191 | }
192 | }
193 | if isSubquery {
194 | dictionary[property] = propertyQueries
195 | }
196 | }
197 |
198 | if !selected.isEmpty {
199 | dictionary["$select"] = selected
200 | }
201 |
202 | var orQueries: [[String: Any]] = []
203 | for (property, subquery) in orQuery {
204 | var propertyQuery: [String: Any] = [:]
205 | switch subquery {
206 | case let .gt(value):
207 | propertyQuery[property] = ["$gt": value]
208 | case let .gte(value):
209 | propertyQuery[property] = ["$gte": value]
210 | case let .lt(value):
211 | propertyQuery[property] = ["$lt": value]
212 | case let .lte(value):
213 | propertyQuery[property] = ["$lte": value]
214 | case let .`in`(values):
215 | propertyQuery[property] = ["$in": values]
216 | case let .nin(values):
217 | propertyQuery[property] = ["$nin": values]
218 | case let .ne(value):
219 | propertyQuery[property] = ["$ne": value]
220 | case let .eq(value):
221 | propertyQuery[property] = value
222 | }
223 | orQueries.append(propertyQuery)
224 | }
225 |
226 | if !orQueries.isEmpty {
227 | dictionary["$or"] = orQueries
228 | }
229 |
230 | return dictionary
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/FeathersTests/ServiceSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ServiceSpec.swift
3 | // Feathers
4 | //
5 | // Created by Brendan Conron on 5/18/17.
6 | // Copyright © 2017 FeathersJS. All rights reserved.
7 | //
8 |
9 | import Quick
10 | import Nimble
11 | import Feathers
12 |
13 | class ServiceSpec: QuickSpec {
14 |
15 | override func spec() {
16 |
17 | describe("Service") {
18 |
19 | var app: Feathers!
20 | var service: ServiceType!
21 |
22 | beforeEach {
23 | app = Feathers(provider: StubProvider(data: ["name": "Bob"]))
24 | service = app.service(path: "users")
25 | }
26 |
27 | it("should stub the request") {
28 | print(service)
29 | var error: Error?
30 | var response: Response?
31 | var data: [String: String]?
32 | service.request(.find(query: nil))
33 | .on(failed: {
34 | error = $0
35 | }, value: {
36 | response = $0
37 | data = $0.data.value as? [String: String]
38 | print($0)
39 | })
40 | .start()
41 | expect(error).toEventually(beNil())
42 | expect(response).toEventuallyNot(beNil())
43 | expect(data).toEventuallyNot(beNil())
44 | expect(data).to(equal(["name": "Bob"]))
45 | }
46 |
47 | describe("Hooks") {
48 |
49 | context("before") {
50 |
51 | var beforeHooks: Service.Hooks!
52 |
53 | beforeEach {
54 | beforeHooks = Service.Hooks(all: [StubHook(data: .object(["name": "Henry"]))])
55 | service.before(beforeHooks)
56 | }
57 |
58 | it("should run the before hook and skip the request") {
59 | var error: Error?
60 | var response: Response?
61 | var data: [String: String]?
62 | service.request(.find(query: nil))
63 | .on(failed: {
64 | error = $0
65 | }, value: {
66 | response = $0
67 | data = $0.data.value as? [String: String]
68 | })
69 | .start()
70 | expect(error).toEventually(beNil())
71 | expect(response).toEventuallyNot(beNil())
72 | expect(data).toEventuallyNot(beNil())
73 | expect(data).to(equal(["name": "Henry"]))
74 | }
75 |
76 | }
77 |
78 | context("after") {
79 |
80 | var afterHooks: Service.Hooks!
81 |
82 | beforeEach {
83 | afterHooks = Service.Hooks(all: [PopuplateDataAfterHook(data: ["name": "Susie"])])
84 | service.after(afterHooks)
85 | }
86 |
87 | it("should change the response") {
88 | var error: Error?
89 | var response: Response?
90 | var data: [String: String]?
91 | service.request(.find(query: nil))
92 | .on(failed: {
93 | error = $0
94 | }, value: {
95 | response = $0
96 | data = $0.data.value as? [String: String]
97 | })
98 | .start()
99 | expect(error).toEventually(beNil())
100 | expect(response).toEventuallyNot(beNil())
101 | expect(data).toEventuallyNot(beNil())
102 | expect(data).to(equal(["name": "Susie"]))
103 | }
104 |
105 | }
106 |
107 | context("error") {
108 |
109 | var beforeHooks: Service.Hooks!
110 |
111 | beforeEach {
112 | // Force the hook to error with ErrorHook
113 | beforeHooks = Service.Hooks(all: [ErrorHook(error:FeathersNetworkError.unknown)])
114 | service.before(beforeHooks)
115 | }
116 |
117 | context("when a hook rejects with an error") {
118 |
119 | it("should pass through the original error") {
120 | var error: FeathersNetworkError?
121 | var response: Response?
122 | var data: [String: String]?
123 | service.request(.find(query: nil))
124 | .on(failed: {
125 | error = $0.error as? FeathersNetworkError
126 | }, value: {
127 | response = $0
128 | data = $0.data.value as? [String: String]
129 | })
130 | .start()
131 | expect(error).toEventuallyNot(beNil())
132 | expect(error).toEventually(equal(FeathersNetworkError.unknown))
133 | expect(response).toEventually(beNil())
134 | expect(data).toEventually(beNil())
135 | }
136 |
137 | context("when given an error hook that rejects with an error") {
138 |
139 | var errorHooks: Service.Hooks!
140 |
141 | beforeEach {
142 | errorHooks = Service.Hooks(all: [ErrorHook(error: FeathersNetworkError.unavailable), ErrorHook(error: FeathersNetworkError.unknown)])
143 | service.error(errorHooks)
144 | }
145 |
146 | it("should be able to modify the final error and skip the rest of the chain") {
147 | var error: FeathersNetworkError?
148 | var response: Response?
149 | var data: [String: String]?
150 | service.request(.find(query: nil))
151 | .on(failed: {
152 | error = $0.error as? FeathersNetworkError
153 | }, value: {
154 | response = $0
155 | data = $0.data.value as? [String: String]
156 | })
157 | .start()
158 | expect(error).toEventuallyNot(beNil())
159 | expect(error).toEventually(equal(FeathersNetworkError.unavailable))
160 | expect(response).toEventually(beNil())
161 | expect(data).toEventually(beNil())
162 | }
163 |
164 | }
165 |
166 | context("when given an error hook that modifies the hook error") {
167 |
168 | var errorHooks: Service.Hooks!
169 |
170 | beforeEach {
171 | errorHooks = Service.Hooks(all: [ModifyErrorHook(error: FeathersNetworkError.unavailable)])
172 | service.error(errorHooks)
173 | }
174 |
175 | it("should be able to modify the final error") {
176 | var error: FeathersNetworkError?
177 | var response: Response?
178 | var data: [String: String]?
179 | service.request(.find(query: nil))
180 | .on(failed: {
181 | error = $0.error as? FeathersNetworkError
182 | }, value: {
183 | response = $0
184 | data = $0.data.value as? [String: String]
185 | })
186 | .start()
187 | expect(error).toEventuallyNot(beNil())
188 | expect(error).toEventually(equal(FeathersNetworkError.unavailable))
189 | expect(response).toEventually(beNil())
190 | expect(data).toEventually(beNil())
191 | }
192 |
193 | context("with multiple error hooks that modify the error ") {
194 |
195 | beforeEach {
196 | service.error(Service.Hooks(all: [ModifyErrorHook(error: FeathersNetworkError.unknown)]))
197 | }
198 |
199 | it("should pass back the final error") {
200 | var error: FeathersNetworkError?
201 | var response: Response?
202 | var data: [String: String]?
203 | service.request(.find(query: nil))
204 | .on(failed: {
205 | error = $0.error as? FeathersNetworkError
206 | }, value: {
207 | response = $0
208 | data = $0.data.value as? [String: String]
209 | })
210 | .start()
211 | expect(error).toEventuallyNot(beNil())
212 | expect(error).toEventually(equal(FeathersNetworkError.unknown))
213 | expect(response).toEventually(beNil())
214 | expect(data).toEventually(beNil())
215 | }
216 |
217 | }
218 |
219 | }
220 |
221 | }
222 |
223 | }
224 |
225 | }
226 |
227 | }
228 |
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FeathersSwift
2 |
3 | [](#carthage) [](#cocoapods) [](https://github.com/feathersjs-ecosystem/feathers-swift/releases)   
4 |
5 | 
6 |
7 | ## What is FeathersSwift?
8 |
9 | FeathersSwift is a Cocoa networking library for interacting with a [FeathersJS](https://feathersjs.com/) backend.
10 | Why should you use it?
11 |
12 | * Swift 4 :thumbsup:
13 | * Network abstraction layer
14 | * Integrates seemlessly with any FeathersJS services
15 | * Supports iOS, macOS, tvOS, and watchOS
16 | * Reactive API ([ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift))
17 |
18 | If you use FeathersJS (which you should), FeathersSwift is the perfect choice for you. No more dealing with HTTP requests or socket clients. One simple interface to rule them all and in the darkness, unify them :ring:.
19 |
20 | ## Installation
21 |
22 | ### Cocoapods
23 | ```
24 | pod `Feathers`
25 | ```
26 |
27 | ### Carthage
28 |
29 | Add the following line to your Cartfile:
30 |
31 | ```
32 | github "feathersjs/feathers-swift"
33 | ```
34 | ## Getting Started
35 |
36 | FeathersSwift is spread out across multiple repositories to ensure that you're only pulling in exactly what you need and no more. There are two Feathers providers, [feathers-swift-rest](https://github.com/feathersjs-ecosystem/feathers-swift-rest) and [feathers-swift-socketio](https://github.com/feathersjs-ecosystem/feathers-swift-socketio). Install either provider using the instructions on their respective READMEs.
37 |
38 | Once you've install a provider, either rest of socketio, an instance of a `Feathers` application with it:
39 |
40 | ```swift
41 |
42 | let feathersRestApp = Feathers(RestProvider(baseURL: URL(string: "https://myserver.com")))
43 |
44 | ```
45 |
46 | Then grab a service:
47 |
48 | ```swift
49 | let userService = feathersRestApp.service("users")
50 | ```
51 |
52 | Finally, make a request:
53 |
54 | ```swift
55 | service.request(.find(parameters: ["name": "Waldo"]))
56 | .on(value: { response in
57 | print(response)
58 | })
59 | .start()
60 | ```
61 |
62 | FeathersSwift's API is built entirely using [ReactiveSwift](https://github.com/ReactiveCocoa/ReactiveSwift), an awesome functional-reactive library. Because promises aren't standard in Swift, we had to find an alternative that offers similar usage patterns. By doing this, we can avoid the pyramid of doom and endlessly nesting callbacks, instead offering a simplified reactive API.
63 |
64 | ### Service
65 |
66 | There are six types of requests you can make that correspond with Feathers service methods:
67 |
68 | ```swift
69 | public enum Method {
70 |
71 | case find(query: Query?)
72 | case get(id: String, query: Query?)
73 | case create(data: [String: Any], query: Query?)
74 | case update(id: String?, data: [String: Any], query: Query?)
75 | case patch(id: String?, data: [String: Any], query: Query?)
76 | case remove(id: String?, query: Query?)
77 |
78 | }
79 | ```
80 |
81 | With `.update`, `.patch`, and `.remove`, you may pass in nil for the id when you want to delete a list of entities. The list of entities is determined by the query you pass in.
82 |
83 | By default, FeathersSwift will return an instance of `ProviderService` which wraps the application's transport provider in a service. However, you can also register your own services:
84 |
85 | ```swift
86 | feathers.use("users-local", CoreDataService())
87 | ```
88 |
89 | All custom services must conform to `ServiceType`. Thankfully, that's easy due to the FeathersSwift provided `Service` class which handles things such as hook storage and no-op implementations of the required methods.
90 |
91 | A simple custom service might look like:
92 |
93 | ```swift
94 | class FileService: Service {
95 |
96 | public override func request(_ method: Service.Method) -> SignalProducer {
97 | let fileManager = FileManager.default
98 | switch method {
99 | case let .create(data, _):
100 | guard let id = data["id"] else { break }
101 | let fileData = NSKeyedArchiver.archiveData(withRootObject: data)
102 | fileManager.createFile(atPath: "\(path)/\(id)", contents: fileData, attributes: nil)
103 | default: break
104 | }
105 | }
106 |
107 | }
108 | ```
109 |
110 | While a tiny example, custom services can be infinitely more complex and used anywhere in the hook process. Just call `hookObject.app.service("my-custom-service").request(.create(data: [:], parameters: nil))`.
111 |
112 | ### Querying
113 |
114 | You may have noticed instead of passing a dictionary of parameters through a request, FeathersSwift uses a `Query` object. The `Query` class has a simple and composable API for represent complex queries without messing around with dictionaries. It supports all the normal queries FeathersJS users have come to know and love such as `ne` or `or`, just in a simplified, type-safe manner.
115 |
116 | To create a query:
117 |
118 | ```swift
119 | let query = Query()
120 | .ne("age", 50)
121 | .limit(25)
122 | .skip(5)
123 |
124 | let service = feathers.service("users")
125 |
126 | service.request(.find(query)).start()
127 |
128 | ```
129 |
130 | Gone are the days of wondering if you formatted your dictionary correctly, `Query` knows how to serialize itself and takes care of that for you.
131 |
132 | ### Authentication
133 |
134 | To authenticate your application with your Feathers back end:
135 |
136 | ```swift
137 | feathersRestApp.authenticate([
138 | "strategy": "facebook-token",
139 | "access_token": "ACCESS_TOKEN"
140 | ])
141 | .start()
142 | ```
143 |
144 | Authentication returns a JWT payload which is cached by the application and used to make subsequent requests. Currently there is not a re-authentication mechanism but look for that in coming versions.
145 |
146 | To log out, simply call:
147 |
148 | ```swift
149 | feathersRestApp.logout().start()
150 | ```
151 |
152 | ### Real-Time Events
153 |
154 | When using the socket provider, you can not only use it to call Feathers service methods, you can also listen for real-time events. Simply use [feathers-swift-socketio](https://github.com/feathersjs/feathers-swift-socketio) create a feathers application with an instance of `SocketProvider` and register for events using `.on` on your services.
155 |
156 | There are four different real-time events:
157 |
158 | ```swift
159 | public enum RealTimeEvent: String {
160 |
161 | case created
162 | case updated
163 | case patched
164 | case removed
165 |
166 | }
167 | ```
168 |
169 | You can use these events to things like dynamically update the UI, save entities to a database, or just log that the event happened.
170 |
171 | ```swift
172 | let feathersSocket = Feathers(provider: SocketProvider(baseURL: URL(string: "https://myserver.com")!, configuration: []))
173 |
174 | let userService = feathersSocket.service(path: "users")
175 | userService.on(.created)
176 | .observeValues { entity in
177 | print(entity) // Prints the object that was just created
178 | }
179 | ```
180 |
181 | When you're finished, be sure to call `.off` to unregister from the event. Otherwise your completion block will be retained by the provider.
182 |
183 | ```swift
184 | userService.off(.created)
185 | ```
186 |
187 | There's also a nifty `.once` function that does exactly what you expect; you listen for one event and one event only.
188 |
189 | ### Hooks
190 |
191 | 
192 |
193 | Like in FeathersJS, you can register `before`, `after`, and `error` hooks to run when requests are made. Possible use cases could include stubbing out a unit test with a before hook or simple logging.
194 |
195 | To create a hook, create an object that conforms to `Hook`:
196 |
197 | ```swift
198 | public protocol Hook {
199 | func run(with hookObject: HookObject) -> Promise
200 | }
201 | ```
202 |
203 | A hook that logs all `create` events might look like this:
204 |
205 | ```swift
206 | struct CreateLogHook: Hook {
207 |
208 | func run(with hookObject: HookObject) -> Promise {
209 | var object = hookObject
210 | if object.method == .create {
211 | print("create happened")
212 | }
213 | return Promise(value: object)
214 | }
215 |
216 | }
217 | ```
218 |
219 | Or you can do something more complex like a network call:
220 |
221 | ```swift
222 | struct FetchAssociatedUserHook: Hook {
223 | func run(with hookObject: HookObject) -> Promise {
224 | var object = hookObject
225 | guard object.app.service.path == "groups" else {
226 | return Promise(value: hookObject)
227 | }
228 | guard case var .get(id, parameters) = object.method else {
229 | return Promise(value: hookObject)
230 | }
231 | guard let userIdentifier = parameters["user_id"] as? String else {
232 | return Promise(error: .myCustomError("no associated user found when expected to exist"))
233 | }
234 | return object.app.service("users").request(.get(parameters: ["id": userIdentifier])).then { response in
235 | if case let .jsonObject(object) = response.data {
236 | parameters["user_id"] = object["id"]
237 | object.method = .get(id, parameters)
238 | }
239 | return Promise(value: object)
240 | }
241 | }
242 | }
243 | ```
244 |
245 | Important to note is `var object = hookObject`. Swift function parameters are `let` constants so you first have to copy the object if you want to modify it.
246 |
247 | #### Hook Object
248 |
249 | The hook object gets passed around through hooks in succession. The interface matches the JS one fairly closely:
250 |
251 | ```swift
252 | /// Hook object that gets passed through hook functions
253 | public struct HookObject {
254 |
255 | /// Represents the kind of hook.
256 | ///
257 | /// - before: Hook is run before the request is made.
258 | /// - after: Hook is run after the request is made.
259 | /// - error: Runs when there's an error.
260 | public enum Kind {
261 | case before, after, error
262 | }
263 |
264 | /// The kind of hook.
265 | public let type: Kind
266 |
267 | /// Feathers application, used to retrieve other services.
268 | public let app: Feathers
269 |
270 | /// The service this hook currently runs on.
271 | public let service: Service
272 |
273 | /// The service method.
274 | public var method: Service.Method
275 |
276 | /// Error that can be set which will stop the hook processing chain and run a special chain of error hooks.
277 | public var error: FeathersError?
278 |
279 | /// Result of a successful method call, only in after hooks.
280 | public var result: Response?
281 |
282 | public init(
283 | type: Kind,
284 | app: Feathers,
285 | service: Service,
286 | method: Service.Method) {
287 | self.type = type
288 | self.app = app
289 | self.service = service
290 | self.method = method
291 | }
292 |
293 | }
294 | ```
295 |
296 | All the `var` declarations are mutable and you can set and mutate them as needed in your hooks, including `.method` if you want to do things like swap out parameters or change the method call entirely (e.g. changing a `.get` to a `.find`).
297 |
298 | Important things to note about the hook object:
299 | - Setting `error` will cause the hook processing chain to stop and immediately run any error hooks. If that happens in a `before` hook, the request will also be skipped.
300 | - Setting `result` to some `Response` value in a `before` hook will skip the request, essentially stubbing it.
301 |
302 | #### Hook Registration
303 |
304 | To register your hooks, you first have to create a `Service.Hooks` object:
305 |
306 | ```swift
307 | let beforeHooks = Service.Hooks(all: [LogResultHook()], create: [AppendUserIdHook()]])
308 | let afterHooks = Service.Hooks(find: [SaveInRealmHook()])
309 | let errorHooks = Service.Hooks(all: [LogErrorHook(destination: "log.txt")])
310 | ```
311 |
312 | Registering the hooks is just as easy:
313 |
314 | ```swift
315 | let service = app.service("users")
316 | service.before(beforeHooks)
317 | service.after(afterHooks)
318 | service.error(errorHooks)
319 | ```
320 |
321 | **Important**: The hooks registered for `all` are run first, then the hooks for the particular service method.
322 |
323 | If at any point you need to inspect your hooks, you can do that too using `.hooks`:
324 |
325 | ```swift
326 |
327 | let beforeHooks = service.hooks(for: .before)
328 |
329 | ```
330 |
331 | ### Contributing
332 |
333 | Have an issue? Open an issue! Have an idea? Open a pull request!
334 |
335 | If you like the library, please :star: it!
336 |
337 | ### Further Help
338 |
339 | FeathersSwift is extensively documented so please take a look at the source code if you have any questions about how something works.
340 | You can also ping me in the [Feathers' slack team](http://slack.feathersjs.com/) @brendan.
341 |
342 | Cheers! :beers:
343 |
--------------------------------------------------------------------------------
/Feathers.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | EA0505A51ED9B262006B5CCB /* QuerySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0F01ED7E0FC00EA04F4 /* QuerySpec.swift */; };
11 | EA0505A61ED9B292006B5CCB /* QuerySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0F01ED7E0FC00EA04F4 /* QuerySpec.swift */; };
12 | EA0F0BB21ED0A6C9000517A4 /* FeathersSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0BB11ED0A6C9000517A4 /* FeathersSpec.swift */; };
13 | EA0F0C7C1ED0B25F000517A4 /* Feathers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C731ED0B25E000517A4 /* Feathers.framework */; };
14 | EA0F0C8D1ED0B2D0000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8A1ED0B2D0000517A4 /* KeychainSwift.framework */; };
15 | EA0F0C8E1ED0B2D0000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8B1ED0B2D0000517A4 /* ReactiveSwift.framework */; };
16 | EA0F0C8F1ED0B2D0000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8C1ED0B2D0000517A4 /* Result.framework */; };
17 | EA0F0C901ED0B2DD000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8A1ED0B2D0000517A4 /* KeychainSwift.framework */; };
18 | EA0F0C911ED0B2DD000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8B1ED0B2D0000517A4 /* ReactiveSwift.framework */; };
19 | EA0F0C921ED0B2DD000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C8C1ED0B2D0000517A4 /* Result.framework */; };
20 | EA0F0C951ED0B2E1000517A4 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C931ED0B2E1000517A4 /* Nimble.framework */; };
21 | EA0F0C961ED0B2E1000517A4 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0C941ED0B2E1000517A4 /* Quick.framework */; };
22 | EA0F0C9C1ED0B2EA000517A4 /* KeychainSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = EA0F0C971ED0B2EA000517A4 /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
23 | EA0F0C9D1ED0B2EA000517A4 /* Nimble.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = EA0F0C981ED0B2EA000517A4 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
24 | EA0F0C9E1ED0B2EA000517A4 /* Quick.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = EA0F0C991ED0B2EA000517A4 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
25 | EA0F0C9F1ED0B2EA000517A4 /* ReactiveSwift.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = EA0F0C9A1ED0B2EA000517A4 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
26 | EA0F0CA01ED0B2EA000517A4 /* Result.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = EA0F0C9B1ED0B2EA000517A4 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | EA0F0CA41ED0B318000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA11ED0B318000517A4 /* KeychainSwift.framework */; };
28 | EA0F0CA51ED0B318000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA21ED0B318000517A4 /* ReactiveSwift.framework */; };
29 | EA0F0CA61ED0B318000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA31ED0B318000517A4 /* Result.framework */; };
30 | EA0F0CAC1ED0B38B000517A4 /* KeychainSwift.framework.dSYM in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CA91ED0B38B000517A4 /* KeychainSwift.framework.dSYM */; };
31 | EA0F0CAD1ED0B38B000517A4 /* ReactiveSwift.framework.dSYM in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CAA1ED0B38B000517A4 /* ReactiveSwift.framework.dSYM */; };
32 | EA0F0CAE1ED0B38B000517A4 /* Result.framework.dSYM in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CAB1ED0B38B000517A4 /* Result.framework.dSYM */; };
33 | EA0F0CAF1ED0B3B3000517A4 /* StubProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB91EC8CD0400114F19 /* StubProvider.swift */; };
34 | EA0F0CB01ED0B3B3000517A4 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB41EC8BEBD00114F19 /* Hooks.swift */; };
35 | EA0F0CB11ED0B3B3000517A4 /* ServiceSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA52954B1ECE912C0014F156 /* ServiceSpec.swift */; };
36 | EA0F0CB21ED0B3B3000517A4 /* FeathersSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0BB11ED0A6C9000517A4 /* FeathersSpec.swift */; };
37 | EA0F0CB31ED0B3E2000517A4 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3017131EC7867E00114F19 /* Hooks.swift */; };
38 | EA0F0CB41ED0B3E2000517A4 /* LoggerHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */; };
39 | EA0F0CB51ED0B3E2000517A4 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE121EBFA6A400522603 /* Service.swift */; };
40 | EA0F0CB61ED0B3E2000517A4 /* AuthenticationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */; };
41 | EA0F0CB71ED0B3E2000517A4 /* AuthenticationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */; };
42 | EA0F0CB81ED0B3E2000517A4 /* Feathers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0C1EBFA6A400522603 /* Feathers.swift */; };
43 | EA0F0CB91ED0B3E2000517A4 /* FeathersError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0D1EBFA6A400522603 /* FeathersError.swift */; };
44 | EA0F0CBA1ED0B3E2000517A4 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0F1EBFA6A400522603 /* Provider.swift */; };
45 | EA0F0CBB1ED0B3E2000517A4 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE101EBFA6A400522603 /* Response.swift */; };
46 | EA0F0CBC1ED0B3E2000517A4 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE05D691EBFDB0000DA55BF /* Endpoint.swift */; };
47 | EA0F0CBD1ED0B400000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA11ED0B318000517A4 /* KeychainSwift.framework */; };
48 | EA0F0CBE1ED0B400000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA21ED0B318000517A4 /* ReactiveSwift.framework */; };
49 | EA0F0CBF1ED0B400000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CA31ED0B318000517A4 /* Result.framework */; };
50 | EA0F0CC21ED0B400000517A4 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CC01ED0B400000517A4 /* Nimble.framework */; };
51 | EA0F0CC31ED0B400000517A4 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CC11ED0B400000517A4 /* Quick.framework */; };
52 | EA0F0CCA1ED0B413000517A4 /* KeychainSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CC51ED0B413000517A4 /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
53 | EA0F0CCB1ED0B413000517A4 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CC61ED0B413000517A4 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
54 | EA0F0CCC1ED0B413000517A4 /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CC71ED0B413000517A4 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
55 | EA0F0CCD1ED0B413000517A4 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CC81ED0B413000517A4 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
56 | EA0F0CCE1ED0B413000517A4 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0CC91ED0B413000517A4 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
57 | EA0F0CDD1ED0B448000517A4 /* Feathers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CD41ED0B448000517A4 /* Feathers.framework */; };
58 | EA0F0CEB1ED0B46B000517A4 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3017131EC7867E00114F19 /* Hooks.swift */; };
59 | EA0F0CEC1ED0B46B000517A4 /* LoggerHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */; };
60 | EA0F0CED1ED0B46B000517A4 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE121EBFA6A400522603 /* Service.swift */; };
61 | EA0F0CEE1ED0B46B000517A4 /* AuthenticationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */; };
62 | EA0F0CEF1ED0B46B000517A4 /* AuthenticationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */; };
63 | EA0F0CF01ED0B46B000517A4 /* Feathers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0C1EBFA6A400522603 /* Feathers.swift */; };
64 | EA0F0CF11ED0B46B000517A4 /* FeathersError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0D1EBFA6A400522603 /* FeathersError.swift */; };
65 | EA0F0CF21ED0B46B000517A4 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0F1EBFA6A400522603 /* Provider.swift */; };
66 | EA0F0CF31ED0B46B000517A4 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE101EBFA6A400522603 /* Response.swift */; };
67 | EA0F0CF41ED0B46B000517A4 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE05D691EBFDB0000DA55BF /* Endpoint.swift */; };
68 | EA0F0CF51ED0B46F000517A4 /* StubProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB91EC8CD0400114F19 /* StubProvider.swift */; };
69 | EA0F0CF61ED0B46F000517A4 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB41EC8BEBD00114F19 /* Hooks.swift */; };
70 | EA0F0CF71ED0B46F000517A4 /* ServiceSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA52954B1ECE912C0014F156 /* ServiceSpec.swift */; };
71 | EA0F0CF81ED0B46F000517A4 /* FeathersSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0BB11ED0A6C9000517A4 /* FeathersSpec.swift */; };
72 | EA0F0CFC1ED0B48C000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CF91ED0B48C000517A4 /* KeychainSwift.framework */; };
73 | EA0F0CFD1ED0B48C000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CFA1ED0B48C000517A4 /* ReactiveSwift.framework */; };
74 | EA0F0CFE1ED0B48C000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CFB1ED0B48C000517A4 /* Result.framework */; };
75 | EA0F0D001ED0B4C7000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CF91ED0B48C000517A4 /* KeychainSwift.framework */; };
76 | EA0F0D011ED0B4C7000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CFA1ED0B48C000517A4 /* ReactiveSwift.framework */; };
77 | EA0F0D021ED0B4C7000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0CFB1ED0B48C000517A4 /* Result.framework */; };
78 | EA0F0D051ED0B4C7000517A4 /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0D031ED0B4C7000517A4 /* Nimble.framework */; };
79 | EA0F0D061ED0B4C7000517A4 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0D041ED0B4C7000517A4 /* Quick.framework */; };
80 | EA0F0D0D1ED0B4D7000517A4 /* KeychainSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0D081ED0B4D7000517A4 /* KeychainSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
81 | EA0F0D0E1ED0B4D7000517A4 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0D091ED0B4D7000517A4 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
82 | EA0F0D0F1ED0B4D7000517A4 /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0D0A1ED0B4D7000517A4 /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83 | EA0F0D101ED0B4D7000517A4 /* ReactiveSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0D0B1ED0B4D7000517A4 /* ReactiveSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
84 | EA0F0D111ED0B4D7000517A4 /* Result.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EA0F0D0C1ED0B4D7000517A4 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
85 | EA0F0D221ED0B535000517A4 /* KeychainSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0D1F1ED0B535000517A4 /* KeychainSwift.framework */; };
86 | EA0F0D231ED0B535000517A4 /* ReactiveSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0D201ED0B535000517A4 /* ReactiveSwift.framework */; };
87 | EA0F0D241ED0B535000517A4 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA0F0D211ED0B535000517A4 /* Result.framework */; };
88 | EA0F0D251ED0B539000517A4 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3017131EC7867E00114F19 /* Hooks.swift */; };
89 | EA0F0D261ED0B539000517A4 /* LoggerHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */; };
90 | EA0F0D271ED0B539000517A4 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE121EBFA6A400522603 /* Service.swift */; };
91 | EA0F0D281ED0B539000517A4 /* AuthenticationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */; };
92 | EA0F0D291ED0B539000517A4 /* AuthenticationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */; };
93 | EA0F0D2A1ED0B539000517A4 /* Feathers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0C1EBFA6A400522603 /* Feathers.swift */; };
94 | EA0F0D2B1ED0B539000517A4 /* FeathersError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0D1EBFA6A400522603 /* FeathersError.swift */; };
95 | EA0F0D2C1ED0B539000517A4 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0F1EBFA6A400522603 /* Provider.swift */; };
96 | EA0F0D2D1ED0B539000517A4 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE101EBFA6A400522603 /* Response.swift */; };
97 | EA0F0D2E1ED0B539000517A4 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE05D691EBFDB0000DA55BF /* Endpoint.swift */; };
98 | EA0F0D311ED290EF000517A4 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D301ED290EF000517A4 /* ServiceType.swift */; };
99 | EA0F0D321ED290EF000517A4 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D301ED290EF000517A4 /* ServiceType.swift */; };
100 | EA0F0D331ED290EF000517A4 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D301ED290EF000517A4 /* ServiceType.swift */; };
101 | EA0F0D341ED290EF000517A4 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D301ED290EF000517A4 /* ServiceType.swift */; };
102 | EA0F0D3B1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */; };
103 | EA0F0D3C1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */; };
104 | EA0F0D3D1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */; };
105 | EA0F0D3E1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */; };
106 | EA0F0D401ED2AB99000517A4 /* ProviderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */; };
107 | EA0F0D411ED2AB99000517A4 /* ProviderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */; };
108 | EA0F0D421ED2AB99000517A4 /* ProviderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */; };
109 | EA0F0D431ED2AB99000517A4 /* ProviderService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */; };
110 | EA0F0D461ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */; };
111 | EA0F0D471ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */; };
112 | EA0F0D481ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */; };
113 | EA0F0D491ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */; };
114 | EA12E0631ECE950100A0DCF7 /* Feathers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAC0BCD21EC90FDB0049FB13 /* Feathers.framework */; };
115 | EA12E0691ECE950C00A0DCF7 /* ServiceSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA52954B1ECE912C0014F156 /* ServiceSpec.swift */; };
116 | EA12E06A1ECE952400A0DCF7 /* StubProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB91EC8CD0400114F19 /* StubProvider.swift */; };
117 | EA12E06B1ECE952400A0DCF7 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA301AB41EC8BEBD00114F19 /* Hooks.swift */; };
118 | EA8CE0EC1ED7DC2800EA04F4 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */; };
119 | EA8CE0ED1ED7DC2800EA04F4 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */; };
120 | EA8CE0EE1ED7DC2800EA04F4 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */; };
121 | EA8CE0EF1ED7DC2800EA04F4 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */; };
122 | EA8CE0F11ED7E0FC00EA04F4 /* QuerySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8CE0F01ED7E0FC00EA04F4 /* QuerySpec.swift */; };
123 | EA954C781EC9BEEC0094FF43 /* LoggerHooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */; };
124 | EAC0BCE91EC90FF30049FB13 /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE121EBFA6A400522603 /* Service.swift */; };
125 | EAC0BCEA1EC90FF30049FB13 /* AuthenticationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */; };
126 | EAC0BCEB1EC90FF30049FB13 /* AuthenticationStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */; };
127 | EAC0BCEC1EC90FF30049FB13 /* Feathers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0C1EBFA6A400522603 /* Feathers.swift */; };
128 | EAC0BCED1EC90FF30049FB13 /* FeathersError.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0D1EBFA6A400522603 /* FeathersError.swift */; };
129 | EAC0BCEF1EC90FF30049FB13 /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE0F1EBFA6A400522603 /* Provider.swift */; };
130 | EAC0BCF01EC90FF30049FB13 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = EABBAE101EBFA6A400522603 /* Response.swift */; };
131 | EAC0BCF31EC90FF30049FB13 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE05D691EBFDB0000DA55BF /* Endpoint.swift */; };
132 | EAC0BCF41EC90FF30049FB13 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3017131EC7867E00114F19 /* Hooks.swift */; };
133 | /* End PBXBuildFile section */
134 |
135 | /* Begin PBXContainerItemProxy section */
136 | EA0F0C7D1ED0B25F000517A4 /* PBXContainerItemProxy */ = {
137 | isa = PBXContainerItemProxy;
138 | containerPortal = EA452A5E1EA2B14300427CEF /* Project object */;
139 | proxyType = 1;
140 | remoteGlobalIDString = EA0F0C721ED0B25E000517A4;
141 | remoteInfo = "Feathers-macOS";
142 | };
143 | EA0F0CDE1ED0B448000517A4 /* PBXContainerItemProxy */ = {
144 | isa = PBXContainerItemProxy;
145 | containerPortal = EA452A5E1EA2B14300427CEF /* Project object */;
146 | proxyType = 1;
147 | remoteGlobalIDString = EA0F0CD31ED0B448000517A4;
148 | remoteInfo = "Feathers-tvOS";
149 | };
150 | EA12E0641ECE950100A0DCF7 /* PBXContainerItemProxy */ = {
151 | isa = PBXContainerItemProxy;
152 | containerPortal = EA452A5E1EA2B14300427CEF /* Project object */;
153 | proxyType = 1;
154 | remoteGlobalIDString = EAC0BCD11EC90FDB0049FB13;
155 | remoteInfo = "Feathers-iOS";
156 | };
157 | /* End PBXContainerItemProxy section */
158 |
159 | /* Begin PBXCopyFilesBuildPhase section */
160 | EA0F0BC01ED0ABA4000517A4 /* Copy Files */ = {
161 | isa = PBXCopyFilesBuildPhase;
162 | buildActionMask = 2147483647;
163 | dstPath = "";
164 | dstSubfolderSpec = 10;
165 | files = (
166 | EA0F0C9C1ED0B2EA000517A4 /* KeychainSwift.framework in Copy Files */,
167 | EA0F0C9D1ED0B2EA000517A4 /* Nimble.framework in Copy Files */,
168 | EA0F0C9E1ED0B2EA000517A4 /* Quick.framework in Copy Files */,
169 | EA0F0C9F1ED0B2EA000517A4 /* ReactiveSwift.framework in Copy Files */,
170 | EA0F0CA01ED0B2EA000517A4 /* Result.framework in Copy Files */,
171 | );
172 | name = "Copy Files";
173 | runOnlyForDeploymentPostprocessing = 0;
174 | };
175 | EA0F0CA81ED0B380000517A4 /* CopyFiles */ = {
176 | isa = PBXCopyFilesBuildPhase;
177 | buildActionMask = 2147483647;
178 | dstPath = "";
179 | dstSubfolderSpec = 16;
180 | files = (
181 | EA0F0CAC1ED0B38B000517A4 /* KeychainSwift.framework.dSYM in CopyFiles */,
182 | EA0F0CAD1ED0B38B000517A4 /* ReactiveSwift.framework.dSYM in CopyFiles */,
183 | EA0F0CAE1ED0B38B000517A4 /* Result.framework.dSYM in CopyFiles */,
184 | );
185 | runOnlyForDeploymentPostprocessing = 0;
186 | };
187 | EA0F0CC41ED0B403000517A4 /* CopyFiles */ = {
188 | isa = PBXCopyFilesBuildPhase;
189 | buildActionMask = 2147483647;
190 | dstPath = "";
191 | dstSubfolderSpec = 10;
192 | files = (
193 | EA0F0CCA1ED0B413000517A4 /* KeychainSwift.framework in CopyFiles */,
194 | EA0F0CCB1ED0B413000517A4 /* Nimble.framework in CopyFiles */,
195 | EA0F0CCC1ED0B413000517A4 /* Quick.framework in CopyFiles */,
196 | EA0F0CCD1ED0B413000517A4 /* ReactiveSwift.framework in CopyFiles */,
197 | EA0F0CCE1ED0B413000517A4 /* Result.framework in CopyFiles */,
198 | );
199 | runOnlyForDeploymentPostprocessing = 0;
200 | };
201 | EA0F0D071ED0B4CC000517A4 /* CopyFiles */ = {
202 | isa = PBXCopyFilesBuildPhase;
203 | buildActionMask = 2147483647;
204 | dstPath = "";
205 | dstSubfolderSpec = 10;
206 | files = (
207 | EA0F0D0D1ED0B4D7000517A4 /* KeychainSwift.framework in CopyFiles */,
208 | EA0F0D0E1ED0B4D7000517A4 /* Nimble.framework in CopyFiles */,
209 | EA0F0D0F1ED0B4D7000517A4 /* Quick.framework in CopyFiles */,
210 | EA0F0D101ED0B4D7000517A4 /* ReactiveSwift.framework in CopyFiles */,
211 | EA0F0D111ED0B4D7000517A4 /* Result.framework in CopyFiles */,
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | /* End PBXCopyFilesBuildPhase section */
216 |
217 | /* Begin PBXFileReference section */
218 | EA0F0BB11ED0A6C9000517A4 /* FeathersSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeathersSpec.swift; path = FeathersTests/FeathersSpec.swift; sourceTree = SOURCE_ROOT; };
219 | EA0F0C731ED0B25E000517A4 /* Feathers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Feathers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
220 | EA0F0C7B1ED0B25F000517A4 /* Feathers-macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Feathers-macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
221 | EA0F0C8A1ED0B2D0000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/iOS/KeychainSwift.framework; sourceTree = ""; };
222 | EA0F0C8B1ED0B2D0000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; };
223 | EA0F0C8C1ED0B2D0000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; };
224 | EA0F0C931ED0B2E1000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; };
225 | EA0F0C941ED0B2E1000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; };
226 | EA0F0C971ED0B2EA000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/iOS/KeychainSwift.framework; sourceTree = ""; };
227 | EA0F0C981ED0B2EA000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; };
228 | EA0F0C991ED0B2EA000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; };
229 | EA0F0C9A1ED0B2EA000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/iOS/ReactiveSwift.framework; sourceTree = ""; };
230 | EA0F0C9B1ED0B2EA000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = ""; };
231 | EA0F0CA11ED0B318000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/Mac/KeychainSwift.framework; sourceTree = ""; };
232 | EA0F0CA21ED0B318000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/Mac/ReactiveSwift.framework; sourceTree = ""; };
233 | EA0F0CA31ED0B318000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; };
234 | EA0F0CA91ED0B38B000517A4 /* KeychainSwift.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = KeychainSwift.framework.dSYM; path = Carthage/Build/Mac/KeychainSwift.framework.dSYM; sourceTree = ""; };
235 | EA0F0CAA1ED0B38B000517A4 /* ReactiveSwift.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = ReactiveSwift.framework.dSYM; path = Carthage/Build/Mac/ReactiveSwift.framework.dSYM; sourceTree = ""; };
236 | EA0F0CAB1ED0B38B000517A4 /* Result.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Result.framework.dSYM; path = Carthage/Build/Mac/Result.framework.dSYM; sourceTree = ""; };
237 | EA0F0CC01ED0B400000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = ""; };
238 | EA0F0CC11ED0B400000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = ""; };
239 | EA0F0CC51ED0B413000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/Mac/KeychainSwift.framework; sourceTree = ""; };
240 | EA0F0CC61ED0B413000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = ""; };
241 | EA0F0CC71ED0B413000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = ""; };
242 | EA0F0CC81ED0B413000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/Mac/ReactiveSwift.framework; sourceTree = ""; };
243 | EA0F0CC91ED0B413000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; };
244 | EA0F0CD41ED0B448000517A4 /* Feathers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Feathers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
245 | EA0F0CDC1ED0B448000517A4 /* Feathers-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Feathers-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
246 | EA0F0CF91ED0B48C000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/tvOS/KeychainSwift.framework; sourceTree = ""; };
247 | EA0F0CFA1ED0B48C000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/tvOS/ReactiveSwift.framework; sourceTree = ""; };
248 | EA0F0CFB1ED0B48C000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/tvOS/Result.framework; sourceTree = ""; };
249 | EA0F0D031ED0B4C7000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/tvOS/Nimble.framework; sourceTree = ""; };
250 | EA0F0D041ED0B4C7000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/tvOS/Quick.framework; sourceTree = ""; };
251 | EA0F0D081ED0B4D7000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/tvOS/KeychainSwift.framework; sourceTree = ""; };
252 | EA0F0D091ED0B4D7000517A4 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/tvOS/Nimble.framework; sourceTree = ""; };
253 | EA0F0D0A1ED0B4D7000517A4 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/tvOS/Quick.framework; sourceTree = ""; };
254 | EA0F0D0B1ED0B4D7000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/tvOS/ReactiveSwift.framework; sourceTree = ""; };
255 | EA0F0D0C1ED0B4D7000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/tvOS/Result.framework; sourceTree = ""; };
256 | EA0F0D171ED0B50D000517A4 /* Feathers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Feathers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
257 | EA0F0D1F1ED0B535000517A4 /* KeychainSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainSwift.framework; path = Carthage/Build/watchOS/KeychainSwift.framework; sourceTree = ""; };
258 | EA0F0D201ED0B535000517A4 /* ReactiveSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveSwift.framework; path = Carthage/Build/watchOS/ReactiveSwift.framework; sourceTree = ""; };
259 | EA0F0D211ED0B535000517A4 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/watchOS/Result.framework; sourceTree = ""; };
260 | EA0F0D301ED290EF000517A4 /* ServiceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServiceType.swift; path = Core/ServiceType.swift; sourceTree = ""; };
261 | EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServiceWrapper.swift; path = Core/ServiceWrapper.swift; sourceTree = ""; };
262 | EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProviderService.swift; path = Core/ProviderService.swift; sourceTree = ""; };
263 | EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "SignalProducer+Convenience.swift"; path = "Core/SignalProducer+Convenience.swift"; sourceTree = ""; };
264 | EA12E05E1ECE950000A0DCF7 /* Feathers-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Feathers-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
265 | EA3017131EC7867E00114F19 /* Hooks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Hooks.swift; path = Core/Hooks.swift; sourceTree = ""; };
266 | EA301AB41EC8BEBD00114F19 /* Hooks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Hooks.swift; path = FeathersTests/Hooks.swift; sourceTree = SOURCE_ROOT; };
267 | EA301AB91EC8CD0400114F19 /* StubProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StubProvider.swift; path = FeathersTests/StubProvider.swift; sourceTree = SOURCE_ROOT; };
268 | EA452A6A1EA2B14300427CEF /* Feathers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Feathers.h; sourceTree = ""; };
269 | EA452A6B1EA2B14300427CEF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
270 | EA52954B1ECE912C0014F156 /* ServiceSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ServiceSpec.swift; path = FeathersTests/ServiceSpec.swift; sourceTree = SOURCE_ROOT; };
271 | EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Query.swift; path = Core/Query.swift; sourceTree = ""; };
272 | EA8CE0F01ED7E0FC00EA04F4 /* QuerySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = QuerySpec.swift; path = FeathersTests/QuerySpec.swift; sourceTree = SOURCE_ROOT; };
273 | EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoggerHooks.swift; path = Core/LoggerHooks.swift; sourceTree = ""; };
274 | EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthenticationConfiguration.swift; path = Core/AuthenticationConfiguration.swift; sourceTree = ""; };
275 | EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AuthenticationStorage.swift; path = Core/AuthenticationStorage.swift; sourceTree = ""; };
276 | EABBAE0C1EBFA6A400522603 /* Feathers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Feathers.swift; path = Core/Feathers.swift; sourceTree = ""; };
277 | EABBAE0D1EBFA6A400522603 /* FeathersError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeathersError.swift; path = Core/FeathersError.swift; sourceTree = ""; };
278 | EABBAE0F1EBFA6A400522603 /* Provider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Provider.swift; path = Core/Provider.swift; sourceTree = ""; };
279 | EABBAE101EBFA6A400522603 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Response.swift; path = Core/Response.swift; sourceTree = ""; };
280 | EABBAE121EBFA6A400522603 /* Service.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Service.swift; path = Core/Service.swift; sourceTree = ""; };
281 | EAC0BC821EC90A600049FB13 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = FeathersTests/Info.plist; sourceTree = SOURCE_ROOT; };
282 | EAC0BCD21EC90FDB0049FB13 /* Feathers.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Feathers.framework; sourceTree = BUILT_PRODUCTS_DIR; };
283 | EAE05D691EBFDB0000DA55BF /* Endpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Endpoint.swift; path = Core/Endpoint.swift; sourceTree = ""; };
284 | /* End PBXFileReference section */
285 |
286 | /* Begin PBXFrameworksBuildPhase section */
287 | EA0F0C6F1ED0B25E000517A4 /* Frameworks */ = {
288 | isa = PBXFrameworksBuildPhase;
289 | buildActionMask = 2147483647;
290 | files = (
291 | EA0F0CA41ED0B318000517A4 /* KeychainSwift.framework in Frameworks */,
292 | EA0F0CA51ED0B318000517A4 /* ReactiveSwift.framework in Frameworks */,
293 | EA0F0CA61ED0B318000517A4 /* Result.framework in Frameworks */,
294 | );
295 | runOnlyForDeploymentPostprocessing = 0;
296 | };
297 | EA0F0C781ED0B25F000517A4 /* Frameworks */ = {
298 | isa = PBXFrameworksBuildPhase;
299 | buildActionMask = 2147483647;
300 | files = (
301 | EA0F0C7C1ED0B25F000517A4 /* Feathers.framework in Frameworks */,
302 | EA0F0CC21ED0B400000517A4 /* Nimble.framework in Frameworks */,
303 | EA0F0CC31ED0B400000517A4 /* Quick.framework in Frameworks */,
304 | EA0F0CBD1ED0B400000517A4 /* KeychainSwift.framework in Frameworks */,
305 | EA0F0CBE1ED0B400000517A4 /* ReactiveSwift.framework in Frameworks */,
306 | EA0F0CBF1ED0B400000517A4 /* Result.framework in Frameworks */,
307 | );
308 | runOnlyForDeploymentPostprocessing = 0;
309 | };
310 | EA0F0CD01ED0B448000517A4 /* Frameworks */ = {
311 | isa = PBXFrameworksBuildPhase;
312 | buildActionMask = 2147483647;
313 | files = (
314 | EA0F0CFC1ED0B48C000517A4 /* KeychainSwift.framework in Frameworks */,
315 | EA0F0CFD1ED0B48C000517A4 /* ReactiveSwift.framework in Frameworks */,
316 | EA0F0CFE1ED0B48C000517A4 /* Result.framework in Frameworks */,
317 | );
318 | runOnlyForDeploymentPostprocessing = 0;
319 | };
320 | EA0F0CD91ED0B448000517A4 /* Frameworks */ = {
321 | isa = PBXFrameworksBuildPhase;
322 | buildActionMask = 2147483647;
323 | files = (
324 | EA0F0CDD1ED0B448000517A4 /* Feathers.framework in Frameworks */,
325 | EA0F0D051ED0B4C7000517A4 /* Nimble.framework in Frameworks */,
326 | EA0F0D061ED0B4C7000517A4 /* Quick.framework in Frameworks */,
327 | EA0F0D001ED0B4C7000517A4 /* KeychainSwift.framework in Frameworks */,
328 | EA0F0D011ED0B4C7000517A4 /* ReactiveSwift.framework in Frameworks */,
329 | EA0F0D021ED0B4C7000517A4 /* Result.framework in Frameworks */,
330 | );
331 | runOnlyForDeploymentPostprocessing = 0;
332 | };
333 | EA0F0D131ED0B50D000517A4 /* Frameworks */ = {
334 | isa = PBXFrameworksBuildPhase;
335 | buildActionMask = 2147483647;
336 | files = (
337 | EA0F0D221ED0B535000517A4 /* KeychainSwift.framework in Frameworks */,
338 | EA0F0D231ED0B535000517A4 /* ReactiveSwift.framework in Frameworks */,
339 | EA0F0D241ED0B535000517A4 /* Result.framework in Frameworks */,
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | };
343 | EA12E05B1ECE950000A0DCF7 /* Frameworks */ = {
344 | isa = PBXFrameworksBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | EA12E0631ECE950100A0DCF7 /* Feathers.framework in Frameworks */,
348 | EA0F0C901ED0B2DD000517A4 /* KeychainSwift.framework in Frameworks */,
349 | EA0F0C911ED0B2DD000517A4 /* ReactiveSwift.framework in Frameworks */,
350 | EA0F0C921ED0B2DD000517A4 /* Result.framework in Frameworks */,
351 | EA0F0C951ED0B2E1000517A4 /* Nimble.framework in Frameworks */,
352 | EA0F0C961ED0B2E1000517A4 /* Quick.framework in Frameworks */,
353 | );
354 | runOnlyForDeploymentPostprocessing = 0;
355 | };
356 | EAC0BCCE1EC90FDB0049FB13 /* Frameworks */ = {
357 | isa = PBXFrameworksBuildPhase;
358 | buildActionMask = 2147483647;
359 | files = (
360 | EA0F0C8D1ED0B2D0000517A4 /* KeychainSwift.framework in Frameworks */,
361 | EA0F0C8E1ED0B2D0000517A4 /* ReactiveSwift.framework in Frameworks */,
362 | EA0F0C8F1ED0B2D0000517A4 /* Result.framework in Frameworks */,
363 | );
364 | runOnlyForDeploymentPostprocessing = 0;
365 | };
366 | /* End PBXFrameworksBuildPhase section */
367 |
368 | /* Begin PBXGroup section */
369 | E6F86BE4240DD808800C3F09 /* Frameworks */ = {
370 | isa = PBXGroup;
371 | children = (
372 | EA0F0D1F1ED0B535000517A4 /* KeychainSwift.framework */,
373 | EA0F0D201ED0B535000517A4 /* ReactiveSwift.framework */,
374 | EA0F0D211ED0B535000517A4 /* Result.framework */,
375 | EA0F0D081ED0B4D7000517A4 /* KeychainSwift.framework */,
376 | EA0F0D091ED0B4D7000517A4 /* Nimble.framework */,
377 | EA0F0D0A1ED0B4D7000517A4 /* Quick.framework */,
378 | EA0F0D0B1ED0B4D7000517A4 /* ReactiveSwift.framework */,
379 | EA0F0D0C1ED0B4D7000517A4 /* Result.framework */,
380 | EA0F0D031ED0B4C7000517A4 /* Nimble.framework */,
381 | EA0F0D041ED0B4C7000517A4 /* Quick.framework */,
382 | EA0F0CF91ED0B48C000517A4 /* KeychainSwift.framework */,
383 | EA0F0CFA1ED0B48C000517A4 /* ReactiveSwift.framework */,
384 | EA0F0CFB1ED0B48C000517A4 /* Result.framework */,
385 | EA0F0CC51ED0B413000517A4 /* KeychainSwift.framework */,
386 | EA0F0CC61ED0B413000517A4 /* Nimble.framework */,
387 | EA0F0CC71ED0B413000517A4 /* Quick.framework */,
388 | EA0F0CC81ED0B413000517A4 /* ReactiveSwift.framework */,
389 | EA0F0CC91ED0B413000517A4 /* Result.framework */,
390 | EA0F0CC01ED0B400000517A4 /* Nimble.framework */,
391 | EA0F0CC11ED0B400000517A4 /* Quick.framework */,
392 | EA0F0CA91ED0B38B000517A4 /* KeychainSwift.framework.dSYM */,
393 | EA0F0CAA1ED0B38B000517A4 /* ReactiveSwift.framework.dSYM */,
394 | EA0F0CAB1ED0B38B000517A4 /* Result.framework.dSYM */,
395 | EA0F0C971ED0B2EA000517A4 /* KeychainSwift.framework */,
396 | EA0F0C981ED0B2EA000517A4 /* Nimble.framework */,
397 | EA0F0C991ED0B2EA000517A4 /* Quick.framework */,
398 | EA0F0C9A1ED0B2EA000517A4 /* ReactiveSwift.framework */,
399 | EA0F0C9B1ED0B2EA000517A4 /* Result.framework */,
400 | EA0F0CA11ED0B318000517A4 /* KeychainSwift.framework */,
401 | EA0F0CA21ED0B318000517A4 /* ReactiveSwift.framework */,
402 | EA0F0CA31ED0B318000517A4 /* Result.framework */,
403 | EA0F0C931ED0B2E1000517A4 /* Nimble.framework */,
404 | EA0F0C941ED0B2E1000517A4 /* Quick.framework */,
405 | EA0F0C8A1ED0B2D0000517A4 /* KeychainSwift.framework */,
406 | EA0F0C8B1ED0B2D0000517A4 /* ReactiveSwift.framework */,
407 | EA0F0C8C1ED0B2D0000517A4 /* Result.framework */,
408 | );
409 | name = Frameworks;
410 | sourceTree = "";
411 | };
412 | EA0F0D441ED3F85A000517A4 /* Extensions */ = {
413 | isa = PBXGroup;
414 | children = (
415 | EA0F0D451ED3F87F000517A4 /* SignalProducer+Convenience.swift */,
416 | );
417 | name = Extensions;
418 | sourceTree = "";
419 | };
420 | EA301AB31EC8BEAF00114F19 /* Fakes */ = {
421 | isa = PBXGroup;
422 | children = (
423 | EA301AB41EC8BEBD00114F19 /* Hooks.swift */,
424 | );
425 | name = Fakes;
426 | sourceTree = "";
427 | };
428 | EA301ABD1EC8CD0B00114F19 /* Stubs */ = {
429 | isa = PBXGroup;
430 | children = (
431 | EA301AB91EC8CD0400114F19 /* StubProvider.swift */,
432 | );
433 | name = Stubs;
434 | sourceTree = "";
435 | };
436 | EA452A5D1EA2B14300427CEF = {
437 | isa = PBXGroup;
438 | children = (
439 | EA452A691EA2B14300427CEF /* Feathers */,
440 | EABBADB01EBE77C900522603 /* FeathersTests */,
441 | EA452A681EA2B14300427CEF /* Products */,
442 | E6F86BE4240DD808800C3F09 /* Frameworks */,
443 | );
444 | sourceTree = "";
445 | };
446 | EA452A681EA2B14300427CEF /* Products */ = {
447 | isa = PBXGroup;
448 | children = (
449 | EAC0BCD21EC90FDB0049FB13 /* Feathers.framework */,
450 | EA12E05E1ECE950000A0DCF7 /* Feathers-iOSTests.xctest */,
451 | EA0F0C731ED0B25E000517A4 /* Feathers.framework */,
452 | EA0F0C7B1ED0B25F000517A4 /* Feathers-macOSTests.xctest */,
453 | EA0F0CD41ED0B448000517A4 /* Feathers.framework */,
454 | EA0F0CDC1ED0B448000517A4 /* Feathers-tvOSTests.xctest */,
455 | EA0F0D171ED0B50D000517A4 /* Feathers.framework */,
456 | );
457 | name = Products;
458 | sourceTree = "";
459 | };
460 | EA452A691EA2B14300427CEF /* Feathers */ = {
461 | isa = PBXGroup;
462 | children = (
463 | EABBAE091EBFA69C00522603 /* Core */,
464 | EA452A6A1EA2B14300427CEF /* Feathers.h */,
465 | EA452A6B1EA2B14300427CEF /* Info.plist */,
466 | );
467 | path = Feathers;
468 | sourceTree = "";
469 | };
470 | EABBADB01EBE77C900522603 /* FeathersTests */ = {
471 | isa = PBXGroup;
472 | children = (
473 | EA301ABD1EC8CD0B00114F19 /* Stubs */,
474 | EA301AB31EC8BEAF00114F19 /* Fakes */,
475 | EAC0BC821EC90A600049FB13 /* Info.plist */,
476 | EA52954B1ECE912C0014F156 /* ServiceSpec.swift */,
477 | EA8CE0F01ED7E0FC00EA04F4 /* QuerySpec.swift */,
478 | EA0F0BB11ED0A6C9000517A4 /* FeathersSpec.swift */,
479 | );
480 | path = FeathersTests;
481 | sourceTree = "";
482 | };
483 | EABBAE091EBFA69C00522603 /* Core */ = {
484 | isa = PBXGroup;
485 | children = (
486 | EA0F0D441ED3F85A000517A4 /* Extensions */,
487 | EA3017131EC7867E00114F19 /* Hooks.swift */,
488 | EA954C771EC9BEEC0094FF43 /* LoggerHooks.swift */,
489 | EABBAE121EBFA6A400522603 /* Service.swift */,
490 | EABBAE0A1EBFA6A400522603 /* AuthenticationConfiguration.swift */,
491 | EABBAE0B1EBFA6A400522603 /* AuthenticationStorage.swift */,
492 | EABBAE0C1EBFA6A400522603 /* Feathers.swift */,
493 | EA0F0D3F1ED2AB99000517A4 /* ProviderService.swift */,
494 | EABBAE0D1EBFA6A400522603 /* FeathersError.swift */,
495 | EABBAE0F1EBFA6A400522603 /* Provider.swift */,
496 | EABBAE101EBFA6A400522603 /* Response.swift */,
497 | EAE05D691EBFDB0000DA55BF /* Endpoint.swift */,
498 | EA0F0D301ED290EF000517A4 /* ServiceType.swift */,
499 | EA0F0D3A1ED2AA49000517A4 /* ServiceWrapper.swift */,
500 | EA8CE0EB1ED7DC2800EA04F4 /* Query.swift */,
501 | );
502 | name = Core;
503 | sourceTree = "";
504 | };
505 | /* End PBXGroup section */
506 |
507 | /* Begin PBXHeadersBuildPhase section */
508 | EA0F0C701ED0B25E000517A4 /* Headers */ = {
509 | isa = PBXHeadersBuildPhase;
510 | buildActionMask = 2147483647;
511 | files = (
512 | );
513 | runOnlyForDeploymentPostprocessing = 0;
514 | };
515 | EA0F0CD11ED0B448000517A4 /* Headers */ = {
516 | isa = PBXHeadersBuildPhase;
517 | buildActionMask = 2147483647;
518 | files = (
519 | );
520 | runOnlyForDeploymentPostprocessing = 0;
521 | };
522 | EA0F0D141ED0B50D000517A4 /* Headers */ = {
523 | isa = PBXHeadersBuildPhase;
524 | buildActionMask = 2147483647;
525 | files = (
526 | );
527 | runOnlyForDeploymentPostprocessing = 0;
528 | };
529 | EAC0BCCF1EC90FDB0049FB13 /* Headers */ = {
530 | isa = PBXHeadersBuildPhase;
531 | buildActionMask = 2147483647;
532 | files = (
533 | );
534 | runOnlyForDeploymentPostprocessing = 0;
535 | };
536 | /* End PBXHeadersBuildPhase section */
537 |
538 | /* Begin PBXNativeTarget section */
539 | EA0F0C721ED0B25E000517A4 /* Feathers-macOS */ = {
540 | isa = PBXNativeTarget;
541 | buildConfigurationList = EA0F0C841ED0B25F000517A4 /* Build configuration list for PBXNativeTarget "Feathers-macOS" */;
542 | buildPhases = (
543 | EA0F0C6E1ED0B25E000517A4 /* Sources */,
544 | EA0F0C6F1ED0B25E000517A4 /* Frameworks */,
545 | EA0F0C701ED0B25E000517A4 /* Headers */,
546 | EA0F0C711ED0B25E000517A4 /* Resources */,
547 | EA0F0CA81ED0B380000517A4 /* CopyFiles */,
548 | );
549 | buildRules = (
550 | );
551 | dependencies = (
552 | );
553 | name = "Feathers-macOS";
554 | productName = "Feathers-macOS";
555 | productReference = EA0F0C731ED0B25E000517A4 /* Feathers.framework */;
556 | productType = "com.apple.product-type.framework";
557 | };
558 | EA0F0C7A1ED0B25F000517A4 /* Feathers-macOSTests */ = {
559 | isa = PBXNativeTarget;
560 | buildConfigurationList = EA0F0C871ED0B25F000517A4 /* Build configuration list for PBXNativeTarget "Feathers-macOSTests" */;
561 | buildPhases = (
562 | EA0F0C771ED0B25F000517A4 /* Sources */,
563 | EA0F0C781ED0B25F000517A4 /* Frameworks */,
564 | EA0F0C791ED0B25F000517A4 /* Resources */,
565 | EA0F0CC41ED0B403000517A4 /* CopyFiles */,
566 | );
567 | buildRules = (
568 | );
569 | dependencies = (
570 | EA0F0C7E1ED0B25F000517A4 /* PBXTargetDependency */,
571 | );
572 | name = "Feathers-macOSTests";
573 | productName = "Feathers-macOSTests";
574 | productReference = EA0F0C7B1ED0B25F000517A4 /* Feathers-macOSTests.xctest */;
575 | productType = "com.apple.product-type.bundle.unit-test";
576 | };
577 | EA0F0CD31ED0B448000517A4 /* Feathers-tvOS */ = {
578 | isa = PBXNativeTarget;
579 | buildConfigurationList = EA0F0CE51ED0B448000517A4 /* Build configuration list for PBXNativeTarget "Feathers-tvOS" */;
580 | buildPhases = (
581 | EA0F0CCF1ED0B448000517A4 /* Sources */,
582 | EA0F0CD01ED0B448000517A4 /* Frameworks */,
583 | EA0F0CD11ED0B448000517A4 /* Headers */,
584 | EA0F0CD21ED0B448000517A4 /* Resources */,
585 | );
586 | buildRules = (
587 | );
588 | dependencies = (
589 | );
590 | name = "Feathers-tvOS";
591 | productName = "Feathers-tvOS";
592 | productReference = EA0F0CD41ED0B448000517A4 /* Feathers.framework */;
593 | productType = "com.apple.product-type.framework";
594 | };
595 | EA0F0CDB1ED0B448000517A4 /* Feathers-tvOSTests */ = {
596 | isa = PBXNativeTarget;
597 | buildConfigurationList = EA0F0CE81ED0B448000517A4 /* Build configuration list for PBXNativeTarget "Feathers-tvOSTests" */;
598 | buildPhases = (
599 | EA0F0CD81ED0B448000517A4 /* Sources */,
600 | EA0F0CD91ED0B448000517A4 /* Frameworks */,
601 | EA0F0CDA1ED0B448000517A4 /* Resources */,
602 | EA0F0D071ED0B4CC000517A4 /* CopyFiles */,
603 | );
604 | buildRules = (
605 | );
606 | dependencies = (
607 | EA0F0CDF1ED0B448000517A4 /* PBXTargetDependency */,
608 | );
609 | name = "Feathers-tvOSTests";
610 | productName = "Feathers-tvOSTests";
611 | productReference = EA0F0CDC1ED0B448000517A4 /* Feathers-tvOSTests.xctest */;
612 | productType = "com.apple.product-type.bundle.unit-test";
613 | };
614 | EA0F0D161ED0B50D000517A4 /* Feathers-watchOS */ = {
615 | isa = PBXNativeTarget;
616 | buildConfigurationList = EA0F0D1C1ED0B50D000517A4 /* Build configuration list for PBXNativeTarget "Feathers-watchOS" */;
617 | buildPhases = (
618 | EA0F0D121ED0B50D000517A4 /* Sources */,
619 | EA0F0D131ED0B50D000517A4 /* Frameworks */,
620 | EA0F0D141ED0B50D000517A4 /* Headers */,
621 | EA0F0D151ED0B50D000517A4 /* Resources */,
622 | );
623 | buildRules = (
624 | );
625 | dependencies = (
626 | );
627 | name = "Feathers-watchOS";
628 | productName = "Feathers-watchOS";
629 | productReference = EA0F0D171ED0B50D000517A4 /* Feathers.framework */;
630 | productType = "com.apple.product-type.framework";
631 | };
632 | EA12E05D1ECE950000A0DCF7 /* Feathers-iOSTests */ = {
633 | isa = PBXNativeTarget;
634 | buildConfigurationList = EA12E0661ECE950100A0DCF7 /* Build configuration list for PBXNativeTarget "Feathers-iOSTests" */;
635 | buildPhases = (
636 | EA12E05A1ECE950000A0DCF7 /* Sources */,
637 | EA12E05B1ECE950000A0DCF7 /* Frameworks */,
638 | EA12E05C1ECE950000A0DCF7 /* Resources */,
639 | EA0F0BC01ED0ABA4000517A4 /* Copy Files */,
640 | );
641 | buildRules = (
642 | );
643 | dependencies = (
644 | EA12E0651ECE950100A0DCF7 /* PBXTargetDependency */,
645 | );
646 | name = "Feathers-iOSTests";
647 | productName = "FeathersTests-iOS";
648 | productReference = EA12E05E1ECE950000A0DCF7 /* Feathers-iOSTests.xctest */;
649 | productType = "com.apple.product-type.bundle.unit-test";
650 | };
651 | EAC0BCD11EC90FDB0049FB13 /* Feathers-iOS */ = {
652 | isa = PBXNativeTarget;
653 | buildConfigurationList = EAC0BCE31EC90FDB0049FB13 /* Build configuration list for PBXNativeTarget "Feathers-iOS" */;
654 | buildPhases = (
655 | EAC0BCCD1EC90FDB0049FB13 /* Sources */,
656 | EAC0BCCE1EC90FDB0049FB13 /* Frameworks */,
657 | EAC0BCCF1EC90FDB0049FB13 /* Headers */,
658 | EAC0BCD01EC90FDB0049FB13 /* Resources */,
659 | );
660 | buildRules = (
661 | );
662 | dependencies = (
663 | );
664 | name = "Feathers-iOS";
665 | productName = "Feathers-iOS";
666 | productReference = EAC0BCD21EC90FDB0049FB13 /* Feathers.framework */;
667 | productType = "com.apple.product-type.framework";
668 | };
669 | /* End PBXNativeTarget section */
670 |
671 | /* Begin PBXProject section */
672 | EA452A5E1EA2B14300427CEF /* Project object */ = {
673 | isa = PBXProject;
674 | attributes = {
675 | LastSwiftUpdateCheck = 0830;
676 | LastUpgradeCheck = 1020;
677 | ORGANIZATIONNAME = "Swoopy Studios";
678 | TargetAttributes = {
679 | EA0F0C721ED0B25E000517A4 = {
680 | CreatedOnToolsVersion = 8.3.2;
681 | LastSwiftMigration = 0920;
682 | ProvisioningStyle = Automatic;
683 | };
684 | EA0F0C7A1ED0B25F000517A4 = {
685 | CreatedOnToolsVersion = 8.3.2;
686 | LastSwiftMigration = 0920;
687 | ProvisioningStyle = Automatic;
688 | };
689 | EA0F0CD31ED0B448000517A4 = {
690 | CreatedOnToolsVersion = 8.3.2;
691 | LastSwiftMigration = 0920;
692 | ProvisioningStyle = Automatic;
693 | };
694 | EA0F0CDB1ED0B448000517A4 = {
695 | CreatedOnToolsVersion = 8.3.2;
696 | LastSwiftMigration = 0920;
697 | ProvisioningStyle = Automatic;
698 | };
699 | EA0F0D161ED0B50D000517A4 = {
700 | CreatedOnToolsVersion = 8.3.2;
701 | LastSwiftMigration = 0920;
702 | ProvisioningStyle = Automatic;
703 | };
704 | EA12E05D1ECE950000A0DCF7 = {
705 | CreatedOnToolsVersion = 8.3.2;
706 | LastSwiftMigration = 0920;
707 | ProvisioningStyle = Automatic;
708 | };
709 | EAC0BCD11EC90FDB0049FB13 = {
710 | CreatedOnToolsVersion = 8.3.2;
711 | LastSwiftMigration = 0920;
712 | ProvisioningStyle = Automatic;
713 | };
714 | };
715 | };
716 | buildConfigurationList = EA452A611EA2B14300427CEF /* Build configuration list for PBXProject "Feathers" */;
717 | compatibilityVersion = "Xcode 3.2";
718 | developmentRegion = en;
719 | hasScannedForEncodings = 0;
720 | knownRegions = (
721 | en,
722 | Base,
723 | );
724 | mainGroup = EA452A5D1EA2B14300427CEF;
725 | productRefGroup = EA452A681EA2B14300427CEF /* Products */;
726 | projectDirPath = "";
727 | projectRoot = "";
728 | targets = (
729 | EAC0BCD11EC90FDB0049FB13 /* Feathers-iOS */,
730 | EA12E05D1ECE950000A0DCF7 /* Feathers-iOSTests */,
731 | EA0F0C721ED0B25E000517A4 /* Feathers-macOS */,
732 | EA0F0C7A1ED0B25F000517A4 /* Feathers-macOSTests */,
733 | EA0F0CD31ED0B448000517A4 /* Feathers-tvOS */,
734 | EA0F0CDB1ED0B448000517A4 /* Feathers-tvOSTests */,
735 | EA0F0D161ED0B50D000517A4 /* Feathers-watchOS */,
736 | );
737 | };
738 | /* End PBXProject section */
739 |
740 | /* Begin PBXResourcesBuildPhase section */
741 | EA0F0C711ED0B25E000517A4 /* Resources */ = {
742 | isa = PBXResourcesBuildPhase;
743 | buildActionMask = 2147483647;
744 | files = (
745 | );
746 | runOnlyForDeploymentPostprocessing = 0;
747 | };
748 | EA0F0C791ED0B25F000517A4 /* Resources */ = {
749 | isa = PBXResourcesBuildPhase;
750 | buildActionMask = 2147483647;
751 | files = (
752 | );
753 | runOnlyForDeploymentPostprocessing = 0;
754 | };
755 | EA0F0CD21ED0B448000517A4 /* Resources */ = {
756 | isa = PBXResourcesBuildPhase;
757 | buildActionMask = 2147483647;
758 | files = (
759 | );
760 | runOnlyForDeploymentPostprocessing = 0;
761 | };
762 | EA0F0CDA1ED0B448000517A4 /* Resources */ = {
763 | isa = PBXResourcesBuildPhase;
764 | buildActionMask = 2147483647;
765 | files = (
766 | );
767 | runOnlyForDeploymentPostprocessing = 0;
768 | };
769 | EA0F0D151ED0B50D000517A4 /* Resources */ = {
770 | isa = PBXResourcesBuildPhase;
771 | buildActionMask = 2147483647;
772 | files = (
773 | );
774 | runOnlyForDeploymentPostprocessing = 0;
775 | };
776 | EA12E05C1ECE950000A0DCF7 /* Resources */ = {
777 | isa = PBXResourcesBuildPhase;
778 | buildActionMask = 2147483647;
779 | files = (
780 | );
781 | runOnlyForDeploymentPostprocessing = 0;
782 | };
783 | EAC0BCD01EC90FDB0049FB13 /* Resources */ = {
784 | isa = PBXResourcesBuildPhase;
785 | buildActionMask = 2147483647;
786 | files = (
787 | );
788 | runOnlyForDeploymentPostprocessing = 0;
789 | };
790 | /* End PBXResourcesBuildPhase section */
791 |
792 | /* Begin PBXSourcesBuildPhase section */
793 | EA0F0C6E1ED0B25E000517A4 /* Sources */ = {
794 | isa = PBXSourcesBuildPhase;
795 | buildActionMask = 2147483647;
796 | files = (
797 | EA0F0CB61ED0B3E2000517A4 /* AuthenticationConfiguration.swift in Sources */,
798 | EA0F0CB91ED0B3E2000517A4 /* FeathersError.swift in Sources */,
799 | EA0F0D411ED2AB99000517A4 /* ProviderService.swift in Sources */,
800 | EA0F0CB81ED0B3E2000517A4 /* Feathers.swift in Sources */,
801 | EA0F0CB71ED0B3E2000517A4 /* AuthenticationStorage.swift in Sources */,
802 | EA0F0CBC1ED0B3E2000517A4 /* Endpoint.swift in Sources */,
803 | EA0F0CBB1ED0B3E2000517A4 /* Response.swift in Sources */,
804 | EA0F0CBA1ED0B3E2000517A4 /* Provider.swift in Sources */,
805 | EA0F0CB41ED0B3E2000517A4 /* LoggerHooks.swift in Sources */,
806 | EA0F0CB31ED0B3E2000517A4 /* Hooks.swift in Sources */,
807 | EA8CE0ED1ED7DC2800EA04F4 /* Query.swift in Sources */,
808 | EA0F0D471ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */,
809 | EA0F0D3C1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */,
810 | EA0F0D321ED290EF000517A4 /* ServiceType.swift in Sources */,
811 | EA0F0CB51ED0B3E2000517A4 /* Service.swift in Sources */,
812 | );
813 | runOnlyForDeploymentPostprocessing = 0;
814 | };
815 | EA0F0C771ED0B25F000517A4 /* Sources */ = {
816 | isa = PBXSourcesBuildPhase;
817 | buildActionMask = 2147483647;
818 | files = (
819 | EA0F0CB01ED0B3B3000517A4 /* Hooks.swift in Sources */,
820 | EA0F0CB11ED0B3B3000517A4 /* ServiceSpec.swift in Sources */,
821 | EA0F0CB21ED0B3B3000517A4 /* FeathersSpec.swift in Sources */,
822 | EA0505A51ED9B262006B5CCB /* QuerySpec.swift in Sources */,
823 | EA0F0CAF1ED0B3B3000517A4 /* StubProvider.swift in Sources */,
824 | );
825 | runOnlyForDeploymentPostprocessing = 0;
826 | };
827 | EA0F0CCF1ED0B448000517A4 /* Sources */ = {
828 | isa = PBXSourcesBuildPhase;
829 | buildActionMask = 2147483647;
830 | files = (
831 | EA0F0CEE1ED0B46B000517A4 /* AuthenticationConfiguration.swift in Sources */,
832 | EA0F0CF11ED0B46B000517A4 /* FeathersError.swift in Sources */,
833 | EA0F0D421ED2AB99000517A4 /* ProviderService.swift in Sources */,
834 | EA0F0CF01ED0B46B000517A4 /* Feathers.swift in Sources */,
835 | EA0F0CEF1ED0B46B000517A4 /* AuthenticationStorage.swift in Sources */,
836 | EA0F0CF41ED0B46B000517A4 /* Endpoint.swift in Sources */,
837 | EA0F0CF31ED0B46B000517A4 /* Response.swift in Sources */,
838 | EA0F0CF21ED0B46B000517A4 /* Provider.swift in Sources */,
839 | EA0F0CEC1ED0B46B000517A4 /* LoggerHooks.swift in Sources */,
840 | EA0F0CEB1ED0B46B000517A4 /* Hooks.swift in Sources */,
841 | EA8CE0EE1ED7DC2800EA04F4 /* Query.swift in Sources */,
842 | EA0F0D481ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */,
843 | EA0F0D3D1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */,
844 | EA0F0D331ED290EF000517A4 /* ServiceType.swift in Sources */,
845 | EA0F0CED1ED0B46B000517A4 /* Service.swift in Sources */,
846 | );
847 | runOnlyForDeploymentPostprocessing = 0;
848 | };
849 | EA0F0CD81ED0B448000517A4 /* Sources */ = {
850 | isa = PBXSourcesBuildPhase;
851 | buildActionMask = 2147483647;
852 | files = (
853 | EA0F0CF61ED0B46F000517A4 /* Hooks.swift in Sources */,
854 | EA0F0CF71ED0B46F000517A4 /* ServiceSpec.swift in Sources */,
855 | EA0F0CF81ED0B46F000517A4 /* FeathersSpec.swift in Sources */,
856 | EA0505A61ED9B292006B5CCB /* QuerySpec.swift in Sources */,
857 | EA0F0CF51ED0B46F000517A4 /* StubProvider.swift in Sources */,
858 | );
859 | runOnlyForDeploymentPostprocessing = 0;
860 | };
861 | EA0F0D121ED0B50D000517A4 /* Sources */ = {
862 | isa = PBXSourcesBuildPhase;
863 | buildActionMask = 2147483647;
864 | files = (
865 | EA0F0D281ED0B539000517A4 /* AuthenticationConfiguration.swift in Sources */,
866 | EA0F0D2B1ED0B539000517A4 /* FeathersError.swift in Sources */,
867 | EA0F0D431ED2AB99000517A4 /* ProviderService.swift in Sources */,
868 | EA0F0D2A1ED0B539000517A4 /* Feathers.swift in Sources */,
869 | EA0F0D291ED0B539000517A4 /* AuthenticationStorage.swift in Sources */,
870 | EA0F0D2E1ED0B539000517A4 /* Endpoint.swift in Sources */,
871 | EA0F0D2D1ED0B539000517A4 /* Response.swift in Sources */,
872 | EA0F0D2C1ED0B539000517A4 /* Provider.swift in Sources */,
873 | EA0F0D261ED0B539000517A4 /* LoggerHooks.swift in Sources */,
874 | EA0F0D251ED0B539000517A4 /* Hooks.swift in Sources */,
875 | EA8CE0EF1ED7DC2800EA04F4 /* Query.swift in Sources */,
876 | EA0F0D491ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */,
877 | EA0F0D3E1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */,
878 | EA0F0D341ED290EF000517A4 /* ServiceType.swift in Sources */,
879 | EA0F0D271ED0B539000517A4 /* Service.swift in Sources */,
880 | );
881 | runOnlyForDeploymentPostprocessing = 0;
882 | };
883 | EA12E05A1ECE950000A0DCF7 /* Sources */ = {
884 | isa = PBXSourcesBuildPhase;
885 | buildActionMask = 2147483647;
886 | files = (
887 | EA12E06A1ECE952400A0DCF7 /* StubProvider.swift in Sources */,
888 | EA12E0691ECE950C00A0DCF7 /* ServiceSpec.swift in Sources */,
889 | EA0F0BB21ED0A6C9000517A4 /* FeathersSpec.swift in Sources */,
890 | EA8CE0F11ED7E0FC00EA04F4 /* QuerySpec.swift in Sources */,
891 | EA12E06B1ECE952400A0DCF7 /* Hooks.swift in Sources */,
892 | );
893 | runOnlyForDeploymentPostprocessing = 0;
894 | };
895 | EAC0BCCD1EC90FDB0049FB13 /* Sources */ = {
896 | isa = PBXSourcesBuildPhase;
897 | buildActionMask = 2147483647;
898 | files = (
899 | EA954C781EC9BEEC0094FF43 /* LoggerHooks.swift in Sources */,
900 | EAC0BCF01EC90FF30049FB13 /* Response.swift in Sources */,
901 | EA0F0D401ED2AB99000517A4 /* ProviderService.swift in Sources */,
902 | EAC0BCEB1EC90FF30049FB13 /* AuthenticationStorage.swift in Sources */,
903 | EAC0BCEF1EC90FF30049FB13 /* Provider.swift in Sources */,
904 | EAC0BCEA1EC90FF30049FB13 /* AuthenticationConfiguration.swift in Sources */,
905 | EAC0BCE91EC90FF30049FB13 /* Service.swift in Sources */,
906 | EAC0BCF41EC90FF30049FB13 /* Hooks.swift in Sources */,
907 | EAC0BCED1EC90FF30049FB13 /* FeathersError.swift in Sources */,
908 | EAC0BCEC1EC90FF30049FB13 /* Feathers.swift in Sources */,
909 | EA8CE0EC1ED7DC2800EA04F4 /* Query.swift in Sources */,
910 | EA0F0D461ED3F87F000517A4 /* SignalProducer+Convenience.swift in Sources */,
911 | EA0F0D3B1ED2AA49000517A4 /* ServiceWrapper.swift in Sources */,
912 | EA0F0D311ED290EF000517A4 /* ServiceType.swift in Sources */,
913 | EAC0BCF31EC90FF30049FB13 /* Endpoint.swift in Sources */,
914 | );
915 | runOnlyForDeploymentPostprocessing = 0;
916 | };
917 | /* End PBXSourcesBuildPhase section */
918 |
919 | /* Begin PBXTargetDependency section */
920 | EA0F0C7E1ED0B25F000517A4 /* PBXTargetDependency */ = {
921 | isa = PBXTargetDependency;
922 | target = EA0F0C721ED0B25E000517A4 /* Feathers-macOS */;
923 | targetProxy = EA0F0C7D1ED0B25F000517A4 /* PBXContainerItemProxy */;
924 | };
925 | EA0F0CDF1ED0B448000517A4 /* PBXTargetDependency */ = {
926 | isa = PBXTargetDependency;
927 | target = EA0F0CD31ED0B448000517A4 /* Feathers-tvOS */;
928 | targetProxy = EA0F0CDE1ED0B448000517A4 /* PBXContainerItemProxy */;
929 | };
930 | EA12E0651ECE950100A0DCF7 /* PBXTargetDependency */ = {
931 | isa = PBXTargetDependency;
932 | target = EAC0BCD11EC90FDB0049FB13 /* Feathers-iOS */;
933 | targetProxy = EA12E0641ECE950100A0DCF7 /* PBXContainerItemProxy */;
934 | };
935 | /* End PBXTargetDependency section */
936 |
937 | /* Begin XCBuildConfiguration section */
938 | EA0F0C851ED0B25F000517A4 /* Debug */ = {
939 | isa = XCBuildConfiguration;
940 | buildSettings = {
941 | CODE_SIGN_IDENTITY = "-";
942 | COMBINE_HIDPI_IMAGES = YES;
943 | DEFINES_MODULE = YES;
944 | DYLIB_COMPATIBILITY_VERSION = 1;
945 | DYLIB_CURRENT_VERSION = 1;
946 | DYLIB_INSTALL_NAME_BASE = "@rpath";
947 | FRAMEWORK_SEARCH_PATHS = (
948 | "$(inherited)",
949 | "$(PROJECT_DIR)/Carthage/Build/Mac",
950 | );
951 | FRAMEWORK_VERSION = A;
952 | INFOPLIST_FILE = Feathers/Info.plist;
953 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
954 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
955 | MACOSX_DEPLOYMENT_TARGET = 10.10;
956 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-macOS";
957 | PRODUCT_NAME = Feathers;
958 | SDKROOT = macosx;
959 | SKIP_INSTALL = YES;
960 | SWIFT_VERSION = 5.0;
961 | TVOS_DEPLOYMENT_TARGET = 9.0;
962 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
963 | };
964 | name = Debug;
965 | };
966 | EA0F0C861ED0B25F000517A4 /* Release */ = {
967 | isa = XCBuildConfiguration;
968 | buildSettings = {
969 | CODE_SIGN_IDENTITY = "-";
970 | COMBINE_HIDPI_IMAGES = YES;
971 | DEFINES_MODULE = YES;
972 | DYLIB_COMPATIBILITY_VERSION = 1;
973 | DYLIB_CURRENT_VERSION = 1;
974 | DYLIB_INSTALL_NAME_BASE = "@rpath";
975 | FRAMEWORK_SEARCH_PATHS = (
976 | "$(inherited)",
977 | "$(PROJECT_DIR)/Carthage/Build/Mac",
978 | );
979 | FRAMEWORK_VERSION = A;
980 | INFOPLIST_FILE = Feathers/Info.plist;
981 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
982 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
983 | MACOSX_DEPLOYMENT_TARGET = 10.10;
984 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-macOS";
985 | PRODUCT_NAME = Feathers;
986 | SDKROOT = macosx;
987 | SKIP_INSTALL = YES;
988 | SWIFT_VERSION = 5.0;
989 | TVOS_DEPLOYMENT_TARGET = 9.0;
990 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
991 | };
992 | name = Release;
993 | };
994 | EA0F0C881ED0B25F000517A4 /* Debug */ = {
995 | isa = XCBuildConfiguration;
996 | buildSettings = {
997 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
998 | CODE_SIGN_IDENTITY = "-";
999 | COMBINE_HIDPI_IMAGES = YES;
1000 | FRAMEWORK_SEARCH_PATHS = (
1001 | "$(inherited)",
1002 | "$(PROJECT_DIR)/Carthage/Build/Mac",
1003 | );
1004 | INFOPLIST_FILE = FeathersTests/Info.plist;
1005 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
1006 | MACOSX_DEPLOYMENT_TARGET = 10.12;
1007 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-macOSTests";
1008 | PRODUCT_NAME = "$(TARGET_NAME)";
1009 | SDKROOT = macosx;
1010 | SWIFT_VERSION = 5.0;
1011 | };
1012 | name = Debug;
1013 | };
1014 | EA0F0C891ED0B25F000517A4 /* Release */ = {
1015 | isa = XCBuildConfiguration;
1016 | buildSettings = {
1017 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1018 | CODE_SIGN_IDENTITY = "-";
1019 | COMBINE_HIDPI_IMAGES = YES;
1020 | FRAMEWORK_SEARCH_PATHS = (
1021 | "$(inherited)",
1022 | "$(PROJECT_DIR)/Carthage/Build/Mac",
1023 | );
1024 | INFOPLIST_FILE = FeathersTests/Info.plist;
1025 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
1026 | MACOSX_DEPLOYMENT_TARGET = 10.12;
1027 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-macOSTests";
1028 | PRODUCT_NAME = "$(TARGET_NAME)";
1029 | SDKROOT = macosx;
1030 | SWIFT_VERSION = 5.0;
1031 | };
1032 | name = Release;
1033 | };
1034 | EA0F0CE61ED0B448000517A4 /* Debug */ = {
1035 | isa = XCBuildConfiguration;
1036 | buildSettings = {
1037 | CODE_SIGN_IDENTITY = "";
1038 | DEFINES_MODULE = YES;
1039 | DYLIB_COMPATIBILITY_VERSION = 1;
1040 | DYLIB_CURRENT_VERSION = 1;
1041 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1042 | FRAMEWORK_SEARCH_PATHS = (
1043 | "$(inherited)",
1044 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
1045 | );
1046 | INFOPLIST_FILE = Feathers/Info.plist;
1047 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1048 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1049 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-tvOS";
1050 | PRODUCT_NAME = Feathers;
1051 | SDKROOT = appletvos;
1052 | SKIP_INSTALL = YES;
1053 | SWIFT_VERSION = 5.0;
1054 | TARGETED_DEVICE_FAMILY = 3;
1055 | TVOS_DEPLOYMENT_TARGET = 9.0;
1056 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1057 | };
1058 | name = Debug;
1059 | };
1060 | EA0F0CE71ED0B448000517A4 /* Release */ = {
1061 | isa = XCBuildConfiguration;
1062 | buildSettings = {
1063 | CODE_SIGN_IDENTITY = "";
1064 | DEFINES_MODULE = YES;
1065 | DYLIB_COMPATIBILITY_VERSION = 1;
1066 | DYLIB_CURRENT_VERSION = 1;
1067 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1068 | FRAMEWORK_SEARCH_PATHS = (
1069 | "$(inherited)",
1070 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
1071 | );
1072 | INFOPLIST_FILE = Feathers/Info.plist;
1073 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1074 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1075 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-tvOS";
1076 | PRODUCT_NAME = Feathers;
1077 | SDKROOT = appletvos;
1078 | SKIP_INSTALL = YES;
1079 | SWIFT_VERSION = 5.0;
1080 | TARGETED_DEVICE_FAMILY = 3;
1081 | TVOS_DEPLOYMENT_TARGET = 9.0;
1082 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1083 | };
1084 | name = Release;
1085 | };
1086 | EA0F0CE91ED0B448000517A4 /* Debug */ = {
1087 | isa = XCBuildConfiguration;
1088 | buildSettings = {
1089 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1090 | FRAMEWORK_SEARCH_PATHS = (
1091 | "$(inherited)",
1092 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
1093 | );
1094 | INFOPLIST_FILE = FeathersTests/Info.plist;
1095 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1096 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-tvOSTests";
1097 | PRODUCT_NAME = "$(TARGET_NAME)";
1098 | SDKROOT = appletvos;
1099 | SWIFT_VERSION = 5.0;
1100 | TVOS_DEPLOYMENT_TARGET = 10.2;
1101 | };
1102 | name = Debug;
1103 | };
1104 | EA0F0CEA1ED0B448000517A4 /* Release */ = {
1105 | isa = XCBuildConfiguration;
1106 | buildSettings = {
1107 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1108 | FRAMEWORK_SEARCH_PATHS = (
1109 | "$(inherited)",
1110 | "$(PROJECT_DIR)/Carthage/Build/tvOS",
1111 | );
1112 | INFOPLIST_FILE = FeathersTests/Info.plist;
1113 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1114 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-tvOSTests";
1115 | PRODUCT_NAME = "$(TARGET_NAME)";
1116 | SDKROOT = appletvos;
1117 | SWIFT_VERSION = 5.0;
1118 | TVOS_DEPLOYMENT_TARGET = 10.2;
1119 | };
1120 | name = Release;
1121 | };
1122 | EA0F0D1D1ED0B50D000517A4 /* Debug */ = {
1123 | isa = XCBuildConfiguration;
1124 | buildSettings = {
1125 | APPLICATION_EXTENSION_API_ONLY = YES;
1126 | CODE_SIGN_IDENTITY = "";
1127 | DEFINES_MODULE = YES;
1128 | DYLIB_COMPATIBILITY_VERSION = 1;
1129 | DYLIB_CURRENT_VERSION = 1;
1130 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1131 | FRAMEWORK_SEARCH_PATHS = (
1132 | "$(inherited)",
1133 | "$(PROJECT_DIR)/Carthage/Build/watchOS",
1134 | );
1135 | INFOPLIST_FILE = Feathers/Info.plist;
1136 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1137 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1138 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-watchOS";
1139 | PRODUCT_NAME = Feathers;
1140 | SDKROOT = watchos;
1141 | SKIP_INSTALL = YES;
1142 | SWIFT_VERSION = 5.0;
1143 | TARGETED_DEVICE_FAMILY = 4;
1144 | TVOS_DEPLOYMENT_TARGET = 9.0;
1145 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1146 | };
1147 | name = Debug;
1148 | };
1149 | EA0F0D1E1ED0B50D000517A4 /* Release */ = {
1150 | isa = XCBuildConfiguration;
1151 | buildSettings = {
1152 | APPLICATION_EXTENSION_API_ONLY = YES;
1153 | CODE_SIGN_IDENTITY = "";
1154 | DEFINES_MODULE = YES;
1155 | DYLIB_COMPATIBILITY_VERSION = 1;
1156 | DYLIB_CURRENT_VERSION = 1;
1157 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1158 | FRAMEWORK_SEARCH_PATHS = (
1159 | "$(inherited)",
1160 | "$(PROJECT_DIR)/Carthage/Build/watchOS",
1161 | );
1162 | INFOPLIST_FILE = Feathers/Info.plist;
1163 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1164 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1165 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.Feathers-watchOS";
1166 | PRODUCT_NAME = Feathers;
1167 | SDKROOT = watchos;
1168 | SKIP_INSTALL = YES;
1169 | SWIFT_VERSION = 5.0;
1170 | TARGETED_DEVICE_FAMILY = 4;
1171 | TVOS_DEPLOYMENT_TARGET = 9.0;
1172 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1173 | };
1174 | name = Release;
1175 | };
1176 | EA12E0671ECE950100A0DCF7 /* Debug */ = {
1177 | isa = XCBuildConfiguration;
1178 | buildSettings = {
1179 | FRAMEWORK_SEARCH_PATHS = (
1180 | "$(inherited)",
1181 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1182 | );
1183 | INFOPLIST_FILE = FeathersTests/Info.plist;
1184 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1185 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.FeathersTests-iOS";
1186 | PRODUCT_NAME = "$(TARGET_NAME)";
1187 | SWIFT_VERSION = 5.0;
1188 | };
1189 | name = Debug;
1190 | };
1191 | EA12E0681ECE950100A0DCF7 /* Release */ = {
1192 | isa = XCBuildConfiguration;
1193 | buildSettings = {
1194 | FRAMEWORK_SEARCH_PATHS = (
1195 | "$(inherited)",
1196 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1197 | );
1198 | INFOPLIST_FILE = FeathersTests/Info.plist;
1199 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1200 | PRODUCT_BUNDLE_IDENTIFIER = "com.feathersjs.FeathersTests-iOS";
1201 | PRODUCT_NAME = "$(TARGET_NAME)";
1202 | SWIFT_VERSION = 5.0;
1203 | };
1204 | name = Release;
1205 | };
1206 | EA452A791EA2B14300427CEF /* Debug */ = {
1207 | isa = XCBuildConfiguration;
1208 | buildSettings = {
1209 | ALWAYS_SEARCH_USER_PATHS = NO;
1210 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
1211 | CLANG_ANALYZER_NONNULL = YES;
1212 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
1213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
1214 | CLANG_CXX_LIBRARY = "libc++";
1215 | CLANG_ENABLE_MODULES = YES;
1216 | CLANG_ENABLE_OBJC_ARC = YES;
1217 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
1218 | CLANG_WARN_BOOL_CONVERSION = YES;
1219 | CLANG_WARN_COMMA = YES;
1220 | CLANG_WARN_CONSTANT_CONVERSION = YES;
1221 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
1222 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
1223 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
1224 | CLANG_WARN_EMPTY_BODY = YES;
1225 | CLANG_WARN_ENUM_CONVERSION = YES;
1226 | CLANG_WARN_INFINITE_RECURSION = YES;
1227 | CLANG_WARN_INT_CONVERSION = YES;
1228 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
1229 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
1230 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
1231 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
1232 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
1233 | CLANG_WARN_STRICT_PROTOTYPES = YES;
1234 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
1235 | CLANG_WARN_UNREACHABLE_CODE = YES;
1236 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1237 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
1238 | COPY_PHASE_STRIP = NO;
1239 | CURRENT_PROJECT_VERSION = 1;
1240 | DEBUG_INFORMATION_FORMAT = dwarf;
1241 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1242 | ENABLE_TESTABILITY = YES;
1243 | GCC_C_LANGUAGE_STANDARD = gnu99;
1244 | GCC_DYNAMIC_NO_PIC = NO;
1245 | GCC_NO_COMMON_BLOCKS = YES;
1246 | GCC_OPTIMIZATION_LEVEL = 0;
1247 | GCC_PREPROCESSOR_DEFINITIONS = (
1248 | "DEBUG=1",
1249 | "$(inherited)",
1250 | );
1251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1253 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1255 | GCC_WARN_UNUSED_FUNCTION = YES;
1256 | GCC_WARN_UNUSED_VARIABLE = YES;
1257 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
1258 | MACOSX_DEPLOYMENT_TARGET = 10.10;
1259 | MTL_ENABLE_DEBUG_INFO = YES;
1260 | ONLY_ACTIVE_ARCH = YES;
1261 | PRODUCT_NAME = Feathers;
1262 | SDKROOT = iphoneos;
1263 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
1264 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1265 | TARGETED_DEVICE_FAMILY = "1,2";
1266 | VERSIONING_SYSTEM = "apple-generic";
1267 | VERSION_INFO_PREFIX = "";
1268 | };
1269 | name = Debug;
1270 | };
1271 | EA452A7A1EA2B14300427CEF /* Release */ = {
1272 | isa = XCBuildConfiguration;
1273 | buildSettings = {
1274 | ALWAYS_SEARCH_USER_PATHS = NO;
1275 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
1276 | CLANG_ANALYZER_NONNULL = YES;
1277 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
1278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
1279 | CLANG_CXX_LIBRARY = "libc++";
1280 | CLANG_ENABLE_MODULES = YES;
1281 | CLANG_ENABLE_OBJC_ARC = YES;
1282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
1283 | CLANG_WARN_BOOL_CONVERSION = YES;
1284 | CLANG_WARN_COMMA = YES;
1285 | CLANG_WARN_CONSTANT_CONVERSION = YES;
1286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
1287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
1288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
1289 | CLANG_WARN_EMPTY_BODY = YES;
1290 | CLANG_WARN_ENUM_CONVERSION = YES;
1291 | CLANG_WARN_INFINITE_RECURSION = YES;
1292 | CLANG_WARN_INT_CONVERSION = YES;
1293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
1294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
1295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
1296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
1297 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
1298 | CLANG_WARN_STRICT_PROTOTYPES = YES;
1299 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
1300 | CLANG_WARN_UNREACHABLE_CODE = YES;
1301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1302 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
1303 | COPY_PHASE_STRIP = NO;
1304 | CURRENT_PROJECT_VERSION = 1;
1305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
1306 | ENABLE_NS_ASSERTIONS = NO;
1307 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1308 | GCC_C_LANGUAGE_STANDARD = gnu99;
1309 | GCC_NO_COMMON_BLOCKS = YES;
1310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1312 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1314 | GCC_WARN_UNUSED_FUNCTION = YES;
1315 | GCC_WARN_UNUSED_VARIABLE = YES;
1316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
1317 | MACOSX_DEPLOYMENT_TARGET = 10.10;
1318 | MTL_ENABLE_DEBUG_INFO = NO;
1319 | PRODUCT_NAME = Feathers;
1320 | SDKROOT = iphoneos;
1321 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
1322 | TARGETED_DEVICE_FAMILY = "1,2";
1323 | VALIDATE_PRODUCT = YES;
1324 | VERSIONING_SYSTEM = "apple-generic";
1325 | VERSION_INFO_PREFIX = "";
1326 | };
1327 | name = Release;
1328 | };
1329 | EAC0BCE41EC90FDB0049FB13 /* Debug */ = {
1330 | isa = XCBuildConfiguration;
1331 | buildSettings = {
1332 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
1333 | CODE_SIGN_IDENTITY = "";
1334 | DEFINES_MODULE = YES;
1335 | DYLIB_COMPATIBILITY_VERSION = 1;
1336 | DYLIB_CURRENT_VERSION = 1;
1337 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1338 | FRAMEWORK_SEARCH_PATHS = (
1339 | "$(inherited)",
1340 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1341 | );
1342 | INFOPLIST_FILE = Feathers/Info.plist;
1343 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1344 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
1345 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1346 | OTHER_LDFLAGS = "$(inherited)";
1347 | PRODUCT_BUNDLE_IDENTIFIER = "com.swoopystudios.Feathers-iOS";
1348 | PRODUCT_NAME = Feathers;
1349 | SKIP_INSTALL = YES;
1350 | SWIFT_VERSION = 5.0;
1351 | TVOS_DEPLOYMENT_TARGET = 9.0;
1352 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1353 | };
1354 | name = Debug;
1355 | };
1356 | EAC0BCE51EC90FDB0049FB13 /* Release */ = {
1357 | isa = XCBuildConfiguration;
1358 | buildSettings = {
1359 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)";
1360 | CODE_SIGN_IDENTITY = "";
1361 | DEFINES_MODULE = YES;
1362 | DYLIB_COMPATIBILITY_VERSION = 1;
1363 | DYLIB_CURRENT_VERSION = 1;
1364 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1365 | FRAMEWORK_SEARCH_PATHS = (
1366 | "$(inherited)",
1367 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1368 | );
1369 | INFOPLIST_FILE = Feathers/Info.plist;
1370 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1371 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
1372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1373 | OTHER_LDFLAGS = "$(inherited)";
1374 | PRODUCT_BUNDLE_IDENTIFIER = "com.swoopystudios.Feathers-iOS";
1375 | PRODUCT_NAME = Feathers;
1376 | SKIP_INSTALL = YES;
1377 | SWIFT_VERSION = 5.0;
1378 | TVOS_DEPLOYMENT_TARGET = 9.0;
1379 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
1380 | };
1381 | name = Release;
1382 | };
1383 | /* End XCBuildConfiguration section */
1384 |
1385 | /* Begin XCConfigurationList section */
1386 | EA0F0C841ED0B25F000517A4 /* Build configuration list for PBXNativeTarget "Feathers-macOS" */ = {
1387 | isa = XCConfigurationList;
1388 | buildConfigurations = (
1389 | EA0F0C851ED0B25F000517A4 /* Debug */,
1390 | EA0F0C861ED0B25F000517A4 /* Release */,
1391 | );
1392 | defaultConfigurationIsVisible = 0;
1393 | defaultConfigurationName = Release;
1394 | };
1395 | EA0F0C871ED0B25F000517A4 /* Build configuration list for PBXNativeTarget "Feathers-macOSTests" */ = {
1396 | isa = XCConfigurationList;
1397 | buildConfigurations = (
1398 | EA0F0C881ED0B25F000517A4 /* Debug */,
1399 | EA0F0C891ED0B25F000517A4 /* Release */,
1400 | );
1401 | defaultConfigurationIsVisible = 0;
1402 | defaultConfigurationName = Release;
1403 | };
1404 | EA0F0CE51ED0B448000517A4 /* Build configuration list for PBXNativeTarget "Feathers-tvOS" */ = {
1405 | isa = XCConfigurationList;
1406 | buildConfigurations = (
1407 | EA0F0CE61ED0B448000517A4 /* Debug */,
1408 | EA0F0CE71ED0B448000517A4 /* Release */,
1409 | );
1410 | defaultConfigurationIsVisible = 0;
1411 | defaultConfigurationName = Release;
1412 | };
1413 | EA0F0CE81ED0B448000517A4 /* Build configuration list for PBXNativeTarget "Feathers-tvOSTests" */ = {
1414 | isa = XCConfigurationList;
1415 | buildConfigurations = (
1416 | EA0F0CE91ED0B448000517A4 /* Debug */,
1417 | EA0F0CEA1ED0B448000517A4 /* Release */,
1418 | );
1419 | defaultConfigurationIsVisible = 0;
1420 | defaultConfigurationName = Release;
1421 | };
1422 | EA0F0D1C1ED0B50D000517A4 /* Build configuration list for PBXNativeTarget "Feathers-watchOS" */ = {
1423 | isa = XCConfigurationList;
1424 | buildConfigurations = (
1425 | EA0F0D1D1ED0B50D000517A4 /* Debug */,
1426 | EA0F0D1E1ED0B50D000517A4 /* Release */,
1427 | );
1428 | defaultConfigurationIsVisible = 0;
1429 | defaultConfigurationName = Release;
1430 | };
1431 | EA12E0661ECE950100A0DCF7 /* Build configuration list for PBXNativeTarget "Feathers-iOSTests" */ = {
1432 | isa = XCConfigurationList;
1433 | buildConfigurations = (
1434 | EA12E0671ECE950100A0DCF7 /* Debug */,
1435 | EA12E0681ECE950100A0DCF7 /* Release */,
1436 | );
1437 | defaultConfigurationIsVisible = 0;
1438 | defaultConfigurationName = Release;
1439 | };
1440 | EA452A611EA2B14300427CEF /* Build configuration list for PBXProject "Feathers" */ = {
1441 | isa = XCConfigurationList;
1442 | buildConfigurations = (
1443 | EA452A791EA2B14300427CEF /* Debug */,
1444 | EA452A7A1EA2B14300427CEF /* Release */,
1445 | );
1446 | defaultConfigurationIsVisible = 0;
1447 | defaultConfigurationName = Release;
1448 | };
1449 | EAC0BCE31EC90FDB0049FB13 /* Build configuration list for PBXNativeTarget "Feathers-iOS" */ = {
1450 | isa = XCConfigurationList;
1451 | buildConfigurations = (
1452 | EAC0BCE41EC90FDB0049FB13 /* Debug */,
1453 | EAC0BCE51EC90FDB0049FB13 /* Release */,
1454 | );
1455 | defaultConfigurationIsVisible = 0;
1456 | defaultConfigurationName = Release;
1457 | };
1458 | /* End XCConfigurationList section */
1459 | };
1460 | rootObject = EA452A5E1EA2B14300427CEF /* Project object */;
1461 | }
1462 |
--------------------------------------------------------------------------------