├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj ├── Result │ ├── LICENSE │ ├── README.md │ └── Result │ │ ├── Result.swift │ │ └── ResultProtocol.swift ├── Target Support Files │ ├── Alamofire │ │ ├── Alamofire-dummy.m │ │ ├── Alamofire-prefix.pch │ │ ├── Alamofire-umbrella.h │ │ ├── Alamofire.modulemap │ │ ├── Alamofire.xcconfig │ │ └── Info.plist │ ├── Pods-luckybeast │ │ ├── Info.plist │ │ ├── Pods-luckybeast-acknowledgements.markdown │ │ ├── Pods-luckybeast-acknowledgements.plist │ │ ├── Pods-luckybeast-dummy.m │ │ ├── Pods-luckybeast-frameworks.sh │ │ ├── Pods-luckybeast-resources.sh │ │ ├── Pods-luckybeast-umbrella.h │ │ ├── Pods-luckybeast.debug.xcconfig │ │ ├── Pods-luckybeast.modulemap │ │ └── Pods-luckybeast.release.xcconfig │ ├── Pods-luckybeastTests │ │ ├── Info.plist │ │ ├── Pods-luckybeastTests-acknowledgements.markdown │ │ ├── Pods-luckybeastTests-acknowledgements.plist │ │ ├── Pods-luckybeastTests-dummy.m │ │ ├── Pods-luckybeastTests-frameworks.sh │ │ ├── Pods-luckybeastTests-resources.sh │ │ ├── Pods-luckybeastTests-umbrella.h │ │ ├── Pods-luckybeastTests.debug.xcconfig │ │ ├── Pods-luckybeastTests.modulemap │ │ └── Pods-luckybeastTests.release.xcconfig │ ├── Result │ │ ├── Info.plist │ │ ├── Result-dummy.m │ │ ├── Result-prefix.pch │ │ ├── Result-umbrella.h │ │ ├── Result.modulemap │ │ └── Result.xcconfig │ └── UIView+Shake │ │ ├── Info.plist │ │ ├── UIView+Shake-dummy.m │ │ ├── UIView+Shake-prefix.pch │ │ ├── UIView+Shake-umbrella.h │ │ ├── UIView+Shake.modulemap │ │ └── UIView+Shake.xcconfig └── UIView+Shake │ ├── LICENSE │ ├── README.md │ └── Source │ ├── UIView+Shake.h │ └── UIView+Shake.m ├── README.md ├── luckybeast.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── luckybeast.xcworkspace └── contents.xcworkspacedata ├── luckybeast ├── AppDelegate.swift ├── AppSettings.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── BackgroundView.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── BossCoreView.swift ├── Capturer.swift ├── Const.swift ├── DisplayMode.swift ├── EyeView.swift ├── FoundationExt.swift ├── Info.plist ├── Keys.plist ├── Listener.swift ├── LuckyBeast.swift ├── ServerAPI.swift ├── Speaker.swift ├── Vibrator.swift └── ViewController.swift └── luckybeastTests ├── Info.plist └── luckybeastTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/94d87c504178b3a4e8b95eb95e78e96c6ccefe46/Swift.gitignore 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xcuserstate 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | *.dSYM.zip 30 | *.dSYM 31 | 32 | ## Playgrounds 33 | timeline.xctimeline 34 | playground.xcworkspace 35 | 36 | # Swift Package Manager 37 | # 38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 39 | # Packages/ 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | 69 | 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sota Yokoe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '9.0' 3 | 4 | target 'luckybeast' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for luckybeast 9 | pod "Alamofire" 10 | pod "Result" 11 | pod "UIView+Shake" 12 | 13 | target 'luckybeastTests' do 14 | inherit! :search_paths 15 | # Pods for testing 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.4.0) 3 | - Result (3.2.1) 4 | - UIView+Shake (1.3.0) 5 | 6 | DEPENDENCIES: 7 | - Alamofire 8 | - Result 9 | - UIView+Shake 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d 13 | Result: 2453a22e5c5b11c0c3a478736e82cd02f763b781 14 | UIView+Shake: 3ccbf966d40a76ea2cdbdd48d8219d322d5df5f5 15 | 16 | PODFILE CHECKSUM: d9379960b7e95d6c90f06b0a2ab108f19148b64a 17 | 18 | COCOAPODS: 1.3.0 19 | -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/NetworkReachabilityManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkReachabilityManager.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | #if !os(watchOS) 26 | 27 | import Foundation 28 | import SystemConfiguration 29 | 30 | /// The `NetworkReachabilityManager` class listens for reachability changes of hosts and addresses for both WWAN and 31 | /// WiFi network interfaces. 32 | /// 33 | /// Reachability can be used to determine background information about why a network operation failed, or to retry 34 | /// network requests when a connection is established. It should not be used to prevent a user from initiating a network 35 | /// request, as it's possible that an initial request may be required to establish reachability. 36 | public class NetworkReachabilityManager { 37 | /// Defines the various states of network reachability. 38 | /// 39 | /// - unknown: It is unknown whether the network is reachable. 40 | /// - notReachable: The network is not reachable. 41 | /// - reachable: The network is reachable. 42 | public enum NetworkReachabilityStatus { 43 | case unknown 44 | case notReachable 45 | case reachable(ConnectionType) 46 | } 47 | 48 | /// Defines the various connection types detected by reachability flags. 49 | /// 50 | /// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi. 51 | /// - wwan: The connection type is a WWAN connection. 52 | public enum ConnectionType { 53 | case ethernetOrWiFi 54 | case wwan 55 | } 56 | 57 | /// A closure executed when the network reachability status changes. The closure takes a single argument: the 58 | /// network reachability status. 59 | public typealias Listener = (NetworkReachabilityStatus) -> Void 60 | 61 | // MARK: - Properties 62 | 63 | /// Whether the network is currently reachable. 64 | public var isReachable: Bool { return isReachableOnWWAN || isReachableOnEthernetOrWiFi } 65 | 66 | /// Whether the network is currently reachable over the WWAN interface. 67 | public var isReachableOnWWAN: Bool { return networkReachabilityStatus == .reachable(.wwan) } 68 | 69 | /// Whether the network is currently reachable over Ethernet or WiFi interface. 70 | public var isReachableOnEthernetOrWiFi: Bool { return networkReachabilityStatus == .reachable(.ethernetOrWiFi) } 71 | 72 | /// The current network reachability status. 73 | public var networkReachabilityStatus: NetworkReachabilityStatus { 74 | guard let flags = self.flags else { return .unknown } 75 | return networkReachabilityStatusForFlags(flags) 76 | } 77 | 78 | /// The dispatch queue to execute the `listener` closure on. 79 | public var listenerQueue: DispatchQueue = DispatchQueue.main 80 | 81 | /// A closure executed when the network reachability status changes. 82 | public var listener: Listener? 83 | 84 | private var flags: SCNetworkReachabilityFlags? { 85 | var flags = SCNetworkReachabilityFlags() 86 | 87 | if SCNetworkReachabilityGetFlags(reachability, &flags) { 88 | return flags 89 | } 90 | 91 | return nil 92 | } 93 | 94 | private let reachability: SCNetworkReachability 95 | private var previousFlags: SCNetworkReachabilityFlags 96 | 97 | // MARK: - Initialization 98 | 99 | /// Creates a `NetworkReachabilityManager` instance with the specified host. 100 | /// 101 | /// - parameter host: The host used to evaluate network reachability. 102 | /// 103 | /// - returns: The new `NetworkReachabilityManager` instance. 104 | public convenience init?(host: String) { 105 | guard let reachability = SCNetworkReachabilityCreateWithName(nil, host) else { return nil } 106 | self.init(reachability: reachability) 107 | } 108 | 109 | /// Creates a `NetworkReachabilityManager` instance that monitors the address 0.0.0.0. 110 | /// 111 | /// Reachability treats the 0.0.0.0 address as a special token that causes it to monitor the general routing 112 | /// status of the device, both IPv4 and IPv6. 113 | /// 114 | /// - returns: The new `NetworkReachabilityManager` instance. 115 | public convenience init?() { 116 | var address = sockaddr_in() 117 | address.sin_len = UInt8(MemoryLayout.size) 118 | address.sin_family = sa_family_t(AF_INET) 119 | 120 | guard let reachability = withUnsafePointer(to: &address, { pointer in 121 | return pointer.withMemoryRebound(to: sockaddr.self, capacity: MemoryLayout.size) { 122 | return SCNetworkReachabilityCreateWithAddress(nil, $0) 123 | } 124 | }) else { return nil } 125 | 126 | self.init(reachability: reachability) 127 | } 128 | 129 | private init(reachability: SCNetworkReachability) { 130 | self.reachability = reachability 131 | self.previousFlags = SCNetworkReachabilityFlags() 132 | } 133 | 134 | deinit { 135 | stopListening() 136 | } 137 | 138 | // MARK: - Listening 139 | 140 | /// Starts listening for changes in network reachability status. 141 | /// 142 | /// - returns: `true` if listening was started successfully, `false` otherwise. 143 | @discardableResult 144 | public func startListening() -> Bool { 145 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 146 | context.info = Unmanaged.passUnretained(self).toOpaque() 147 | 148 | let callbackEnabled = SCNetworkReachabilitySetCallback( 149 | reachability, 150 | { (_, flags, info) in 151 | let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() 152 | reachability.notifyListener(flags) 153 | }, 154 | &context 155 | ) 156 | 157 | let queueEnabled = SCNetworkReachabilitySetDispatchQueue(reachability, listenerQueue) 158 | 159 | listenerQueue.async { 160 | self.previousFlags = SCNetworkReachabilityFlags() 161 | self.notifyListener(self.flags ?? SCNetworkReachabilityFlags()) 162 | } 163 | 164 | return callbackEnabled && queueEnabled 165 | } 166 | 167 | /// Stops listening for changes in network reachability status. 168 | public func stopListening() { 169 | SCNetworkReachabilitySetCallback(reachability, nil, nil) 170 | SCNetworkReachabilitySetDispatchQueue(reachability, nil) 171 | } 172 | 173 | // MARK: - Internal - Listener Notification 174 | 175 | func notifyListener(_ flags: SCNetworkReachabilityFlags) { 176 | guard previousFlags != flags else { return } 177 | previousFlags = flags 178 | 179 | listener?(networkReachabilityStatusForFlags(flags)) 180 | } 181 | 182 | // MARK: - Internal - Network Reachability Status 183 | 184 | func networkReachabilityStatusForFlags(_ flags: SCNetworkReachabilityFlags) -> NetworkReachabilityStatus { 185 | guard flags.contains(.reachable) else { return .notReachable } 186 | 187 | var networkStatus: NetworkReachabilityStatus = .notReachable 188 | 189 | if !flags.contains(.connectionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 190 | 191 | if flags.contains(.connectionOnDemand) || flags.contains(.connectionOnTraffic) { 192 | if !flags.contains(.interventionRequired) { networkStatus = .reachable(.ethernetOrWiFi) } 193 | } 194 | 195 | #if os(iOS) 196 | if flags.contains(.isWWAN) { networkStatus = .reachable(.wwan) } 197 | #endif 198 | 199 | return networkStatus 200 | } 201 | } 202 | 203 | // MARK: - 204 | 205 | extension NetworkReachabilityManager.NetworkReachabilityStatus: Equatable {} 206 | 207 | /// Returns whether the two network reachability status values are equal. 208 | /// 209 | /// - parameter lhs: The left-hand side value to compare. 210 | /// - parameter rhs: The right-hand side value to compare. 211 | /// 212 | /// - returns: `true` if the two values are equal, `false` otherwise. 213 | public func ==( 214 | lhs: NetworkReachabilityManager.NetworkReachabilityStatus, 215 | rhs: NetworkReachabilityManager.NetworkReachabilityStatus) 216 | -> Bool 217 | { 218 | switch (lhs, rhs) { 219 | case (.unknown, .unknown): 220 | return true 221 | case (.notReachable, .notReachable): 222 | return true 223 | case let (.reachable(lhsConnectionType), .reachable(rhsConnectionType)): 224 | return lhsConnectionType == rhsConnectionType 225 | default: 226 | return false 227 | } 228 | } 229 | 230 | #endif 231 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Used to represent whether a request was successful or encountered an error. 28 | /// 29 | /// - success: The request and all post processing operations were successful resulting in the serialization of the 30 | /// provided associated value. 31 | /// 32 | /// - failure: The request encountered an error resulting in a failure. The associated values are the original data 33 | /// provided by the server as well as the error that caused the failure. 34 | public enum Result { 35 | case success(Value) 36 | case failure(Error) 37 | 38 | /// Returns `true` if the result is a success, `false` otherwise. 39 | public var isSuccess: Bool { 40 | switch self { 41 | case .success: 42 | return true 43 | case .failure: 44 | return false 45 | } 46 | } 47 | 48 | /// Returns `true` if the result is a failure, `false` otherwise. 49 | public var isFailure: Bool { 50 | return !isSuccess 51 | } 52 | 53 | /// Returns the associated value if the result is a success, `nil` otherwise. 54 | public var value: Value? { 55 | switch self { 56 | case .success(let value): 57 | return value 58 | case .failure: 59 | return nil 60 | } 61 | } 62 | 63 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 64 | public var error: Error? { 65 | switch self { 66 | case .success: 67 | return nil 68 | case .failure(let error): 69 | return error 70 | } 71 | } 72 | } 73 | 74 | // MARK: - CustomStringConvertible 75 | 76 | extension Result: CustomStringConvertible { 77 | /// The textual representation used when written to an output stream, which includes whether the result was a 78 | /// success or failure. 79 | public var description: String { 80 | switch self { 81 | case .success: 82 | return "SUCCESS" 83 | case .failure: 84 | return "FAILURE" 85 | } 86 | } 87 | } 88 | 89 | // MARK: - CustomDebugStringConvertible 90 | 91 | extension Result: CustomDebugStringConvertible { 92 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 93 | /// success or failure in addition to the value or error. 94 | public var debugDescription: String { 95 | switch self { 96 | case .success(let value): 97 | return "SUCCESS: \(value)" 98 | case .failure(let error): 99 | return "FAILURE: \(error)" 100 | } 101 | } 102 | } 103 | 104 | // MARK: - Functional APIs 105 | 106 | extension Result { 107 | /// Creates a `Result` instance from the result of a closure. 108 | /// 109 | /// A failure result is created when the closure throws, and a success result is created when the closure 110 | /// succeeds without throwing an error. 111 | /// 112 | /// func someString() throws -> String { ... } 113 | /// 114 | /// let result = Result(value: { 115 | /// return try someString() 116 | /// }) 117 | /// 118 | /// // The type of result is Result 119 | /// 120 | /// The trailing closure syntax is also supported: 121 | /// 122 | /// let result = Result { try someString() } 123 | /// 124 | /// - parameter value: The closure to execute and create the result for. 125 | public init(value: () throws -> Value) { 126 | do { 127 | self = try .success(value()) 128 | } catch { 129 | self = .failure(error) 130 | } 131 | } 132 | 133 | /// Returns the success value, or throws the failure error. 134 | /// 135 | /// let possibleString: Result = .success("success") 136 | /// try print(possibleString.unwrap()) 137 | /// // Prints "success" 138 | /// 139 | /// let noString: Result = .failure(error) 140 | /// try print(noString.unwrap()) 141 | /// // Throws error 142 | public func unwrap() throws -> Value { 143 | switch self { 144 | case .success(let value): 145 | return value 146 | case .failure(let error): 147 | throw error 148 | } 149 | } 150 | 151 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 152 | /// 153 | /// Use the `map` method with a closure that does not throw. For example: 154 | /// 155 | /// let possibleData: Result = .success(Data()) 156 | /// let possibleInt = possibleData.map { $0.count } 157 | /// try print(possibleInt.unwrap()) 158 | /// // Prints "0" 159 | /// 160 | /// let noData: Result = .failure(error) 161 | /// let noInt = noData.map { $0.count } 162 | /// try print(noInt.unwrap()) 163 | /// // Throws error 164 | /// 165 | /// - parameter transform: A closure that takes the success value of the result instance. 166 | /// 167 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 168 | /// same failure. 169 | public func map(_ transform: (Value) -> T) -> Result { 170 | switch self { 171 | case .success(let value): 172 | return .success(transform(value)) 173 | case .failure(let error): 174 | return .failure(error) 175 | } 176 | } 177 | 178 | /// Evaluates the specified closure when the `Result` is a success, passing the unwrapped value as a parameter. 179 | /// 180 | /// Use the `flatMap` method with a closure that may throw an error. For example: 181 | /// 182 | /// let possibleData: Result = .success(Data(...)) 183 | /// let possibleObject = possibleData.flatMap { 184 | /// try JSONSerialization.jsonObject(with: $0) 185 | /// } 186 | /// 187 | /// - parameter transform: A closure that takes the success value of the instance. 188 | /// 189 | /// - returns: A `Result` containing the result of the given closure. If this instance is a failure, returns the 190 | /// same failure. 191 | public func flatMap(_ transform: (Value) throws -> T) -> Result { 192 | switch self { 193 | case .success(let value): 194 | do { 195 | return try .success(transform(value)) 196 | } catch { 197 | return .failure(error) 198 | } 199 | case .failure(let error): 200 | return .failure(error) 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/ServerTrustPolicy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerTrustPolicy.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for managing the mapping of `ServerTrustPolicy` objects to a given host. 28 | open class ServerTrustPolicyManager { 29 | /// The dictionary of policies mapped to a particular host. 30 | open let policies: [String: ServerTrustPolicy] 31 | 32 | /// Initializes the `ServerTrustPolicyManager` instance with the given policies. 33 | /// 34 | /// Since different servers and web services can have different leaf certificates, intermediate and even root 35 | /// certficates, it is important to have the flexibility to specify evaluation policies on a per host basis. This 36 | /// allows for scenarios such as using default evaluation for host1, certificate pinning for host2, public key 37 | /// pinning for host3 and disabling evaluation for host4. 38 | /// 39 | /// - parameter policies: A dictionary of all policies mapped to a particular host. 40 | /// 41 | /// - returns: The new `ServerTrustPolicyManager` instance. 42 | public init(policies: [String: ServerTrustPolicy]) { 43 | self.policies = policies 44 | } 45 | 46 | /// Returns the `ServerTrustPolicy` for the given host if applicable. 47 | /// 48 | /// By default, this method will return the policy that perfectly matches the given host. Subclasses could override 49 | /// this method and implement more complex mapping implementations such as wildcards. 50 | /// 51 | /// - parameter host: The host to use when searching for a matching policy. 52 | /// 53 | /// - returns: The server trust policy for the given host if found. 54 | open func serverTrustPolicy(forHost host: String) -> ServerTrustPolicy? { 55 | return policies[host] 56 | } 57 | } 58 | 59 | // MARK: - 60 | 61 | extension URLSession { 62 | private struct AssociatedKeys { 63 | static var managerKey = "URLSession.ServerTrustPolicyManager" 64 | } 65 | 66 | var serverTrustPolicyManager: ServerTrustPolicyManager? { 67 | get { 68 | return objc_getAssociatedObject(self, &AssociatedKeys.managerKey) as? ServerTrustPolicyManager 69 | } 70 | set (manager) { 71 | objc_setAssociatedObject(self, &AssociatedKeys.managerKey, manager, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 72 | } 73 | } 74 | } 75 | 76 | // MARK: - ServerTrustPolicy 77 | 78 | /// The `ServerTrustPolicy` evaluates the server trust generally provided by an `NSURLAuthenticationChallenge` when 79 | /// connecting to a server over a secure HTTPS connection. The policy configuration then evaluates the server trust 80 | /// with a given set of criteria to determine whether the server trust is valid and the connection should be made. 81 | /// 82 | /// Using pinned certificates or public keys for evaluation helps prevent man-in-the-middle (MITM) attacks and other 83 | /// vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged 84 | /// to route all communication over an HTTPS connection with pinning enabled. 85 | /// 86 | /// - performDefaultEvaluation: Uses the default server trust evaluation while allowing you to control whether to 87 | /// validate the host provided by the challenge. Applications are encouraged to always 88 | /// validate the host in production environments to guarantee the validity of the server's 89 | /// certificate chain. 90 | /// 91 | /// - performRevokedEvaluation: Uses the default and revoked server trust evaluations allowing you to control whether to 92 | /// validate the host provided by the challenge as well as specify the revocation flags for 93 | /// testing for revoked certificates. Apple platforms did not start testing for revoked 94 | /// certificates automatically until iOS 10.1, macOS 10.12 and tvOS 10.1 which is 95 | /// demonstrated in our TLS tests. Applications are encouraged to always validate the host 96 | /// in production environments to guarantee the validity of the server's certificate chain. 97 | /// 98 | /// - pinCertificates: Uses the pinned certificates to validate the server trust. The server trust is 99 | /// considered valid if one of the pinned certificates match one of the server certificates. 100 | /// By validating both the certificate chain and host, certificate pinning provides a very 101 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 102 | /// Applications are encouraged to always validate the host and require a valid certificate 103 | /// chain in production environments. 104 | /// 105 | /// - pinPublicKeys: Uses the pinned public keys to validate the server trust. The server trust is considered 106 | /// valid if one of the pinned public keys match one of the server certificate public keys. 107 | /// By validating both the certificate chain and host, public key pinning provides a very 108 | /// secure form of server trust validation mitigating most, if not all, MITM attacks. 109 | /// Applications are encouraged to always validate the host and require a valid certificate 110 | /// chain in production environments. 111 | /// 112 | /// - disableEvaluation: Disables all evaluation which in turn will always consider any server trust as valid. 113 | /// 114 | /// - customEvaluation: Uses the associated closure to evaluate the validity of the server trust. 115 | public enum ServerTrustPolicy { 116 | case performDefaultEvaluation(validateHost: Bool) 117 | case performRevokedEvaluation(validateHost: Bool, revocationFlags: CFOptionFlags) 118 | case pinCertificates(certificates: [SecCertificate], validateCertificateChain: Bool, validateHost: Bool) 119 | case pinPublicKeys(publicKeys: [SecKey], validateCertificateChain: Bool, validateHost: Bool) 120 | case disableEvaluation 121 | case customEvaluation((_ serverTrust: SecTrust, _ host: String) -> Bool) 122 | 123 | // MARK: - Bundle Location 124 | 125 | /// Returns all certificates within the given bundle with a `.cer` file extension. 126 | /// 127 | /// - parameter bundle: The bundle to search for all `.cer` files. 128 | /// 129 | /// - returns: All certificates within the given bundle. 130 | public static func certificates(in bundle: Bundle = Bundle.main) -> [SecCertificate] { 131 | var certificates: [SecCertificate] = [] 132 | 133 | let paths = Set([".cer", ".CER", ".crt", ".CRT", ".der", ".DER"].map { fileExtension in 134 | bundle.paths(forResourcesOfType: fileExtension, inDirectory: nil) 135 | }.joined()) 136 | 137 | for path in paths { 138 | if 139 | let certificateData = try? Data(contentsOf: URL(fileURLWithPath: path)) as CFData, 140 | let certificate = SecCertificateCreateWithData(nil, certificateData) 141 | { 142 | certificates.append(certificate) 143 | } 144 | } 145 | 146 | return certificates 147 | } 148 | 149 | /// Returns all public keys within the given bundle with a `.cer` file extension. 150 | /// 151 | /// - parameter bundle: The bundle to search for all `*.cer` files. 152 | /// 153 | /// - returns: All public keys within the given bundle. 154 | public static func publicKeys(in bundle: Bundle = Bundle.main) -> [SecKey] { 155 | var publicKeys: [SecKey] = [] 156 | 157 | for certificate in certificates(in: bundle) { 158 | if let publicKey = publicKey(for: certificate) { 159 | publicKeys.append(publicKey) 160 | } 161 | } 162 | 163 | return publicKeys 164 | } 165 | 166 | // MARK: - Evaluation 167 | 168 | /// Evaluates whether the server trust is valid for the given host. 169 | /// 170 | /// - parameter serverTrust: The server trust to evaluate. 171 | /// - parameter host: The host of the challenge protection space. 172 | /// 173 | /// - returns: Whether the server trust is valid. 174 | public func evaluate(_ serverTrust: SecTrust, forHost host: String) -> Bool { 175 | var serverTrustIsValid = false 176 | 177 | switch self { 178 | case let .performDefaultEvaluation(validateHost): 179 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 180 | SecTrustSetPolicies(serverTrust, policy) 181 | 182 | serverTrustIsValid = trustIsValid(serverTrust) 183 | case let .performRevokedEvaluation(validateHost, revocationFlags): 184 | let defaultPolicy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 185 | let revokedPolicy = SecPolicyCreateRevocation(revocationFlags) 186 | SecTrustSetPolicies(serverTrust, [defaultPolicy, revokedPolicy] as CFTypeRef) 187 | 188 | serverTrustIsValid = trustIsValid(serverTrust) 189 | case let .pinCertificates(pinnedCertificates, validateCertificateChain, validateHost): 190 | if validateCertificateChain { 191 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 192 | SecTrustSetPolicies(serverTrust, policy) 193 | 194 | SecTrustSetAnchorCertificates(serverTrust, pinnedCertificates as CFArray) 195 | SecTrustSetAnchorCertificatesOnly(serverTrust, true) 196 | 197 | serverTrustIsValid = trustIsValid(serverTrust) 198 | } else { 199 | let serverCertificatesDataArray = certificateData(for: serverTrust) 200 | let pinnedCertificatesDataArray = certificateData(for: pinnedCertificates) 201 | 202 | outerLoop: for serverCertificateData in serverCertificatesDataArray { 203 | for pinnedCertificateData in pinnedCertificatesDataArray { 204 | if serverCertificateData == pinnedCertificateData { 205 | serverTrustIsValid = true 206 | break outerLoop 207 | } 208 | } 209 | } 210 | } 211 | case let .pinPublicKeys(pinnedPublicKeys, validateCertificateChain, validateHost): 212 | var certificateChainEvaluationPassed = true 213 | 214 | if validateCertificateChain { 215 | let policy = SecPolicyCreateSSL(true, validateHost ? host as CFString : nil) 216 | SecTrustSetPolicies(serverTrust, policy) 217 | 218 | certificateChainEvaluationPassed = trustIsValid(serverTrust) 219 | } 220 | 221 | if certificateChainEvaluationPassed { 222 | outerLoop: for serverPublicKey in ServerTrustPolicy.publicKeys(for: serverTrust) as [AnyObject] { 223 | for pinnedPublicKey in pinnedPublicKeys as [AnyObject] { 224 | if serverPublicKey.isEqual(pinnedPublicKey) { 225 | serverTrustIsValid = true 226 | break outerLoop 227 | } 228 | } 229 | } 230 | } 231 | case .disableEvaluation: 232 | serverTrustIsValid = true 233 | case let .customEvaluation(closure): 234 | serverTrustIsValid = closure(serverTrust, host) 235 | } 236 | 237 | return serverTrustIsValid 238 | } 239 | 240 | // MARK: - Private - Trust Validation 241 | 242 | private func trustIsValid(_ trust: SecTrust) -> Bool { 243 | var isValid = false 244 | 245 | var result = SecTrustResultType.invalid 246 | let status = SecTrustEvaluate(trust, &result) 247 | 248 | if status == errSecSuccess { 249 | let unspecified = SecTrustResultType.unspecified 250 | let proceed = SecTrustResultType.proceed 251 | 252 | 253 | isValid = result == unspecified || result == proceed 254 | } 255 | 256 | return isValid 257 | } 258 | 259 | // MARK: - Private - Certificate Data 260 | 261 | private func certificateData(for trust: SecTrust) -> [Data] { 262 | var certificates: [SecCertificate] = [] 263 | 264 | for index in 0.. [Data] { 274 | return certificates.map { SecCertificateCopyData($0) as Data } 275 | } 276 | 277 | // MARK: - Private - Public Key Extraction 278 | 279 | private static func publicKeys(for trust: SecTrust) -> [SecKey] { 280 | var publicKeys: [SecKey] = [] 281 | 282 | for index in 0.. SecKey? { 295 | var publicKey: SecKey? 296 | 297 | let policy = SecPolicyCreateBasicX509() 298 | var trust: SecTrust? 299 | let trustCreationStatus = SecTrustCreateWithCertificates(certificate, policy, &trust) 300 | 301 | if let trust = trust, trustCreationStatus == errSecSuccess { 302 | publicKey = SecTrustCopyPublicKey(trust) 303 | } 304 | 305 | return publicKey 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/TaskDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaskDelegate.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as 28 | /// executing all operations attached to the serial operation queue upon task completion. 29 | open class TaskDelegate: NSObject { 30 | 31 | // MARK: Properties 32 | 33 | /// The serial operation queue used to execute all operations after the task completes. 34 | open let queue: OperationQueue 35 | 36 | /// The data returned by the server. 37 | public var data: Data? { return nil } 38 | 39 | /// The error generated throughout the lifecyle of the task. 40 | public var error: Error? 41 | 42 | var task: URLSessionTask? { 43 | didSet { reset() } 44 | } 45 | 46 | var initialResponseTime: CFAbsoluteTime? 47 | var credential: URLCredential? 48 | var metrics: AnyObject? // URLSessionTaskMetrics 49 | 50 | // MARK: Lifecycle 51 | 52 | init(task: URLSessionTask?) { 53 | self.task = task 54 | 55 | self.queue = { 56 | let operationQueue = OperationQueue() 57 | 58 | operationQueue.maxConcurrentOperationCount = 1 59 | operationQueue.isSuspended = true 60 | operationQueue.qualityOfService = .utility 61 | 62 | return operationQueue 63 | }() 64 | } 65 | 66 | func reset() { 67 | error = nil 68 | initialResponseTime = nil 69 | } 70 | 71 | // MARK: URLSessionTaskDelegate 72 | 73 | var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)? 74 | var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? 75 | var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)? 76 | var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)? 77 | 78 | @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:) 79 | func urlSession( 80 | _ session: URLSession, 81 | task: URLSessionTask, 82 | willPerformHTTPRedirection response: HTTPURLResponse, 83 | newRequest request: URLRequest, 84 | completionHandler: @escaping (URLRequest?) -> Void) 85 | { 86 | var redirectRequest: URLRequest? = request 87 | 88 | if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection { 89 | redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request) 90 | } 91 | 92 | completionHandler(redirectRequest) 93 | } 94 | 95 | @objc(URLSession:task:didReceiveChallenge:completionHandler:) 96 | func urlSession( 97 | _ session: URLSession, 98 | task: URLSessionTask, 99 | didReceive challenge: URLAuthenticationChallenge, 100 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 101 | { 102 | var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling 103 | var credential: URLCredential? 104 | 105 | if let taskDidReceiveChallenge = taskDidReceiveChallenge { 106 | (disposition, credential) = taskDidReceiveChallenge(session, task, challenge) 107 | } else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust { 108 | let host = challenge.protectionSpace.host 109 | 110 | if 111 | let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host), 112 | let serverTrust = challenge.protectionSpace.serverTrust 113 | { 114 | if serverTrustPolicy.evaluate(serverTrust, forHost: host) { 115 | disposition = .useCredential 116 | credential = URLCredential(trust: serverTrust) 117 | } else { 118 | disposition = .cancelAuthenticationChallenge 119 | } 120 | } 121 | } else { 122 | if challenge.previousFailureCount > 0 { 123 | disposition = .rejectProtectionSpace 124 | } else { 125 | credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace) 126 | 127 | if credential != nil { 128 | disposition = .useCredential 129 | } 130 | } 131 | } 132 | 133 | completionHandler(disposition, credential) 134 | } 135 | 136 | @objc(URLSession:task:needNewBodyStream:) 137 | func urlSession( 138 | _ session: URLSession, 139 | task: URLSessionTask, 140 | needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) 141 | { 142 | var bodyStream: InputStream? 143 | 144 | if let taskNeedNewBodyStream = taskNeedNewBodyStream { 145 | bodyStream = taskNeedNewBodyStream(session, task) 146 | } 147 | 148 | completionHandler(bodyStream) 149 | } 150 | 151 | @objc(URLSession:task:didCompleteWithError:) 152 | func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 153 | if let taskDidCompleteWithError = taskDidCompleteWithError { 154 | taskDidCompleteWithError(session, task, error) 155 | } else { 156 | if let error = error { 157 | if self.error == nil { self.error = error } 158 | 159 | if 160 | let downloadDelegate = self as? DownloadTaskDelegate, 161 | let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data 162 | { 163 | downloadDelegate.resumeData = resumeData 164 | } 165 | } 166 | 167 | queue.isSuspended = false 168 | } 169 | } 170 | } 171 | 172 | // MARK: - 173 | 174 | class DataTaskDelegate: TaskDelegate, URLSessionDataDelegate { 175 | 176 | // MARK: Properties 177 | 178 | var dataTask: URLSessionDataTask { return task as! URLSessionDataTask } 179 | 180 | override var data: Data? { 181 | if dataStream != nil { 182 | return nil 183 | } else { 184 | return mutableData 185 | } 186 | } 187 | 188 | var progress: Progress 189 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 190 | 191 | var dataStream: ((_ data: Data) -> Void)? 192 | 193 | private var totalBytesReceived: Int64 = 0 194 | private var mutableData: Data 195 | 196 | private var expectedContentLength: Int64? 197 | 198 | // MARK: Lifecycle 199 | 200 | override init(task: URLSessionTask?) { 201 | mutableData = Data() 202 | progress = Progress(totalUnitCount: 0) 203 | 204 | super.init(task: task) 205 | } 206 | 207 | override func reset() { 208 | super.reset() 209 | 210 | progress = Progress(totalUnitCount: 0) 211 | totalBytesReceived = 0 212 | mutableData = Data() 213 | expectedContentLength = nil 214 | } 215 | 216 | // MARK: URLSessionDataDelegate 217 | 218 | var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)? 219 | var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)? 220 | var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)? 221 | var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)? 222 | 223 | func urlSession( 224 | _ session: URLSession, 225 | dataTask: URLSessionDataTask, 226 | didReceive response: URLResponse, 227 | completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) 228 | { 229 | var disposition: URLSession.ResponseDisposition = .allow 230 | 231 | expectedContentLength = response.expectedContentLength 232 | 233 | if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse { 234 | disposition = dataTaskDidReceiveResponse(session, dataTask, response) 235 | } 236 | 237 | completionHandler(disposition) 238 | } 239 | 240 | func urlSession( 241 | _ session: URLSession, 242 | dataTask: URLSessionDataTask, 243 | didBecome downloadTask: URLSessionDownloadTask) 244 | { 245 | dataTaskDidBecomeDownloadTask?(session, dataTask, downloadTask) 246 | } 247 | 248 | func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 249 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 250 | 251 | if let dataTaskDidReceiveData = dataTaskDidReceiveData { 252 | dataTaskDidReceiveData(session, dataTask, data) 253 | } else { 254 | if let dataStream = dataStream { 255 | dataStream(data) 256 | } else { 257 | mutableData.append(data) 258 | } 259 | 260 | let bytesReceived = Int64(data.count) 261 | totalBytesReceived += bytesReceived 262 | let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown 263 | 264 | progress.totalUnitCount = totalBytesExpected 265 | progress.completedUnitCount = totalBytesReceived 266 | 267 | if let progressHandler = progressHandler { 268 | progressHandler.queue.async { progressHandler.closure(self.progress) } 269 | } 270 | } 271 | } 272 | 273 | func urlSession( 274 | _ session: URLSession, 275 | dataTask: URLSessionDataTask, 276 | willCacheResponse proposedResponse: CachedURLResponse, 277 | completionHandler: @escaping (CachedURLResponse?) -> Void) 278 | { 279 | var cachedResponse: CachedURLResponse? = proposedResponse 280 | 281 | if let dataTaskWillCacheResponse = dataTaskWillCacheResponse { 282 | cachedResponse = dataTaskWillCacheResponse(session, dataTask, proposedResponse) 283 | } 284 | 285 | completionHandler(cachedResponse) 286 | } 287 | } 288 | 289 | // MARK: - 290 | 291 | class DownloadTaskDelegate: TaskDelegate, URLSessionDownloadDelegate { 292 | 293 | // MARK: Properties 294 | 295 | var downloadTask: URLSessionDownloadTask { return task as! URLSessionDownloadTask } 296 | 297 | var progress: Progress 298 | var progressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 299 | 300 | var resumeData: Data? 301 | override var data: Data? { return resumeData } 302 | 303 | var destination: DownloadRequest.DownloadFileDestination? 304 | 305 | var temporaryURL: URL? 306 | var destinationURL: URL? 307 | 308 | var fileURL: URL? { return destination != nil ? destinationURL : temporaryURL } 309 | 310 | // MARK: Lifecycle 311 | 312 | override init(task: URLSessionTask?) { 313 | progress = Progress(totalUnitCount: 0) 314 | super.init(task: task) 315 | } 316 | 317 | override func reset() { 318 | super.reset() 319 | 320 | progress = Progress(totalUnitCount: 0) 321 | resumeData = nil 322 | } 323 | 324 | // MARK: URLSessionDownloadDelegate 325 | 326 | var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)? 327 | var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? 328 | var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)? 329 | 330 | func urlSession( 331 | _ session: URLSession, 332 | downloadTask: URLSessionDownloadTask, 333 | didFinishDownloadingTo location: URL) 334 | { 335 | temporaryURL = location 336 | 337 | guard 338 | let destination = destination, 339 | let response = downloadTask.response as? HTTPURLResponse 340 | else { return } 341 | 342 | let result = destination(location, response) 343 | let destinationURL = result.destinationURL 344 | let options = result.options 345 | 346 | self.destinationURL = destinationURL 347 | 348 | do { 349 | if options.contains(.removePreviousFile), FileManager.default.fileExists(atPath: destinationURL.path) { 350 | try FileManager.default.removeItem(at: destinationURL) 351 | } 352 | 353 | if options.contains(.createIntermediateDirectories) { 354 | let directory = destinationURL.deletingLastPathComponent() 355 | try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) 356 | } 357 | 358 | try FileManager.default.moveItem(at: location, to: destinationURL) 359 | } catch { 360 | self.error = error 361 | } 362 | } 363 | 364 | func urlSession( 365 | _ session: URLSession, 366 | downloadTask: URLSessionDownloadTask, 367 | didWriteData bytesWritten: Int64, 368 | totalBytesWritten: Int64, 369 | totalBytesExpectedToWrite: Int64) 370 | { 371 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 372 | 373 | if let downloadTaskDidWriteData = downloadTaskDidWriteData { 374 | downloadTaskDidWriteData( 375 | session, 376 | downloadTask, 377 | bytesWritten, 378 | totalBytesWritten, 379 | totalBytesExpectedToWrite 380 | ) 381 | } else { 382 | progress.totalUnitCount = totalBytesExpectedToWrite 383 | progress.completedUnitCount = totalBytesWritten 384 | 385 | if let progressHandler = progressHandler { 386 | progressHandler.queue.async { progressHandler.closure(self.progress) } 387 | } 388 | } 389 | } 390 | 391 | func urlSession( 392 | _ session: URLSession, 393 | downloadTask: URLSessionDownloadTask, 394 | didResumeAtOffset fileOffset: Int64, 395 | expectedTotalBytes: Int64) 396 | { 397 | if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset { 398 | downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes) 399 | } else { 400 | progress.totalUnitCount = expectedTotalBytes 401 | progress.completedUnitCount = fileOffset 402 | } 403 | } 404 | } 405 | 406 | // MARK: - 407 | 408 | class UploadTaskDelegate: DataTaskDelegate { 409 | 410 | // MARK: Properties 411 | 412 | var uploadTask: URLSessionUploadTask { return task as! URLSessionUploadTask } 413 | 414 | var uploadProgress: Progress 415 | var uploadProgressHandler: (closure: Request.ProgressHandler, queue: DispatchQueue)? 416 | 417 | // MARK: Lifecycle 418 | 419 | override init(task: URLSessionTask?) { 420 | uploadProgress = Progress(totalUnitCount: 0) 421 | super.init(task: task) 422 | } 423 | 424 | override func reset() { 425 | super.reset() 426 | uploadProgress = Progress(totalUnitCount: 0) 427 | } 428 | 429 | // MARK: URLSessionTaskDelegate 430 | 431 | var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)? 432 | 433 | func URLSession( 434 | _ session: URLSession, 435 | task: URLSessionTask, 436 | didSendBodyData bytesSent: Int64, 437 | totalBytesSent: Int64, 438 | totalBytesExpectedToSend: Int64) 439 | { 440 | if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() } 441 | 442 | if let taskDidSendBodyData = taskDidSendBodyData { 443 | taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend) 444 | } else { 445 | uploadProgress.totalUnitCount = totalBytesExpectedToSend 446 | uploadProgress.completedUnitCount = totalBytesSent 447 | 448 | if let uploadProgressHandler = uploadProgressHandler { 449 | uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) } 450 | } 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Timeline.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Timeline.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`. 28 | public struct Timeline { 29 | /// The time the request was initialized. 30 | public let requestStartTime: CFAbsoluteTime 31 | 32 | /// The time the first bytes were received from or sent to the server. 33 | public let initialResponseTime: CFAbsoluteTime 34 | 35 | /// The time when the request was completed. 36 | public let requestCompletedTime: CFAbsoluteTime 37 | 38 | /// The time when the response serialization was completed. 39 | public let serializationCompletedTime: CFAbsoluteTime 40 | 41 | /// The time interval in seconds from the time the request started to the initial response from the server. 42 | public let latency: TimeInterval 43 | 44 | /// The time interval in seconds from the time the request started to the time the request completed. 45 | public let requestDuration: TimeInterval 46 | 47 | /// The time interval in seconds from the time the request completed to the time response serialization completed. 48 | public let serializationDuration: TimeInterval 49 | 50 | /// The time interval in seconds from the time the request started to the time response serialization completed. 51 | public let totalDuration: TimeInterval 52 | 53 | /// Creates a new `Timeline` instance with the specified request times. 54 | /// 55 | /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`. 56 | /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server. 57 | /// Defaults to `0.0`. 58 | /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`. 59 | /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults 60 | /// to `0.0`. 61 | /// 62 | /// - returns: The new `Timeline` instance. 63 | public init( 64 | requestStartTime: CFAbsoluteTime = 0.0, 65 | initialResponseTime: CFAbsoluteTime = 0.0, 66 | requestCompletedTime: CFAbsoluteTime = 0.0, 67 | serializationCompletedTime: CFAbsoluteTime = 0.0) 68 | { 69 | self.requestStartTime = requestStartTime 70 | self.initialResponseTime = initialResponseTime 71 | self.requestCompletedTime = requestCompletedTime 72 | self.serializationCompletedTime = serializationCompletedTime 73 | 74 | self.latency = initialResponseTime - requestStartTime 75 | self.requestDuration = requestCompletedTime - requestStartTime 76 | self.serializationDuration = serializationCompletedTime - requestCompletedTime 77 | self.totalDuration = serializationCompletedTime - requestStartTime 78 | } 79 | } 80 | 81 | // MARK: - CustomStringConvertible 82 | 83 | extension Timeline: CustomStringConvertible { 84 | /// The textual representation used when written to an output stream, which includes the latency, the request 85 | /// duration and the total duration. 86 | public var description: String { 87 | let latency = String(format: "%.3f", self.latency) 88 | let requestDuration = String(format: "%.3f", self.requestDuration) 89 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 90 | let totalDuration = String(format: "%.3f", self.totalDuration) 91 | 92 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 93 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 94 | let timings = [ 95 | "\"Latency\": " + latency + " secs", 96 | "\"Request Duration\": " + requestDuration + " secs", 97 | "\"Serialization Duration\": " + serializationDuration + " secs", 98 | "\"Total Duration\": " + totalDuration + " secs" 99 | ] 100 | 101 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 102 | } 103 | } 104 | 105 | // MARK: - CustomDebugStringConvertible 106 | 107 | extension Timeline: CustomDebugStringConvertible { 108 | /// The textual representation used when written to an output stream, which includes the request start time, the 109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request 110 | /// duration and the total duration. 111 | public var debugDescription: String { 112 | let requestStartTime = String(format: "%.3f", self.requestStartTime) 113 | let initialResponseTime = String(format: "%.3f", self.initialResponseTime) 114 | let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime) 115 | let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime) 116 | let latency = String(format: "%.3f", self.latency) 117 | let requestDuration = String(format: "%.3f", self.requestDuration) 118 | let serializationDuration = String(format: "%.3f", self.serializationDuration) 119 | let totalDuration = String(format: "%.3f", self.totalDuration) 120 | 121 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is 122 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1. 123 | let timings = [ 124 | "\"Request Start Time\": " + requestStartTime, 125 | "\"Initial Response Time\": " + initialResponseTime, 126 | "\"Request Completed Time\": " + requestCompletedTime, 127 | "\"Serialization Completed Time\": " + serializationCompletedTime, 128 | "\"Latency\": " + latency + " secs", 129 | "\"Request Duration\": " + requestDuration + " secs", 130 | "\"Serialization Duration\": " + serializationDuration + " secs", 131 | "\"Total Duration\": " + totalDuration + " secs" 132 | ] 133 | 134 | return "Timeline: { " + timings.joined(separator: ", ") + " }" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Validation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validation.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Request { 28 | 29 | // MARK: Helper Types 30 | 31 | fileprivate typealias ErrorReason = AFError.ResponseValidationFailureReason 32 | 33 | /// Used to represent whether validation was successful or encountered an error resulting in a failure. 34 | /// 35 | /// - success: The validation was successful. 36 | /// - failure: The validation failed encountering the provided error. 37 | public enum ValidationResult { 38 | case success 39 | case failure(Error) 40 | } 41 | 42 | fileprivate struct MIMEType { 43 | let type: String 44 | let subtype: String 45 | 46 | var isWildcard: Bool { return type == "*" && subtype == "*" } 47 | 48 | init?(_ string: String) { 49 | let components: [String] = { 50 | let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines) 51 | let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex) 52 | return split.components(separatedBy: "/") 53 | }() 54 | 55 | if let type = components.first, let subtype = components.last { 56 | self.type = type 57 | self.subtype = subtype 58 | } else { 59 | return nil 60 | } 61 | } 62 | 63 | func matches(_ mime: MIMEType) -> Bool { 64 | switch (type, subtype) { 65 | case (mime.type, mime.subtype), (mime.type, "*"), ("*", mime.subtype), ("*", "*"): 66 | return true 67 | default: 68 | return false 69 | } 70 | } 71 | } 72 | 73 | // MARK: Properties 74 | 75 | fileprivate var acceptableStatusCodes: [Int] { return Array(200..<300) } 76 | 77 | fileprivate var acceptableContentTypes: [String] { 78 | if let accept = request?.value(forHTTPHeaderField: "Accept") { 79 | return accept.components(separatedBy: ",") 80 | } 81 | 82 | return ["*/*"] 83 | } 84 | 85 | // MARK: Status Code 86 | 87 | fileprivate func validate( 88 | statusCode acceptableStatusCodes: S, 89 | response: HTTPURLResponse) 90 | -> ValidationResult 91 | where S.Iterator.Element == Int 92 | { 93 | if acceptableStatusCodes.contains(response.statusCode) { 94 | return .success 95 | } else { 96 | let reason: ErrorReason = .unacceptableStatusCode(code: response.statusCode) 97 | return .failure(AFError.responseValidationFailed(reason: reason)) 98 | } 99 | } 100 | 101 | // MARK: Content Type 102 | 103 | fileprivate func validate( 104 | contentType acceptableContentTypes: S, 105 | response: HTTPURLResponse, 106 | data: Data?) 107 | -> ValidationResult 108 | where S.Iterator.Element == String 109 | { 110 | guard let data = data, data.count > 0 else { return .success } 111 | 112 | guard 113 | let responseContentType = response.mimeType, 114 | let responseMIMEType = MIMEType(responseContentType) 115 | else { 116 | for contentType in acceptableContentTypes { 117 | if let mimeType = MIMEType(contentType), mimeType.isWildcard { 118 | return .success 119 | } 120 | } 121 | 122 | let error: AFError = { 123 | let reason: ErrorReason = .missingContentType(acceptableContentTypes: Array(acceptableContentTypes)) 124 | return AFError.responseValidationFailed(reason: reason) 125 | }() 126 | 127 | return .failure(error) 128 | } 129 | 130 | for contentType in acceptableContentTypes { 131 | if let acceptableMIMEType = MIMEType(contentType), acceptableMIMEType.matches(responseMIMEType) { 132 | return .success 133 | } 134 | } 135 | 136 | let error: AFError = { 137 | let reason: ErrorReason = .unacceptableContentType( 138 | acceptableContentTypes: Array(acceptableContentTypes), 139 | responseContentType: responseContentType 140 | ) 141 | 142 | return AFError.responseValidationFailed(reason: reason) 143 | }() 144 | 145 | return .failure(error) 146 | } 147 | } 148 | 149 | // MARK: - 150 | 151 | extension DataRequest { 152 | /// A closure used to validate a request that takes a URL request, a URL response and data, and returns whether the 153 | /// request was valid. 154 | public typealias Validation = (URLRequest?, HTTPURLResponse, Data?) -> ValidationResult 155 | 156 | /// Validates the request, using the specified closure. 157 | /// 158 | /// If validation fails, subsequent calls to response handlers will have an associated error. 159 | /// 160 | /// - parameter validation: A closure to validate the request. 161 | /// 162 | /// - returns: The request. 163 | @discardableResult 164 | public func validate(_ validation: @escaping Validation) -> Self { 165 | let validationExecution: () -> Void = { [unowned self] in 166 | if 167 | let response = self.response, 168 | self.delegate.error == nil, 169 | case let .failure(error) = validation(self.request, response, self.delegate.data) 170 | { 171 | self.delegate.error = error 172 | } 173 | } 174 | 175 | validations.append(validationExecution) 176 | 177 | return self 178 | } 179 | 180 | /// Validates that the response has a status code in the specified sequence. 181 | /// 182 | /// If validation fails, subsequent calls to response handlers will have an associated error. 183 | /// 184 | /// - parameter range: The range of acceptable status codes. 185 | /// 186 | /// - returns: The request. 187 | @discardableResult 188 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 189 | return validate { [unowned self] _, response, _ in 190 | return self.validate(statusCode: acceptableStatusCodes, response: response) 191 | } 192 | } 193 | 194 | /// Validates that the response has a content type in the specified sequence. 195 | /// 196 | /// If validation fails, subsequent calls to response handlers will have an associated error. 197 | /// 198 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 199 | /// 200 | /// - returns: The request. 201 | @discardableResult 202 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 203 | return validate { [unowned self] _, response, data in 204 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 205 | } 206 | } 207 | 208 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 209 | /// type matches any specified in the Accept HTTP header field. 210 | /// 211 | /// If validation fails, subsequent calls to response handlers will have an associated error. 212 | /// 213 | /// - returns: The request. 214 | @discardableResult 215 | public func validate() -> Self { 216 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 217 | } 218 | } 219 | 220 | // MARK: - 221 | 222 | extension DownloadRequest { 223 | /// A closure used to validate a request that takes a URL request, a URL response, a temporary URL and a 224 | /// destination URL, and returns whether the request was valid. 225 | public typealias Validation = ( 226 | _ request: URLRequest?, 227 | _ response: HTTPURLResponse, 228 | _ temporaryURL: URL?, 229 | _ destinationURL: URL?) 230 | -> ValidationResult 231 | 232 | /// Validates the request, using the specified closure. 233 | /// 234 | /// If validation fails, subsequent calls to response handlers will have an associated error. 235 | /// 236 | /// - parameter validation: A closure to validate the request. 237 | /// 238 | /// - returns: The request. 239 | @discardableResult 240 | public func validate(_ validation: @escaping Validation) -> Self { 241 | let validationExecution: () -> Void = { [unowned self] in 242 | let request = self.request 243 | let temporaryURL = self.downloadDelegate.temporaryURL 244 | let destinationURL = self.downloadDelegate.destinationURL 245 | 246 | if 247 | let response = self.response, 248 | self.delegate.error == nil, 249 | case let .failure(error) = validation(request, response, temporaryURL, destinationURL) 250 | { 251 | self.delegate.error = error 252 | } 253 | } 254 | 255 | validations.append(validationExecution) 256 | 257 | return self 258 | } 259 | 260 | /// Validates that the response has a status code in the specified sequence. 261 | /// 262 | /// If validation fails, subsequent calls to response handlers will have an associated error. 263 | /// 264 | /// - parameter range: The range of acceptable status codes. 265 | /// 266 | /// - returns: The request. 267 | @discardableResult 268 | public func validate(statusCode acceptableStatusCodes: S) -> Self where S.Iterator.Element == Int { 269 | return validate { [unowned self] _, response, _, _ in 270 | return self.validate(statusCode: acceptableStatusCodes, response: response) 271 | } 272 | } 273 | 274 | /// Validates that the response has a content type in the specified sequence. 275 | /// 276 | /// If validation fails, subsequent calls to response handlers will have an associated error. 277 | /// 278 | /// - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 279 | /// 280 | /// - returns: The request. 281 | @discardableResult 282 | public func validate(contentType acceptableContentTypes: S) -> Self where S.Iterator.Element == String { 283 | return validate { [unowned self] _, response, _, _ in 284 | let fileURL = self.downloadDelegate.fileURL 285 | 286 | guard let validFileURL = fileURL else { 287 | return .failure(AFError.responseValidationFailed(reason: .dataFileNil)) 288 | } 289 | 290 | do { 291 | let data = try Data(contentsOf: validFileURL) 292 | return self.validate(contentType: acceptableContentTypes, response: response, data: data) 293 | } catch { 294 | return .failure(AFError.responseValidationFailed(reason: .dataFileReadFailed(at: validFileURL))) 295 | } 296 | } 297 | } 298 | 299 | /// Validates that the response has a status code in the default acceptable range of 200...299, and that the content 300 | /// type matches any specified in the Accept HTTP header field. 301 | /// 302 | /// If validation fails, subsequent calls to response handlers will have an associated error. 303 | /// 304 | /// - returns: The request. 305 | @discardableResult 306 | public func validate() -> Self { 307 | return validate(statusCode: self.acceptableStatusCodes).validate(contentType: self.acceptableContentTypes) 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.4.0) 3 | - Result (3.2.1) 4 | - UIView+Shake (1.3.0) 5 | 6 | DEPENDENCIES: 7 | - Alamofire 8 | - Result 9 | - UIView+Shake 10 | 11 | SPEC CHECKSUMS: 12 | Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d 13 | Result: 2453a22e5c5b11c0c3a478736e82cd02f763b781 14 | UIView+Shake: 3ccbf966d40a76ea2cdbdd48d8219d322d5df5f5 15 | 16 | PODFILE CHECKSUM: d9379960b7e95d6c90f06b0a2ab108f19148b64a 17 | 18 | COCOAPODS: 1.3.0 19 | -------------------------------------------------------------------------------- /Pods/Result/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rob Rix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Pods/Result/README.md: -------------------------------------------------------------------------------- 1 | # Result 2 | 3 | [![Build Status](https://travis-ci.org/antitypical/Result.svg?branch=master)](https://travis-ci.org/antitypical/Result) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![CocoaPods](https://img.shields.io/cocoapods/v/Result.svg)](https://cocoapods.org/) 6 | [![Reference Status](https://www.versioneye.com/objective-c/result/reference_badge.svg?style=flat)](https://www.versioneye.com/objective-c/result/references) 7 | 8 | This is a Swift µframework providing `Result`. 9 | 10 | `Result` values are either successful (wrapping `Value`) or failed (wrapping `Error`). This is similar to Swift’s native `Optional` type: `success` is like `some`, and `failure` is like `none` except with an associated `Error` value. The addition of an associated `Error` allows errors to be passed along for logging or displaying to the user. 11 | 12 | Using this µframework instead of rolling your own `Result` type allows you to easily interface with other frameworks that also use `Result`. 13 | 14 | ## Use 15 | 16 | Use `Result` whenever an operation has the possibility of failure. Consider the following example of a function that tries to extract a `String` for a given key from a JSON `Dictionary`. 17 | 18 | ```swift 19 | typealias JSONObject = [String: Any] 20 | 21 | enum JSONError: Error { 22 | case noSuchKey(String) 23 | case typeMismatch 24 | } 25 | 26 | func stringForKey(json: JSONObject, key: String) -> Result { 27 | guard let value = json[key] else { 28 | return .failure(.noSuchKey(key)) 29 | } 30 | 31 | if let value = value as? String { 32 | return .success(value) 33 | } 34 | else { 35 | return .failure(.typeMismatch) 36 | } 37 | } 38 | ``` 39 | 40 | This function provides a more robust wrapper around the default subscripting provided by `Dictionary`. Rather than return `Any?`, it returns a `Result` that either contains the `String` value for the given key, or an `ErrorType` detailing what went wrong. 41 | 42 | One simple way to handle a `Result` is to deconstruct it using a `switch` statement. 43 | 44 | ```swift 45 | switch stringForKey(json, key: "email") { 46 | 47 | case let .success(email): 48 | print("The email is \(email)") 49 | 50 | case let .failure(.noSuchKey(key)): 51 | print("\(key) is not a valid key") 52 | 53 | case .failure(.typeMismatch): 54 | print("Didn't have the right type") 55 | } 56 | ``` 57 | 58 | Using a `switch` statement allows powerful pattern matching, and ensures all possible results are covered. Swift 2.0 offers new ways to deconstruct enums like the `if-case` statement, but be wary as such methods do not ensure errors are handled. 59 | 60 | Other methods available for processing `Result` are detailed in the [API documentation](http://cocoadocs.org/docsets/Result/). 61 | 62 | ## Result vs. Throws 63 | 64 | Swift 2.0 introduces error handling via throwing and catching `Error`. `Result` accomplishes the same goal by encapsulating the result instead of hijacking control flow. The `Result` abstraction enables powerful functionality such as `map` and `flatMap`, making `Result` more composable than `throw`. 65 | 66 | Since dealing with APIs that throw is common, you can convert such functions into a `Result` by using the `materialize` method. Conversely, a `Result` can be used to throw an error by calling `dematerialize`. 67 | 68 | ## Higher Order Functions 69 | 70 | `map` and `flatMap` operate the same as `Optional.map` and `Optional.flatMap` except they apply to `Result`. 71 | 72 | `map` transforms a `Result` into a `Result` of a new type. It does this by taking a function that transforms the `Value` type into a new value. This transformation is only applied in the case of a `success`. In the case of a `failure`, the associated error is re-wrapped in the new `Result`. 73 | 74 | ```swift 75 | // transforms a Result to a Result 76 | let idResult = intForKey(json, key:"id").map { id in String(id) } 77 | ``` 78 | 79 | Here, the final result is either the id as a `String`, or carries over the `failure` from the previous result. 80 | 81 | `flatMap` is similar to `map` in that in transforms the `Result` into another `Result`. However, the function passed into `flatMap` must return a `Result`. 82 | 83 | An in depth discussion of `map` and `flatMap` is beyond the scope of this documentation. If you would like a deeper understanding, read about functors and monads. This article is a good place to [start](http://www.javiersoto.me/post/106875422394). 84 | 85 | ## Integration 86 | 87 | ### Carthage 88 | 89 | 1. Add this repository as a submodule and/or [add it to your Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile) if you’re using [carthage](https://github.com/Carthage/Carthage/) to manage your dependencies. 90 | 2. Drag `Result.xcodeproj` into your project or workspace. 91 | 3. Link your target against `Result.framework`. 92 | 4. Application targets should ensure that the framework gets copied into their application bundle. (Framework targets should instead require the application linking them to include Result.) 93 | 94 | ### Cocoapods 95 | 96 | ```ruby 97 | pod 'Result', '~> 3.0.0' 98 | ``` 99 | 100 | ### Swift Package Manager 101 | 102 | ```swift 103 | import PackageDescription 104 | 105 | let package = Package( 106 | name: "MyProject", 107 | targets: [], 108 | dependencies: [ 109 | .Package(url: "https://github.com/antitypical/Result.git", 110 | majorVersion: 3) 111 | ] 112 | ) 113 | ``` 114 | -------------------------------------------------------------------------------- /Pods/Result/Result/Result.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Rob Rix. All rights reserved. 2 | 3 | /// An enum representing either a failure with an explanatory error, or a success with a result value. 4 | public enum Result: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible { 5 | case success(T) 6 | case failure(Error) 7 | 8 | // MARK: Constructors 9 | 10 | /// Constructs a success wrapping a `value`. 11 | public init(value: T) { 12 | self = .success(value) 13 | } 14 | 15 | /// Constructs a failure wrapping an `error`. 16 | public init(error: Error) { 17 | self = .failure(error) 18 | } 19 | 20 | /// Constructs a result from an `Optional`, failing with `Error` if `nil`. 21 | public init(_ value: T?, failWith: @autoclosure () -> Error) { 22 | self = value.map(Result.success) ?? .failure(failWith()) 23 | } 24 | 25 | /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. 26 | public init(_ f: @autoclosure () throws -> T) { 27 | self.init(attempt: f) 28 | } 29 | 30 | /// Constructs a result from a function that uses `throw`, failing with `Error` if throws. 31 | public init(attempt f: () throws -> T) { 32 | do { 33 | self = .success(try f()) 34 | } catch { 35 | self = .failure(error as! Error) 36 | } 37 | } 38 | 39 | // MARK: Deconstruction 40 | 41 | /// Returns the value from `success` Results or `throw`s the error. 42 | public func dematerialize() throws -> T { 43 | switch self { 44 | case let .success(value): 45 | return value 46 | case let .failure(error): 47 | throw error 48 | } 49 | } 50 | 51 | /// Case analysis for Result. 52 | /// 53 | /// Returns the value produced by applying `ifFailure` to `failure` Results, or `ifSuccess` to `success` Results. 54 | public func analysis(ifSuccess: (T) -> Result, ifFailure: (Error) -> Result) -> Result { 55 | switch self { 56 | case let .success(value): 57 | return ifSuccess(value) 58 | case let .failure(value): 59 | return ifFailure(value) 60 | } 61 | } 62 | 63 | // MARK: Errors 64 | 65 | /// The domain for errors constructed by Result. 66 | public static var errorDomain: String { return "com.antitypical.Result" } 67 | 68 | /// The userInfo key for source functions in errors constructed by Result. 69 | public static var functionKey: String { return "\(errorDomain).function" } 70 | 71 | /// The userInfo key for source file paths in errors constructed by Result. 72 | public static var fileKey: String { return "\(errorDomain).file" } 73 | 74 | /// The userInfo key for source file line numbers in errors constructed by Result. 75 | public static var lineKey: String { return "\(errorDomain).line" } 76 | 77 | /// Constructs an error. 78 | public static func error(_ message: String? = nil, function: String = #function, file: String = #file, line: Int = #line) -> NSError { 79 | var userInfo: [String: Any] = [ 80 | functionKey: function, 81 | fileKey: file, 82 | lineKey: line, 83 | ] 84 | 85 | if let message = message { 86 | userInfo[NSLocalizedDescriptionKey] = message 87 | } 88 | 89 | return NSError(domain: errorDomain, code: 0, userInfo: userInfo) 90 | } 91 | 92 | 93 | // MARK: CustomStringConvertible 94 | 95 | public var description: String { 96 | return analysis( 97 | ifSuccess: { ".success(\($0))" }, 98 | ifFailure: { ".failure(\($0))" }) 99 | } 100 | 101 | 102 | // MARK: CustomDebugStringConvertible 103 | 104 | public var debugDescription: String { 105 | return description 106 | } 107 | } 108 | 109 | // MARK: - Derive result from failable closure 110 | 111 | public func materialize(_ f: () throws -> T) -> Result { 112 | return materialize(try f()) 113 | } 114 | 115 | public func materialize(_ f: @autoclosure () throws -> T) -> Result { 116 | do { 117 | return .success(try f()) 118 | } catch { 119 | return .failure(AnyError(error)) 120 | } 121 | } 122 | 123 | @available(*, deprecated, message: "Use the overload which returns `Result` instead") 124 | public func materialize(_ f: () throws -> T) -> Result { 125 | return materialize(try f()) 126 | } 127 | 128 | @available(*, deprecated, message: "Use the overload which returns `Result` instead") 129 | public func materialize(_ f: @autoclosure () throws -> T) -> Result { 130 | do { 131 | return .success(try f()) 132 | } catch { 133 | // This isn't great, but it lets us maintain compatibility until this deprecated 134 | // method can be removed. 135 | #if _runtime(_ObjC) 136 | return .failure(error as NSError) 137 | #else 138 | // https://github.com/apple/swift-corelibs-foundation/blob/swift-3.0.2-RELEASE/Foundation/NSError.swift#L314 139 | let userInfo = _swift_Foundation_getErrorDefaultUserInfo(error) as? [String: Any] 140 | let nsError = NSError(domain: error._domain, code: error._code, userInfo: userInfo) 141 | return .failure(nsError) 142 | #endif 143 | } 144 | } 145 | 146 | // MARK: - Cocoa API conveniences 147 | 148 | #if !os(Linux) 149 | 150 | /// Constructs a `Result` with the result of calling `try` with an error pointer. 151 | /// 152 | /// This is convenient for wrapping Cocoa API which returns an object or `nil` + an error, by reference. e.g.: 153 | /// 154 | /// Result.try { NSData(contentsOfURL: URL, options: .dataReadingMapped, error: $0) } 155 | public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> T?) -> Result { 156 | var error: NSError? 157 | return `try`(&error).map(Result.success) ?? .failure(error ?? Result.error(function: function, file: file, line: line)) 158 | } 159 | 160 | /// Constructs a `Result` with the result of calling `try` with an error pointer. 161 | /// 162 | /// This is convenient for wrapping Cocoa API which returns a `Bool` + an error, by reference. e.g.: 163 | /// 164 | /// Result.try { NSFileManager.defaultManager().removeItemAtURL(URL, error: $0) } 165 | public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> Bool) -> Result<(), NSError> { 166 | var error: NSError? 167 | return `try`(&error) ? 168 | .success(()) 169 | : .failure(error ?? Result<(), NSError>.error(function: function, file: file, line: line)) 170 | } 171 | 172 | #endif 173 | 174 | // MARK: - ErrorProtocolConvertible conformance 175 | 176 | extension NSError: ErrorProtocolConvertible { 177 | public static func error(from error: Swift.Error) -> Self { 178 | func cast(_ error: Swift.Error) -> T { 179 | return error as! T 180 | } 181 | 182 | return cast(error) 183 | } 184 | } 185 | 186 | // MARK: - Errors 187 | 188 | /// An “error” that is impossible to construct. 189 | /// 190 | /// This can be used to describe `Result`s where failures will never 191 | /// be generated. For example, `Result` describes a result that 192 | /// contains an `Int`eger and is guaranteed never to be a `failure`. 193 | public enum NoError: Swift.Error, Equatable { 194 | public static func ==(lhs: NoError, rhs: NoError) -> Bool { 195 | return true 196 | } 197 | } 198 | 199 | /// A type-erased error which wraps an arbitrary error instance. This should be 200 | /// useful for generic contexts. 201 | public struct AnyError: Swift.Error { 202 | /// The underlying error. 203 | public let error: Swift.Error 204 | 205 | public init(_ error: Swift.Error) { 206 | if let anyError = error as? AnyError { 207 | self = anyError 208 | } else { 209 | self.error = error 210 | } 211 | } 212 | } 213 | 214 | extension AnyError: ErrorProtocolConvertible { 215 | public static func error(from error: Error) -> AnyError { 216 | return AnyError(error) 217 | } 218 | } 219 | 220 | extension AnyError: CustomStringConvertible { 221 | public var description: String { 222 | return String(describing: error) 223 | } 224 | } 225 | 226 | // There appears to be a bug in Foundation on Linux which prevents this from working: 227 | // https://bugs.swift.org/browse/SR-3565 228 | // Don't forget to comment the tests back in when removing this check when it's fixed! 229 | #if !os(Linux) 230 | 231 | extension AnyError: LocalizedError { 232 | public var errorDescription: String? { 233 | return error.localizedDescription 234 | } 235 | 236 | public var failureReason: String? { 237 | return (error as? LocalizedError)?.failureReason 238 | } 239 | 240 | public var helpAnchor: String? { 241 | return (error as? LocalizedError)?.helpAnchor 242 | } 243 | 244 | public var recoverySuggestion: String? { 245 | return (error as? LocalizedError)?.recoverySuggestion 246 | } 247 | } 248 | 249 | #endif 250 | 251 | // MARK: - migration support 252 | extension Result { 253 | @available(*, unavailable, renamed: "success") 254 | public static func Success(_: T) -> Result { 255 | fatalError() 256 | } 257 | 258 | @available(*, unavailable, renamed: "failure") 259 | public static func Failure(_: Error) -> Result { 260 | fatalError() 261 | } 262 | } 263 | 264 | extension NSError { 265 | @available(*, unavailable, renamed: "error(from:)") 266 | public static func errorFromErrorType(_ error: Swift.Error) -> Self { 267 | fatalError() 268 | } 269 | } 270 | 271 | import Foundation 272 | -------------------------------------------------------------------------------- /Pods/Result/Result/ResultProtocol.swift: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Rob Rix. All rights reserved. 2 | 3 | /// A type that can represent either failure with an error or success with a result value. 4 | public protocol ResultProtocol { 5 | associatedtype Value 6 | associatedtype Error: Swift.Error 7 | 8 | /// Constructs a successful result wrapping a `value`. 9 | init(value: Value) 10 | 11 | /// Constructs a failed result wrapping an `error`. 12 | init(error: Error) 13 | 14 | /// Case analysis for ResultProtocol. 15 | /// 16 | /// Returns the value produced by appliying `ifFailure` to the error if self represents a failure, or `ifSuccess` to the result value if self represents a success. 17 | func analysis(ifSuccess: (Value) -> U, ifFailure: (Error) -> U) -> U 18 | 19 | /// Returns the value if self represents a success, `nil` otherwise. 20 | /// 21 | /// A default implementation is provided by a protocol extension. Conforming types may specialize it. 22 | var value: Value? { get } 23 | 24 | /// Returns the error if self represents a failure, `nil` otherwise. 25 | /// 26 | /// A default implementation is provided by a protocol extension. Conforming types may specialize it. 27 | var error: Error? { get } 28 | } 29 | 30 | public extension ResultProtocol { 31 | 32 | /// Returns the value if self represents a success, `nil` otherwise. 33 | public var value: Value? { 34 | return analysis(ifSuccess: { $0 }, ifFailure: { _ in nil }) 35 | } 36 | 37 | /// Returns the error if self represents a failure, `nil` otherwise. 38 | public var error: Error? { 39 | return analysis(ifSuccess: { _ in nil }, ifFailure: { $0 }) 40 | } 41 | 42 | /// Returns a new Result by mapping `Success`es’ values using `transform`, or re-wrapping `Failure`s’ errors. 43 | public func map(_ transform: (Value) -> U) -> Result { 44 | return flatMap { .success(transform($0)) } 45 | } 46 | 47 | /// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors. 48 | public func flatMap(_ transform: (Value) -> Result) -> Result { 49 | return analysis( 50 | ifSuccess: transform, 51 | ifFailure: Result.failure) 52 | } 53 | 54 | /// Returns a Result with a tuple of the receiver and `other` values if both 55 | /// are `Success`es, or re-wrapping the error of the earlier `Failure`. 56 | public func fanout(_ other: @autoclosure () -> R) -> Result<(Value, R.Value), Error> 57 | where Error == R.Error 58 | { 59 | return self.flatMap { left in other().map { right in (left, right) } } 60 | } 61 | 62 | /// Returns a new Result by mapping `Failure`'s values using `transform`, or re-wrapping `Success`es’ values. 63 | public func mapError(_ transform: (Error) -> Error2) -> Result { 64 | return flatMapError { .failure(transform($0)) } 65 | } 66 | 67 | /// Returns the result of applying `transform` to `Failure`’s errors, or re-wrapping `Success`es’ values. 68 | public func flatMapError(_ transform: (Error) -> Result) -> Result { 69 | return analysis( 70 | ifSuccess: Result.success, 71 | ifFailure: transform) 72 | } 73 | 74 | /// Returns a new Result by mapping `Success`es’ values using `success`, and by mapping `Failure`'s values using `failure`. 75 | public func bimap(success: (Value) -> U, failure: (Error) -> Error2) -> Result { 76 | return analysis( 77 | ifSuccess: { .success(success($0)) }, 78 | ifFailure: { .failure(failure($0)) } 79 | ) 80 | } 81 | } 82 | 83 | public extension ResultProtocol { 84 | 85 | // MARK: Higher-order functions 86 | 87 | /// Returns `self.value` if this result is a .Success, or the given value otherwise. Equivalent with `??` 88 | public func recover(_ value: @autoclosure () -> Value) -> Value { 89 | return self.value ?? value() 90 | } 91 | 92 | /// Returns this result if it is a .Success, or the given result otherwise. Equivalent with `??` 93 | public func recover(with result: @autoclosure () -> Self) -> Self { 94 | return analysis( 95 | ifSuccess: { _ in self }, 96 | ifFailure: { _ in result() }) 97 | } 98 | } 99 | 100 | /// Protocol used to constrain `tryMap` to `Result`s with compatible `Error`s. 101 | public protocol ErrorProtocolConvertible: Swift.Error { 102 | static func error(from error: Swift.Error) -> Self 103 | } 104 | 105 | public extension ResultProtocol where Error: ErrorProtocolConvertible { 106 | 107 | /// Returns the result of applying `transform` to `Success`es’ values, or wrapping thrown errors. 108 | public func tryMap(_ transform: (Value) throws -> U) -> Result { 109 | return flatMap { value in 110 | do { 111 | return .success(try transform(value)) 112 | } 113 | catch { 114 | let convertedError = Error.error(from: error) 115 | // Revisit this in a future version of Swift. https://twitter.com/jckarter/status/672931114944696321 116 | return .failure(convertedError) 117 | } 118 | } 119 | } 120 | } 121 | 122 | // MARK: - Operators 123 | 124 | infix operator &&& : LogicalConjunctionPrecedence 125 | 126 | /// Returns a Result with a tuple of `left` and `right` values if both are `Success`es, or re-wrapping the error of the earlier `Failure`. 127 | @available(*, deprecated, renamed: "ResultProtocol.fanout(self:_:)") 128 | public func &&& (left: L, right: @autoclosure () -> R) -> Result<(L.Value, R.Value), L.Error> 129 | where L.Error == R.Error 130 | { 131 | return left.fanout(right) 132 | } 133 | 134 | precedencegroup ChainingPrecedence { 135 | associativity: left 136 | higherThan: TernaryPrecedence 137 | } 138 | 139 | infix operator >>- : ChainingPrecedence 140 | 141 | /// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors. 142 | /// 143 | /// This is a synonym for `flatMap`. 144 | @available(*, deprecated, renamed: "ResultProtocol.flatMap(self:_:)") 145 | public func >>- (result: T, transform: (T.Value) -> Result) -> Result { 146 | return result.flatMap(transform) 147 | } 148 | 149 | /// Returns `true` if `left` and `right` are both `Success`es and their values are equal, or if `left` and `right` are both `Failure`s and their errors are equal. 150 | public func == (left: T, right: T) -> Bool 151 | where T.Value: Equatable, T.Error: Equatable 152 | { 153 | if let left = left.value, let right = right.value { 154 | return left == right 155 | } else if let left = left.error, let right = right.error { 156 | return left == right 157 | } 158 | return false 159 | } 160 | 161 | /// Returns `true` if `left` and `right` represent different cases, or if they represent the same case but different values. 162 | public func != (left: T, right: T) -> Bool 163 | where T.Value: Equatable, T.Error: Equatable 164 | { 165 | return !(left == right) 166 | } 167 | 168 | /// Returns the value of `left` if it is a `Success`, or `right` otherwise. Short-circuits. 169 | public func ?? (left: T, right: @autoclosure () -> T.Value) -> T.Value { 170 | return left.recover(right()) 171 | } 172 | 173 | /// Returns `left` if it is a `Success`es, or `right` otherwise. Short-circuits. 174 | public func ?? (left: T, right: @autoclosure () -> T) -> T { 175 | return left.recover(with: right()) 176 | } 177 | 178 | // MARK: - migration support 179 | @available(*, unavailable, renamed: "ResultProtocol") 180 | public typealias ResultType = ResultProtocol 181 | 182 | @available(*, unavailable, renamed: "Error") 183 | public typealias ResultErrorType = Swift.Error 184 | 185 | @available(*, unavailable, renamed: "ErrorProtocolConvertible") 186 | public typealias ErrorTypeConvertible = ErrorProtocolConvertible 187 | 188 | extension ResultProtocol { 189 | @available(*, unavailable, renamed: "recover(with:)") 190 | public func recoverWith(_ result: @autoclosure () -> Self) -> Self { 191 | fatalError() 192 | } 193 | } 194 | 195 | extension ErrorProtocolConvertible { 196 | @available(*, unavailable, renamed: "error(from:)") 197 | public static func errorFromErrorType(_ error: Swift.Error) -> Self { 198 | fatalError() 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/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 | 4.4.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Alamofire 5 | 6 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## Result 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2014 Rob Rix 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy 34 | of this software and associated documentation files (the "Software"), to deal 35 | in the Software without restriction, including without limitation the rights 36 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 37 | copies of the Software, and to permit persons to whom the Software is 38 | furnished to do so, subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 46 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 48 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 49 | SOFTWARE. 50 | 51 | ## UIView+Shake 52 | 53 | The MIT License (MIT) 54 | 55 | Copyright (c) 2014 Andrea Mazzini 56 | 57 | Permission is hereby granted, free of charge, to any person obtaining a copy 58 | of this software and associated documentation files (the "Software"), to deal 59 | in the Software without restriction, including without limitation the rights 60 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 61 | copies of the Software, and to permit persons to whom the Software is 62 | furnished to do so, subject to the following conditions: 63 | 64 | The above copyright notice and this permission notice shall be included in all 65 | copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 68 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 69 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 70 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 71 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 72 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 73 | SOFTWARE. 74 | Generated by CocoaPods - https://cocoapods.org 75 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Alamofire 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | The MIT License (MIT) 47 | 48 | Copyright (c) 2014 Rob Rix 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in all 58 | copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 66 | SOFTWARE. 67 | License 68 | MIT 69 | Title 70 | Result 71 | Type 72 | PSGroupSpecifier 73 | 74 | 75 | FooterText 76 | The MIT License (MIT) 77 | 78 | Copyright (c) 2014 Andrea Mazzini 79 | 80 | Permission is hereby granted, free of charge, to any person obtaining a copy 81 | of this software and associated documentation files (the "Software"), to deal 82 | in the Software without restriction, including without limitation the rights 83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 84 | copies of the Software, and to permit persons to whom the Software is 85 | furnished to do so, subject to the following conditions: 86 | 87 | The above copyright notice and this permission notice shall be included in all 88 | copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 96 | SOFTWARE. 97 | License 98 | MIT 99 | Title 100 | UIView+Shake 101 | Type 102 | PSGroupSpecifier 103 | 104 | 105 | FooterText 106 | Generated by CocoaPods - https://cocoapods.org 107 | Title 108 | 109 | Type 110 | PSGroupSpecifier 111 | 112 | 113 | StringsTable 114 | Acknowledgements 115 | Title 116 | Acknowledgements 117 | 118 | 119 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_luckybeast : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_luckybeast 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 12 | 13 | install_framework() 14 | { 15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 16 | local source="${BUILT_PRODUCTS_DIR}/$1" 17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 19 | elif [ -r "$1" ]; then 20 | local source="$1" 21 | fi 22 | 23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | 25 | if [ -L "${source}" ]; then 26 | echo "Symlinked..." 27 | source="$(readlink "${source}")" 28 | fi 29 | 30 | # Use filter instead of exclude so missing patterns don't throw errors. 31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 33 | 34 | local basename 35 | basename="$(basename -s .framework "$1")" 36 | binary="${destination}/${basename}.framework/${basename}" 37 | if ! [ -r "$binary" ]; then 38 | binary="${destination}/${basename}" 39 | fi 40 | 41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 43 | strip_invalid_archs "$binary" 44 | fi 45 | 46 | # Resign the code if required by the build settings to avoid unstable apps 47 | code_sign_if_enabled "${destination}/$(basename "$1")" 48 | 49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 51 | local swift_runtime_libs 52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 53 | for lib in $swift_runtime_libs; do 54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 56 | code_sign_if_enabled "${destination}/${lib}" 57 | done 58 | fi 59 | } 60 | 61 | # Copies the dSYM of a vendored framework 62 | install_dsym() { 63 | local source="$1" 64 | if [ -r "$source" ]; then 65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\"" 66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}" 67 | fi 68 | } 69 | 70 | # Signs a framework with the provided identity 71 | code_sign_if_enabled() { 72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 73 | # Use the current code_sign_identitiy 74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 76 | 77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 78 | code_sign_cmd="$code_sign_cmd &" 79 | fi 80 | echo "$code_sign_cmd" 81 | eval "$code_sign_cmd" 82 | fi 83 | } 84 | 85 | # Strip invalid architectures 86 | strip_invalid_archs() { 87 | binary="$1" 88 | # Get architectures for current file 89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 90 | stripped="" 91 | for arch in $archs; do 92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 93 | # Strip non-valid architectures in-place 94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 95 | stripped="$stripped $arch" 96 | fi 97 | done 98 | if [[ "$stripped" ]]; then 99 | echo "Stripped $binary of architectures:$stripped" 100 | fi 101 | } 102 | 103 | 104 | if [[ "$CONFIGURATION" == "Debug" ]]; then 105 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 106 | install_framework "${BUILT_PRODUCTS_DIR}/Result/Result.framework" 107 | install_framework "${BUILT_PRODUCTS_DIR}/UIView+Shake/UIView_Shake.framework" 108 | fi 109 | if [[ "$CONFIGURATION" == "Release" ]]; then 110 | install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework" 111 | install_framework "${BUILT_PRODUCTS_DIR}/Result/Result.framework" 112 | install_framework "${BUILT_PRODUCTS_DIR}/UIView+Shake/UIView_Shake.framework" 113 | fi 114 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 115 | wait 116 | fi 117 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync --delete -avr --copy-links --no-relative "${RSYNC_PROTECT_TMP_FILES[@]}" --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync --delete -avr --copy-links --no-relative "${RSYNC_PROTECT_TMP_FILES[@]}" --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_luckybeastVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_luckybeastVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Result" "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Result/Result.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake/UIView_Shake.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Result" -framework "UIView_Shake" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_luckybeast { 2 | umbrella header "Pods-luckybeast-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeast/Pods-luckybeast.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Result" "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Result/Result.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake/UIView_Shake.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Result" -framework "UIView_Shake" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/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 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_luckybeastTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_luckybeastTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 12 | 13 | install_framework() 14 | { 15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 16 | local source="${BUILT_PRODUCTS_DIR}/$1" 17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 19 | elif [ -r "$1" ]; then 20 | local source="$1" 21 | fi 22 | 23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 24 | 25 | if [ -L "${source}" ]; then 26 | echo "Symlinked..." 27 | source="$(readlink "${source}")" 28 | fi 29 | 30 | # Use filter instead of exclude so missing patterns don't throw errors. 31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 33 | 34 | local basename 35 | basename="$(basename -s .framework "$1")" 36 | binary="${destination}/${basename}.framework/${basename}" 37 | if ! [ -r "$binary" ]; then 38 | binary="${destination}/${basename}" 39 | fi 40 | 41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 43 | strip_invalid_archs "$binary" 44 | fi 45 | 46 | # Resign the code if required by the build settings to avoid unstable apps 47 | code_sign_if_enabled "${destination}/$(basename "$1")" 48 | 49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 51 | local swift_runtime_libs 52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 53 | for lib in $swift_runtime_libs; do 54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 56 | code_sign_if_enabled "${destination}/${lib}" 57 | done 58 | fi 59 | } 60 | 61 | # Copies the dSYM of a vendored framework 62 | install_dsym() { 63 | local source="$1" 64 | if [ -r "$source" ]; then 65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\"" 66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}" 67 | fi 68 | } 69 | 70 | # Signs a framework with the provided identity 71 | code_sign_if_enabled() { 72 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 73 | # Use the current code_sign_identitiy 74 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 75 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 76 | 77 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 78 | code_sign_cmd="$code_sign_cmd &" 79 | fi 80 | echo "$code_sign_cmd" 81 | eval "$code_sign_cmd" 82 | fi 83 | } 84 | 85 | # Strip invalid architectures 86 | strip_invalid_archs() { 87 | binary="$1" 88 | # Get architectures for current file 89 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 90 | stripped="" 91 | for arch in $archs; do 92 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 93 | # Strip non-valid architectures in-place 94 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 95 | stripped="$stripped $arch" 96 | fi 97 | done 98 | if [[ "$stripped" ]]; then 99 | echo "Stripped $binary of architectures:$stripped" 100 | fi 101 | } 102 | 103 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 104 | wait 105 | fi 106 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync --delete -avr --copy-links --no-relative "${RSYNC_PROTECT_TMP_FILES[@]}" --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync --delete -avr --copy-links --no-relative "${RSYNC_PROTECT_TMP_FILES[@]}" --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_luckybeastTestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_luckybeastTestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Result" "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Result/Result.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake/UIView_Shake.framework/Headers" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_luckybeastTests { 2 | umbrella header "Pods-luckybeastTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-luckybeastTests/Pods-luckybeastTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Result" "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Result/Result.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIView+Shake/UIView_Shake.framework/Headers" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/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 | 3.2.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/Result-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Result : NSObject 3 | @end 4 | @implementation PodsDummy_Result 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/Result-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/Result-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double ResultVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char ResultVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/Result.modulemap: -------------------------------------------------------------------------------- 1 | framework module Result { 2 | umbrella header "Result-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Result/Result.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Result 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Result 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/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 | 1.3.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/UIView+Shake-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_UIView_Shake : NSObject 3 | @end 4 | @implementation PodsDummy_UIView_Shake 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/UIView+Shake-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/UIView+Shake-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "UIView+Shake.h" 14 | 15 | FOUNDATION_EXPORT double UIView_ShakeVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char UIView_ShakeVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/UIView+Shake.modulemap: -------------------------------------------------------------------------------- 1 | framework module UIView_Shake { 2 | umbrella header "UIView+Shake-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIView+Shake/UIView+Shake.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/UIView+Shake 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = $BUILD_DIR 5 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/UIView+Shake 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/UIView+Shake/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Andrea Mazzini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Pods/UIView+Shake/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [![Build Status](https://travis-ci.org/andreamazz/UIView-Shake.svg)](https://travis-ci.org/andreamazz/UIView-Shake) 6 | [![CocoaPods](https://cocoapod-badges.herokuapp.com/v/UIView+Shake/badge.svg)](http://cocoapods.org/?q=summary%3Auiview%20name%3Ashake%2A) 7 | [![codecov](https://codecov.io/gh/andreamazz/UIView-Shake/branch/master/graph/badge.svg)](https://codecov.io/gh/andreamazz/UIView-Shake) 8 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 9 | 10 | UIView category that adds a shake animation like the password field of the OSX login screen. 11 | 12 |

13 | 14 | 15 | 16 |

17 | 18 | ##Screenshot 19 | ![UIView+Shake](https://raw.githubusercontent.com/andreamazz/UIView-Shake/master/assets/screenshot.gif) 20 | 21 | ##Setup with CocoaPods 22 | * Add ```pod 'UIView+Shake'``` to your Podfile 23 | * Run ```pod install``` 24 | * Run ```open App.xcworkspace``` 25 | 26 | ###Objective-C 27 | Import ```UIView+Shake.h``` in your controller's header file 28 | ###Swift 29 | If you are using `use_frameworks!` in your Podfile, use this import: 30 | ```swift 31 | import UIView_Shake 32 | ``` 33 | 34 | ##Usage 35 | 36 | ###In Objective-C 37 | 38 | ```objc 39 | // Shake 40 | [[self.view] shake]; 41 | 42 | // Shake with the default speed 43 | [self.view shake:10 // 10 times 44 | withDelta:5 // 5 points wide 45 | ]; 46 | 47 | // Shake with a custom speed 48 | [self.view shake:10 // 10 times 49 | withDelta:5 // 5 points wide 50 | speed:0.03 // 30ms per shake 51 | ]; 52 | 53 | // Shake with a custom speed and direction 54 | [self.view shake:10 // 10 times 55 | withDelta:5 // 5 points wide 56 | speed:0.03 // 30ms per shake 57 | shakeDirection:ShakeDirectionVertical 58 | ]; 59 | ``` 60 | 61 | ###In Swift 62 | 63 | ```swift 64 | // Shake 65 | self.view.shake() 66 | 67 | // Shake with the default speed 68 | self.view.shake(10, // 10 times 69 | withDelta: 5.0 // 5 points wide 70 | ) 71 | 72 | // Shake with a custom speed 73 | self.view.shake(10, // 10 times 74 | withDelta: 5.0, // 5 points wide 75 | speed: 0.03 // 30ms per shake 76 | ) 77 | 78 | // Shake with a custom speed and direction 79 | self.view.shake(10, // 10 times 80 | withDelta: 5.0, // 5 points wide 81 | speed: 0.03, // 30ms per shake 82 | shakeDirection: ShakeDirection.Vertical 83 | ) 84 | ``` 85 | 86 | ##Direction 87 | The shake direction is either `.Vertical`, `.Horizontal` or `.Rotation` 88 | 89 | ##Completion Handler 90 | You can also pass a completion block that will be performed as soon as the shake animation stops 91 | ```objc 92 | - (void)shake:(int)times withDelta:(CGFloat)delta completion:(void((^)()))handler; 93 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval completion:(void((^)()))handler; 94 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection completion:(void((^)()))handler; 95 | ``` 96 | or in Swift using the trailing closure syntax: 97 | ```swift 98 | view.shake(10, withDelta: 5) { 99 | println("done") 100 | } 101 | ``` 102 | 103 | #Author 104 | [Andrea Mazzini](https://twitter.com/theandreamazz). I'm available for freelance work, feel free to contact me. 105 | 106 | Want to support the development of [these free libraries](https://cocoapods.org/owners/734)? Buy me a coffee ☕️ via [Paypal](https://www.paypal.me/andreamazzini). 107 | 108 | #Contributors 109 | Thanks to [everyone](https://github.com/andreamazz/UIView-Shake/graphs/contributors) kind enough to submit a pull request. 110 | 111 | 112 | #MIT License 113 | Copyright (c) 2015 Andrea Mazzini. All rights reserved. 114 | 115 | Permission is hereby granted, free of charge, to any person obtaining a 116 | copy of this software and associated documentation files (the "Software"), 117 | to deal in the Software without restriction, including 118 | without limitation the rights to use, copy, modify, merge, publish, 119 | distribute, sublicense, and/or sell copies of the Software, and to 120 | permit persons to whom the Software is furnished to do so, subject to 121 | the following conditions: 122 | 123 | The above copyright notice and this permission notice shall be included 124 | in all copies or substantial portions of the Software. 125 | 126 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 127 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 128 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 129 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 130 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 131 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 132 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 133 | -------------------------------------------------------------------------------- /Pods/UIView+Shake/Source/UIView+Shake.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Shake.h 3 | // UIView+Shake 4 | // 5 | // Created by Andrea Mazzini on 08/02/14. 6 | // Copyright (c) 2014 Fancy Pixel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** @enum ShakeDirection 12 | * 13 | * Enum that specifies the direction of the shake 14 | */ 15 | typedef NS_ENUM(NSInteger, ShakeDirection) { 16 | /** Shake left and right */ 17 | ShakeDirectionHorizontal, 18 | /** Shake up and down */ 19 | ShakeDirectionVertical, 20 | /** Shake rotation */ 21 | ShakeDirectionRotation 22 | }; 23 | 24 | @interface UIView (Shake) 25 | 26 | /**----------------------------------------------------------------------------- 27 | * @name UIView+Shake 28 | * ----------------------------------------------------------------------------- 29 | */ 30 | 31 | /** Shake the UIView 32 | * 33 | * Shake the view a default number of times 34 | */ 35 | - (void)shake; 36 | 37 | /** Shake the UIView 38 | * 39 | * Shake the view a given number of times 40 | * 41 | * @param times The number of shakes 42 | * @param delta The width of the shake 43 | */ 44 | - (void)shake:(int)times withDelta:(CGFloat)delta; 45 | 46 | /** Shake the UIView 47 | * 48 | * Shake the view a given number of times 49 | * 50 | * @param times The number of shakes 51 | * @param delta The width of the shake 52 | * @param handler A block object to be executed when the shake sequence ends 53 | */ 54 | - (void)shake:(int)times withDelta:(CGFloat)delta completion:(nullable void (^)(void))handler; 55 | 56 | /** Shake the UIView at a custom speed 57 | * 58 | * Shake the view a given number of times with a given speed 59 | * 60 | * @param times The number of shakes 61 | * @param delta The width of the shake 62 | * @param interval The duration of one shake 63 | */ 64 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval; 65 | 66 | /** Shake the UIView at a custom speed 67 | * 68 | * Shake the view a given number of times with a given speed 69 | * 70 | * @param times The number of shakes 71 | * @param delta The width of the shake 72 | * @param interval The duration of one shake 73 | * @param handler A block object to be executed when the shake sequence ends 74 | */ 75 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval completion:(nullable void (^)(void))handler; 76 | 77 | /** Shake the UIView at a custom speed 78 | * 79 | * Shake the view a given number of times with a given speed 80 | * 81 | * @param times The number of shakes 82 | * @param delta The width of the shake 83 | * @param interval The duration of one shake 84 | * @param shakeDirection of the shake 85 | */ 86 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection; 87 | 88 | /** Shake the UIView at a custom speed 89 | * 90 | * Shake the view a given number of times with a given speed, with a completion handler 91 | * 92 | * @param times The number of shakes 93 | * @param delta The width of the shake 94 | * @param interval The duration of one shake 95 | * @param shakeDirection of the shake 96 | * @param completion to be called when the view is done shaking 97 | */ 98 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection completion:(nullable void (^)(void))completion; 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Pods/UIView+Shake/Source/UIView+Shake.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Shake.m 3 | // UIView+Shake 4 | // 5 | // Created by Andrea Mazzini on 08/02/14. 6 | // Copyright (c) 2014 Fancy Pixel. All rights reserved. 7 | // 8 | 9 | #import "UIView+Shake.h" 10 | 11 | @implementation UIView (Shake) 12 | 13 | - (void)shake { 14 | [self shake:10 withDelta:5 speed:0.03]; 15 | } 16 | 17 | - (void)shake:(int)times withDelta:(CGFloat)delta { 18 | [self shake:times withDelta:delta completion:nil]; 19 | } 20 | 21 | - (void)shake:(int)times withDelta:(CGFloat)delta completion:(nullable void (^)(void))handler { 22 | [self shake:times withDelta:delta speed:0.03 completion:handler]; 23 | } 24 | 25 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval { 26 | [self shake:times withDelta:delta speed:interval completion:nil]; 27 | } 28 | 29 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval completion:(nullable void (^)(void))handler { 30 | [self shake:times withDelta:delta speed:interval shakeDirection:ShakeDirectionHorizontal completion:handler]; 31 | } 32 | 33 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection { 34 | [self shake:times withDelta:delta speed:interval shakeDirection:shakeDirection completion:nil]; 35 | } 36 | 37 | - (void)shake:(int)times withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection completion:(nullable void (^)(void))completion { 38 | [self _shake:times direction:1 currentTimes:0 withDelta:delta speed:interval shakeDirection:shakeDirection completion:completion]; 39 | } 40 | 41 | - (void)_shake:(int)times direction:(int)direction currentTimes:(int)current withDelta:(CGFloat)delta speed:(NSTimeInterval)interval shakeDirection:(ShakeDirection)shakeDirection completion:(void (^)(void))completionHandler { 42 | __weak UIView *weakSelf = self; 43 | [UIView animateWithDuration:interval animations:^{ 44 | switch (shakeDirection) { 45 | case ShakeDirectionVertical: 46 | weakSelf.layer.affineTransform = CGAffineTransformMakeTranslation(0, delta * direction); 47 | break; 48 | case ShakeDirectionRotation: 49 | weakSelf.layer.affineTransform = CGAffineTransformMakeRotation(M_PI * delta / 1000.0f * direction); 50 | break; 51 | case ShakeDirectionHorizontal: 52 | weakSelf.layer.affineTransform = CGAffineTransformMakeTranslation(delta * direction, 0); 53 | default: 54 | break; 55 | } 56 | } completion:^(BOOL finished) { 57 | if(current >= times) { 58 | [UIView animateWithDuration:interval animations:^{ 59 | weakSelf.layer.affineTransform = CGAffineTransformIdentity; 60 | } completion:^(BOOL finished){ 61 | if (completionHandler != nil) { 62 | completionHandler(); 63 | } 64 | }]; 65 | return; 66 | } 67 | [weakSelf _shake:(times - 1) 68 | direction:direction * -1 69 | currentTimes:current + 1 70 | withDelta:delta 71 | speed:interval 72 | shakeDirection:shakeDirection 73 | completion:completionHandler]; 74 | }]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LuckyBeast 2 | けものフレンズに出てくるラッキービストみたいな何かをiPhoneで作ってみるテスト 3 | 4 | 詳細 5 | 6 | * https://twitter.com/croquette0212/status/848385807270289409 7 | * https://twitter.com/croquette0212/status/853465013024980992 8 | * [iPhoneのSFSpeechRecognizerとAVSpeechSynthesizerと発泡スチロールでボスっぽいなにかを作る](http://qiita.com/croquette0212/items/bf0e41ca1b65c6d320b4) 9 | 10 | ## 動作環境/必要なもの 11 | * iOS 10以降 12 | 13 | ## ビルド方法 14 | ### CoocaPods 15 | 16 | CocoaPodsが必要です。 17 | 18 | ``` 19 | pod install 20 | ``` 21 | 22 | ## 操作方法 23 | ### モードの切り替え 24 | 画面をダブルタップすると、全身モード(目とボディが表示される)と組み込みモード(真ん中の四角いやつだけが表示される)を切り替えることができます。 25 | 26 | ## ライセンス 27 | MIT License 28 | -------------------------------------------------------------------------------- /luckybeast.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /luckybeast.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /luckybeast/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | 9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 10 | // Override point for customization after application launch. 11 | UIApplication.shared.isIdleTimerDisabled = true 12 | return true 13 | } 14 | 15 | func applicationWillResignActive(_ application: UIApplication) { 16 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 17 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 18 | } 19 | 20 | func applicationDidEnterBackground(_ application: UIApplication) { 21 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 22 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 23 | } 24 | 25 | func applicationWillEnterForeground(_ application: UIApplication) { 26 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 27 | } 28 | 29 | func applicationDidBecomeActive(_ application: UIApplication) { 30 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 31 | } 32 | 33 | func applicationWillTerminate(_ application: UIApplication) { 34 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 35 | } 36 | 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /luckybeast/AppSettings.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class AppSettings { 4 | struct Key { 5 | static let displayMode: String = "DisplayMode" 6 | } 7 | 8 | static let shared = AppSettings() 9 | 10 | var displayMode: DisplayMode? { 11 | set(value) { 12 | UserDefaults.standard.set(value?.rawValue, forKey: Key.displayMode) 13 | UserDefaults.standard.synchronize() 14 | } 15 | get { 16 | guard let object = UserDefaults.standard.object(forKey: Key.displayMode) as? String else { 17 | return nil 18 | } 19 | 20 | return DisplayMode(rawValue: object) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /luckybeast/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /luckybeast/BackgroundView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class BackgroundView: UIView { 4 | enum Mode { 5 | case texture 6 | case plain 7 | } 8 | 9 | var mode: Mode = .texture { 10 | didSet { 11 | setNeedsDisplay() 12 | } 13 | } 14 | 15 | override func draw(_ rect: CGRect) { 16 | switch mode { 17 | case .texture: 18 | UIColor(red: 180 / 255, green: 223 / 255, blue: 214 / 255, alpha: 1).setFill() 19 | UIRectFill(bounds) 20 | 21 | UIColor.white.setFill() 22 | UIBezierPath(ovalIn: bounds.insetBy(dx: -240, dy: -40).offsetBy(dx: 0, dy: 160)).fill() 23 | case .plain: 24 | UIColor.white.setFill() 25 | UIRectFill(bounds) 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /luckybeast/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /luckybeast/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /luckybeast/BossCoreView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class BossCoreView: UIView { 4 | private static let padding: CGFloat = 10 5 | private static let indicatorPadding: CGFloat = 10 6 | private static let indicatorDivisions = 20 7 | 8 | var mode: LuckyBeast.Mode = .idle { 9 | didSet { 10 | if mode == .thinking { 11 | startIndicatorTimer() 12 | } else { 13 | stopIndicatorTimer() 14 | } 15 | setNeedsDisplay() 16 | } 17 | } 18 | private var timerStartedAt: Date? 19 | 20 | override func draw(_ rect: CGRect) { 21 | UIColor.black.setFill() 22 | UIRectFill(bounds) 23 | 24 | drawBackground() 25 | 26 | if mode == .thinking { 27 | drawIndicator() 28 | } 29 | } 30 | 31 | private func drawBackground() { 32 | switch mode { 33 | case .idle: 34 | UIColor.darkGray.setFill() 35 | case .launching: 36 | UIColor.yellow.setFill() 37 | case .listening, .thinking: 38 | UIColor.lightGray.setFill() 39 | case .playing: 40 | UIColor.green.setFill() 41 | case .panic: 42 | UIColor.white.setFill() 43 | } 44 | UIBezierPath(ovalIn: bounds.insetBy(dx: BossCoreView.padding, dy: BossCoreView.padding)).fill() 45 | } 46 | 47 | private func drawIndicator() { 48 | let centerX = bounds.midX 49 | let centerY = bounds.midY 50 | let indicatorRadius = min(centerX, centerY) - BossCoreView.indicatorPadding - BossCoreView.padding 51 | 52 | let timeElapsed = -(timerStartedAt?.timeIntervalSinceNow ?? 0) 53 | let indicatorProgress = TimeInterval(Int(round(timeElapsed * 100)) % 100) * 0.01 54 | 55 | for i in 0...BossCoreView.indicatorDivisions { 56 | let alpha: CGFloat = indicatorProgress * 4 - TimeInterval(i) * 0.1 > 0 ? 1 : 0 57 | UIColor(white: 1, alpha: min(alpha, 1)).setStroke() 58 | 59 | let angle = CGFloat(i) / CGFloat(BossCoreView.indicatorDivisions) * 2 * CGFloat.pi - CGFloat.pi * 0.5 60 | let path = UIBezierPath() 61 | path.move(to: CGPoint(x: centerX + cos(angle) * indicatorRadius, y: centerY + sin(angle) * indicatorRadius)) 62 | path.addLine(to: CGPoint(x: centerX + cos(angle) * indicatorRadius * 0.7, y: centerY + sin(angle) * indicatorRadius * 0.7)) 63 | path.lineWidth = 10 64 | path.stroke() 65 | } 66 | } 67 | 68 | // Indicator timer 69 | private var indicatorTimer: Timer? 70 | private func startIndicatorTimer() { 71 | if indicatorTimer == nil { 72 | timerStartedAt = Date() 73 | indicatorTimer = Timer.scheduledTimer(withTimeInterval: 0.03, repeats: true) { timer in 74 | self.setNeedsDisplay() 75 | } 76 | } 77 | } 78 | 79 | private func stopIndicatorTimer() { 80 | indicatorTimer?.invalidate() 81 | indicatorTimer = nil 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /luckybeast/Capturer.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import AVFoundation 3 | 4 | protocol CapturerDelegate: class { 5 | func capturer(_ capturer: Capturer, didCaptureImage image: UIImage) 6 | } 7 | 8 | enum CapturerError: Error { 9 | case noFrontCamera 10 | } 11 | 12 | class Capturer: NSObject { 13 | fileprivate var captureSession: AVCaptureSession! 14 | fileprivate var stillImageOutput: AVCapturePhotoOutput? 15 | 16 | weak var delegate: CapturerDelegate? 17 | 18 | func start() throws { 19 | captureSession = AVCaptureSession() 20 | stillImageOutput = AVCapturePhotoOutput() 21 | 22 | captureSession.sessionPreset = AVCaptureSessionPreset640x480 23 | guard let device = AVCaptureDevice.defaultDevice(withDeviceType: .builtInWideAngleCamera, mediaType: AVMediaTypeVideo, position: .front) else { 24 | throw CapturerError.noFrontCamera 25 | } 26 | 27 | do { 28 | let input = try AVCaptureDeviceInput(device: device) 29 | 30 | if (captureSession.canAddInput(input)) { 31 | captureSession.addInput(input) 32 | 33 | if (captureSession.canAddOutput(stillImageOutput)) { 34 | captureSession.addOutput(stillImageOutput) 35 | captureSession.startRunning() 36 | } 37 | } 38 | } catch { 39 | debugPrint(error) 40 | } 41 | } 42 | 43 | func capture() { 44 | let settings = AVCapturePhotoSettings() 45 | settings.isAutoStillImageStabilizationEnabled = true 46 | settings.isHighResolutionPhotoEnabled = false 47 | stillImageOutput?.capturePhoto(with: settings, delegate: self) 48 | } 49 | } 50 | 51 | extension Capturer: AVCapturePhotoCaptureDelegate { 52 | func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { 53 | guard let photoSampleBuffer = photoSampleBuffer, 54 | let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer), 55 | let image = UIImage(data: photoData) else { 56 | fatalError("Capture failure.") 57 | } 58 | 59 | delegate?.capturer(self, didCaptureImage: image) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /luckybeast/Const.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class Const: NSObject { 4 | private static func stringValue(forKey key: String) -> String { 5 | guard let plistURL = Bundle.main.url(forResource: "Keys", withExtension: "plist") else { 6 | fatalError("Keys.plist not found.") 7 | } 8 | 9 | guard let plist = NSDictionary(contentsOf: plistURL) else { 10 | fatalError("Cannot load Keys.plist") 11 | } 12 | 13 | guard let value = plist[key] as? String else { 14 | fatalError("\(key) is not set in Keys.plist") 15 | } 16 | 17 | return value 18 | } 19 | 20 | static var luckyBeastServerAPIEndpoint: String { 21 | return stringValue(forKey: "LUCKYBEAST_SERVER_API_ENDPOINT") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /luckybeast/DisplayMode.swift: -------------------------------------------------------------------------------- 1 | enum DisplayMode: String { 2 | case phone 3 | case costume 4 | } 5 | -------------------------------------------------------------------------------- /luckybeast/EyeView.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class EyeView: UIView { 4 | 5 | enum Status { 6 | case normal 7 | case white 8 | case shining 9 | } 10 | 11 | var status: Status = .normal { 12 | didSet { 13 | setNeedsDisplay() 14 | } 15 | } 16 | 17 | override func layoutSubviews() { 18 | self.layer.cornerRadius = bounds.width * 0.5 19 | clipsToBounds = true 20 | } 21 | 22 | // Only override draw() if you perform custom drawing. 23 | // An empty implementation adversely affects performance during animation. 24 | override func draw(_ rect: CGRect) { 25 | UIColor.black.setFill() 26 | UIRectFill(bounds) 27 | 28 | if status == .white { 29 | UIColor.white.setFill() 30 | UIBezierPath(roundedRect: bounds.insetBy(dx: 4, dy: 4), cornerRadius: 8).fill() 31 | } 32 | 33 | UIColor.white.setFill() 34 | UIBezierPath(ovalIn: CGRect(x: 4, y: 8, width: 10, height: 10)).fill() 35 | 36 | if status == .shining { 37 | UIColor.blue.setStroke() 38 | let path = UIBezierPath(roundedRect: bounds.insetBy(dx: 4, dy: 4), cornerRadius: 8) 39 | path.lineWidth = 3 40 | path.stroke() 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /luckybeast/FoundationExt.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | // http://stackoverflow.com/questions/32812268/how-get-the-lastindexof-with-swift-2-0 4 | extension String { 5 | func index(of target: String) -> Int? { 6 | if let range = self.range(of: target) { 7 | return characters.distance(from: startIndex, to: range.lowerBound) 8 | } else { 9 | return nil 10 | } 11 | } 12 | 13 | func lastIndex(of target: String) -> Int? { 14 | if let range = self.range(of: target, options: .backwards) { 15 | return characters.distance(from: startIndex, to: range.lowerBound) 16 | } else { 17 | return nil 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /luckybeast/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 | APPL 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleVersion 20 | 2 21 | LSRequiresIPhoneOS 22 | 23 | NSCameraUsageDescription 24 | カメラ使う 25 | NSMicrophoneUsageDescription 26 | マイクの用途について記載します。 27 | NSPhotoLibraryUsageDescription 28 | ライブラリ使う 29 | NSSpeechRecognitionUsageDescription 30 | 音声認識の用途について記載します。 31 | UIApplicationExitsOnSuspend 32 | true 33 | UILaunchStoryboardName 34 | LaunchScreen 35 | UIMainStoryboardFile 36 | Main 37 | UIRequiredDeviceCapabilities 38 | 39 | armv7 40 | 41 | UIStatusBarHidden 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /luckybeast/Keys.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LUCKYBEAST_SERVER_API_ENDPOINT 6 | https://luckybeast.herokuapp.com 7 | 8 | 9 | -------------------------------------------------------------------------------- /luckybeast/Listener.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Speech 3 | 4 | protocol ListenerDelegate: class { 5 | func listener(_ listener: Listener, didUpdateRecognizerAuthorizationStatus status: SFSpeechRecognizerAuthorizationStatus) 6 | func listener(_ listener: Listener, didDetectWordToLookUp word: String) 7 | func listenerDidDetectCaptureRequest(_ listener: Listener) 8 | func listenerDidDetectNameCalled(_ listener: Listener) 9 | func listener(_ listener: Listener, didUpdateBestTranscription transcription: String?) 10 | } 11 | 12 | class Listener: NSObject { 13 | private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! 14 | private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? 15 | private var recognitionTask: SFSpeechRecognitionTask? 16 | fileprivate let audioEngine = AVAudioEngine() 17 | 18 | weak var delegate: ListenerDelegate? 19 | var bestTranscriptionString: String? { 20 | didSet { 21 | delegate?.listener(self, didUpdateBestTranscription: bestTranscriptionString) 22 | } 23 | } 24 | 25 | private var isMonitoring = false 26 | 27 | var authorizationStatus: SFSpeechRecognizerAuthorizationStatus = .notDetermined { 28 | didSet { 29 | delegate?.listener(self, didUpdateRecognizerAuthorizationStatus: authorizationStatus) 30 | } 31 | } 32 | 33 | override init() { 34 | super.init() 35 | speechRecognizer.delegate = self 36 | } 37 | 38 | func requestAuthorization() { 39 | SFSpeechRecognizer.requestAuthorization { authStatus in 40 | OperationQueue.main.addOperation { 41 | self.authorizationStatus = authStatus 42 | } 43 | } 44 | } 45 | 46 | private var isNameCalled = false 47 | 48 | func startMonitoring() throws { 49 | if isMonitoring { return } 50 | isMonitoring = true 51 | isNameCalled = false 52 | 53 | recognitionTask?.cancel() 54 | recognitionTask = nil 55 | 56 | let audioSession = AVAudioSession.sharedInstance() 57 | try audioSession.setCategory(AVAudioSessionCategoryRecord) 58 | try audioSession.setMode(AVAudioSessionModeMeasurement) 59 | try audioSession.setActive(true, with: .notifyOthersOnDeactivation) 60 | 61 | recognitionRequest = SFSpeechAudioBufferRecognitionRequest() 62 | 63 | guard let inputNode = audioEngine.inputNode else { fatalError("Audio engine has no input node") } 64 | guard let recognitionRequest = recognitionRequest else { fatalError("Unable to created a SFSpeechAudioBufferRecognitionRequest object") } 65 | 66 | recognitionRequest.shouldReportPartialResults = true 67 | 68 | recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in 69 | guard let `self` = self else { return } 70 | 71 | var isFinal = false 72 | 73 | if let result = result { 74 | let input = result.bestTranscription.formattedString 75 | isFinal = result.isFinal 76 | self.bestTranscriptionString = input 77 | 78 | if !self.isNameCalled && input.lastIndex(of: "ボス") != nil { 79 | self.delegate?.listenerDidDetectNameCalled(self) 80 | self.isNameCalled = true 81 | } 82 | 83 | ["ってなんですか", "てなんですか", "って何", "ってなに", "わかりますか", "なに", "なんですか"].forEach({ (suffix) in 84 | if input.hasSuffix(suffix) { 85 | isFinal = true 86 | var word = input.replacingOccurrences(of: suffix, with: "") 87 | self.recognitionRequest?.endAudio() 88 | 89 | if let index = word.lastIndex(of: "ボス") { 90 | word = word.substring(from: word.index(word.startIndex, offsetBy: index)).replacingOccurrences(of: "ボス", with: "") 91 | } 92 | 93 | if word.hasPrefix("これ") { 94 | self.delegate?.listenerDidDetectCaptureRequest(self) 95 | } else { 96 | self.delegate?.listener(self, didDetectWordToLookUp: word) 97 | } 98 | } 99 | }) 100 | } 101 | 102 | // TODO: 60秒以上経過した場合の処理 103 | if error != nil || isFinal { 104 | self.isMonitoring = false 105 | self.audioEngine.stop() 106 | inputNode.removeTap(onBus: 0) 107 | 108 | self.recognitionRequest = nil 109 | self.recognitionTask = nil 110 | } 111 | } 112 | 113 | let recordingFormat = inputNode.outputFormat(forBus: 0) 114 | inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in 115 | self.recognitionRequest?.append(buffer) 116 | } 117 | 118 | audioEngine.prepare() 119 | try audioEngine.start() 120 | } 121 | } 122 | 123 | extension Listener: SFSpeechRecognizerDelegate { 124 | func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) { 125 | debugPrint("SpeechRecognizer availabile: ", available) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /luckybeast/LuckyBeast.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Speech 3 | 4 | protocol LuckyBeastDelegate: class { 5 | func luckyBeast(_ luckyBeast: LuckyBeast, didChangeMode mode: LuckyBeast.Mode) 6 | func luckyBeast(_ luckyBeast: LuckyBeast, didUpdateBestTranscription transcription: String?) 7 | } 8 | class LuckyBeast: NSObject { 9 | enum Mode { 10 | case idle 11 | case launching 12 | case listening(nameCalled: Bool) 13 | case thinking 14 | case playing 15 | case panic 16 | 17 | var isListening: Bool { 18 | switch self { 19 | case .listening(_): 20 | return true 21 | default: 22 | return false 23 | } 24 | } 25 | } 26 | 27 | weak var delegate: LuckyBeastDelegate? 28 | 29 | fileprivate var mode: Mode = .idle { 30 | didSet { 31 | if oldValue != mode { 32 | delegate?.luckyBeast(self, didChangeMode: mode) 33 | vibrator.isOn = mode == .panic 34 | } 35 | } 36 | } 37 | 38 | fileprivate let speaker = Speaker() 39 | fileprivate let api: ServerAPI 40 | private let listener = Listener() 41 | fileprivate let capturer: Capturer 42 | fileprivate let vibrator = Vibrator() 43 | 44 | init(luckyBeastServerAPIEndpoint: String) { 45 | api = ServerAPI(endpoint: luckyBeastServerAPIEndpoint) 46 | capturer = Capturer() 47 | } 48 | 49 | func launch() { 50 | speaker.delegate = self 51 | listener.delegate = self 52 | capturer.delegate = self 53 | 54 | listener.requestAuthorization() 55 | 56 | self.mode = .launching 57 | let when = DispatchTime.now() + 2 58 | DispatchQueue.main.asyncAfter(deadline: when) { 59 | self.speaker.speak("はじめまして。僕はラッキービーストだよ。よろしくね。") { _ in 60 | self.startMonitoring() 61 | } 62 | } 63 | 64 | do { 65 | try capturer.start() 66 | } catch CapturerError.noFrontCamera { 67 | debugPrint("No front camera") 68 | } catch _ { 69 | fatalError("Unexpected error") 70 | } 71 | } 72 | 73 | fileprivate func lookUp(_ word: String, fromLanguage language: String? = nil) { 74 | mode = .thinking 75 | debugPrint("Looking: ", word) 76 | api.lookUp(word, fromLanguage: language) { result in 77 | switch result { 78 | case .success(let wordSummary): 79 | self.speaker.speak(wordSummary.summary) {_ in 80 | self.startMonitoring() 81 | } 82 | case .failure(let error): 83 | self.speaker.speak("あわわわ、あわわわ、あわわわ、あわわわ、わ、わ") {_ in 84 | self.startMonitoring() 85 | } 86 | debugPrint(error) 87 | self.mode = .panic 88 | } 89 | } 90 | } 91 | 92 | fileprivate func startMonitoring() { 93 | if mode.isListening { return } 94 | do { 95 | try listener.startMonitoring() 96 | mode = .listening(nameCalled: false) 97 | } catch _ { 98 | mode = .idle 99 | speaker.speak("モニタリングの開始に失敗しました") 100 | } 101 | } 102 | 103 | fileprivate func describeObject(in image: UIImage) { 104 | api.image(image) { result in 105 | switch result { 106 | case .success(let wordSummary): 107 | self.mode = .playing 108 | self.speaker.speak("これは\(wordSummary.word)だね。\(wordSummary.summary)") {_ in 109 | self.startMonitoring() 110 | } 111 | case .failure(let error): 112 | self.speaker.speak("あわわわ、あわわわ、あわわわ、あわわわ、わ、わ") {_ in 113 | self.startMonitoring() 114 | } 115 | debugPrint(error) 116 | self.mode = .panic 117 | } 118 | } 119 | } 120 | } 121 | 122 | extension LuckyBeast: SpeakerDelegate { 123 | func speaker(_ speaker: Speaker, didUpdateStatus isSpeaking: Bool) { 124 | debugPrint("isSpeaking: ", isSpeaking) 125 | } 126 | } 127 | 128 | extension LuckyBeast: ListenerDelegate { 129 | func listener(_ listener: Listener, didUpdateRecognizerAuthorizationStatus status: SFSpeechRecognizerAuthorizationStatus) { 130 | switch status { 131 | case .authorized: 132 | debugPrint("Ready") 133 | default: 134 | debugPrint("SpeechRecognizer Not ready.") 135 | } 136 | } 137 | 138 | func listener(_ listener: Listener, didDetectWordToLookUp word: String) { 139 | if !mode.isListening { return } 140 | lookUp(word) 141 | } 142 | 143 | func listenerDidDetectCaptureRequest(_ listener: Listener) { 144 | if !mode.isListening { return } 145 | if mode == .thinking { 146 | debugPrint("BUSY") 147 | return 148 | } 149 | 150 | mode = .thinking 151 | capturer.capture() 152 | } 153 | 154 | func listener(_ listener: Listener, didUpdateBestTranscription transcription: String?) { 155 | delegate?.luckyBeast(self, didUpdateBestTranscription: transcription) 156 | } 157 | 158 | func listenerDidDetectNameCalled(_ listener: Listener) { 159 | if !mode.isListening { return } 160 | mode = .listening(nameCalled: true) 161 | } 162 | } 163 | 164 | extension LuckyBeast: CapturerDelegate { 165 | func capturer(_ capturer: Capturer, didCaptureImage image: UIImage) { 166 | describeObject(in: image) 167 | } 168 | } 169 | 170 | func ==(a: LuckyBeast.Mode, b: LuckyBeast.Mode) -> Bool { 171 | switch (a, b) { 172 | case (.idle, .idle), 173 | (.launching, .launching), 174 | (.thinking, .thinking), 175 | (.playing, .playing), 176 | (.panic, .panic): 177 | return true 178 | case (let .listening(nameCalled1), let .listening(nameCalled2)): 179 | return nameCalled1 == nameCalled2 180 | default: 181 | return false 182 | } 183 | } 184 | func !=(a: LuckyBeast.Mode, b: LuckyBeast.Mode) -> Bool { 185 | return !(a == b) 186 | } 187 | -------------------------------------------------------------------------------- /luckybeast/ServerAPI.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Alamofire 3 | import enum Result.Result 4 | 5 | enum ServerError: Error { 6 | case invalidWord 7 | } 8 | 9 | struct WordSummary { 10 | var word: String 11 | var summary: String 12 | } 13 | 14 | class ServerAPI: NSObject { 15 | private var endpointBase: String 16 | init(endpoint: String) { 17 | endpointBase = endpoint 18 | } 19 | 20 | func lookUp(_ word: String, fromLanguage language: String?, completion: @escaping ((Result) -> ())) { 21 | var params = ["word": word] 22 | if let language = language { 23 | params["from"] = language 24 | } 25 | Alamofire.request("\(endpointBase)/wikipedia/search", parameters: params).responseJSON { response in 26 | switch response.result { 27 | case .success(let json): 28 | if let dictionary = json as? [AnyHashable: Any], let summary = dictionary["summary"] as? String, let translatedWord = dictionary["word"] as? String { 29 | completion(.success(WordSummary(word: translatedWord, summary: summary))) 30 | } else { 31 | completion(.failure(ServerError.invalidWord as NSError)) 32 | } 33 | case .failure(let error): 34 | completion(.failure(error as NSError)) 35 | } 36 | } 37 | } 38 | 39 | func image(_ image: UIImage, completion: @escaping ((Result) -> ())) { 40 | Alamofire.upload(multipartFormData: { multipartFormData in 41 | multipartFormData.append(UIImageJPEGRepresentation(image, 0.7)!, withName: "file", fileName: "captured.jpg", mimeType: "image/jpeg") 42 | }, to: "\(endpointBase)/wikipedia/from_image", encodingCompletion: { (encodingResult) in 43 | switch encodingResult { 44 | case .success(let upload, _, _): 45 | upload.responseJSON { response in 46 | switch response.result { 47 | case .success(let json): 48 | if let dictionary = json as? [AnyHashable: Any], let summary = dictionary["summary"] as? String, let translatedWord = dictionary["word"] as? String { 49 | completion(.success(WordSummary(word: translatedWord, summary: summary))) 50 | } else { 51 | completion(.failure(ServerError.invalidWord as NSError)) 52 | } 53 | case .failure(let error): 54 | completion(.failure(error as NSError)) 55 | } 56 | } 57 | case .failure(let encodingError): 58 | completion(.failure(encodingError as NSError)) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /luckybeast/Speaker.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import AVFoundation 3 | 4 | protocol SpeakerDelegate: class { 5 | func speaker(_ speaker: Speaker, didUpdateStatus isSpeaking: Bool) 6 | } 7 | 8 | class Speaker: NSObject { 9 | typealias CompletionBlock = ((Bool) -> ()) 10 | private let synthesizer = AVSpeechSynthesizer() 11 | 12 | var isSpeaking = false { 13 | didSet { 14 | delegate?.speaker(self, didUpdateStatus: isSpeaking) 15 | } 16 | } 17 | weak var delegate: SpeakerDelegate? 18 | var completionBlock: CompletionBlock? 19 | 20 | override init() { 21 | super.init() 22 | synthesizer.delegate = self 23 | } 24 | 25 | func speak(_ message: String, completion: CompletionBlock? = nil) { 26 | let audioSession = AVAudioSession.sharedInstance() 27 | try? audioSession.setCategory(AVAudioSessionCategoryAmbient) 28 | synthesizer.stopSpeaking(at: .immediate) 29 | completionBlock?(false) 30 | 31 | completionBlock = completion 32 | 33 | let utterance = AVSpeechUtterance(string: message) 34 | utterance.pitchMultiplier = 1.2 35 | utterance.voice = AVSpeechSynthesisVoice(language: "ja-JP") 36 | 37 | debugPrint("Speak: ", message) 38 | isSpeaking = true 39 | synthesizer.speak(utterance) 40 | } 41 | 42 | func stop() { 43 | synthesizer.stopSpeaking(at: .immediate) 44 | } 45 | } 46 | 47 | extension Speaker: AVSpeechSynthesizerDelegate { 48 | func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) { 49 | isSpeaking = false 50 | completionBlock?(true) 51 | completionBlock = nil 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /luckybeast/Vibrator.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import AudioToolbox 3 | 4 | class Vibrator { 5 | var isOn = false { 6 | didSet { 7 | if oldValue != isOn { 8 | if isOn { 9 | startVibration() 10 | } else { 11 | stopVibration() 12 | } 13 | } 14 | } 15 | } 16 | 17 | private var timer: Timer? 18 | private func startVibration() { 19 | debugPrint("Start vibration") 20 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in 21 | AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) 22 | }) 23 | } 24 | 25 | private func stopVibration() { 26 | debugPrint("Stop vibration") 27 | timer?.invalidate() 28 | timer = nil 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /luckybeast/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import UIView_Shake 3 | 4 | class ViewController: UIViewController { 5 | private let isSpeechRecognizerDebugEnabled = false 6 | fileprivate let boss = LuckyBeast(luckyBeastServerAPIEndpoint: Const.luckyBeastServerAPIEndpoint) 7 | 8 | @IBOutlet weak var label: UILabel! 9 | @IBOutlet weak var coreView: BossCoreView! 10 | @IBOutlet weak var leftEye: EyeView! 11 | @IBOutlet weak var rightEye: EyeView! 12 | @IBOutlet weak var beltView: UIView! 13 | 14 | private var displayMode: DisplayMode = .phone { 15 | didSet { 16 | updateViews() 17 | } 18 | } 19 | 20 | override func viewWillAppear(_ animated: Bool) { 21 | super.viewWillAppear(animated) 22 | 23 | displayMode = AppSettings.shared.displayMode ?? .phone 24 | } 25 | 26 | public override func viewDidAppear(_ animated: Bool) { 27 | super.viewDidAppear(animated) 28 | 29 | boss.delegate = self 30 | boss.launch() 31 | 32 | label.isHidden = !isSpeechRecognizerDebugEnabled 33 | } 34 | 35 | override var prefersStatusBarHidden: Bool { 36 | return true 37 | } 38 | 39 | @IBAction func onDoubleTapScreen(_ sender: Any) { 40 | displayMode = displayMode == .phone ? .costume : .phone 41 | AppSettings.shared.displayMode = displayMode 42 | 43 | updateViews() 44 | } 45 | 46 | private func updateViews() { 47 | guard let backgroundView: BackgroundView = view as? BackgroundView else { 48 | fatalError("view is not an instance of BackgroundView.") 49 | } 50 | 51 | switch displayMode { 52 | case .costume: 53 | leftEye.isHidden = true 54 | rightEye.isHidden = true 55 | beltView.isHidden = true 56 | backgroundView.mode = .plain 57 | case .phone: 58 | leftEye.isHidden = false 59 | rightEye.isHidden = false 60 | beltView.isHidden = false 61 | backgroundView.mode = .texture 62 | } 63 | } 64 | } 65 | 66 | 67 | 68 | extension ViewController: LuckyBeastDelegate { 69 | func luckyBeast(_ luckyBeast: LuckyBeast, didChangeMode mode: LuckyBeast.Mode) { 70 | let status: EyeView.Status 71 | switch mode { 72 | case .idle, .launching, .playing, .thinking: 73 | status = .normal 74 | case .panic: 75 | status = .white 76 | self.view.shake(200, withDelta: 4.0) 77 | case .listening(let isNameCalled): 78 | status = isNameCalled ? .shining : .normal 79 | } 80 | 81 | leftEye.status = status 82 | rightEye.status = status 83 | 84 | coreView.mode = mode 85 | } 86 | 87 | func luckyBeast(_ luckyBeast: LuckyBeast, didUpdateBestTranscription transcription: String?) { 88 | label.text = transcription 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /luckybeastTests/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 | -------------------------------------------------------------------------------- /luckybeastTests/luckybeastTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import luckybeast 3 | 4 | class luckybeastTests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | // Use XCTAssert and related functions to verify your tests produce the correct results. 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | --------------------------------------------------------------------------------