├── Cartfile ├── Cartfile.resolved ├── Blobfish_icon.png ├── codecov.yml ├── Blobfish.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── kasperwelner.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── xcshareddata │ └── xcschemes │ │ └── Blobfish.xcscheme └── project.pbxproj ├── Blobfish ├── Blobfish.h ├── Classes │ ├── Blobable.swift │ ├── LayoutUtils.swift │ ├── Blob.swift │ ├── MessageBar.swift │ ├── AlamofireBlobfishExtension.swift │ ├── Blobfish.swift │ └── Reachability.swift └── Info.plist ├── LICENSE ├── .travis.yml ├── .gitignore └── README.md /Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" ~> 4.1 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.8.2" 2 | -------------------------------------------------------------------------------- /Blobfish_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ml-archive/Blobfish/master/Blobfish_icon.png -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | patch: false 4 | ignore: 5 | - "BlobfishTests/.*" 6 | -------------------------------------------------------------------------------- /Blobfish.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Blobfish.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Blobfish/Blobfish.h: -------------------------------------------------------------------------------- 1 | // 2 | // Blobfish.h 3 | // Blobfish 4 | // 5 | // Created by Kasper Welner on 13/03/16. 6 | // Copyright © 2016 Nodes. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Blobfish. 12 | FOUNDATION_EXPORT double BlobfishVersionNumber; 13 | 14 | //! Project version string for Blobfish. 15 | FOUNDATION_EXPORT const unsigned char BlobfishVersionString[]; -------------------------------------------------------------------------------- /Blobfish/Classes/Blobable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorRepresentable.swift 3 | // Blobfish 4 | // 5 | // Created by Kasper Welner on 28/02/16. 6 | // Copyright © 2016 Nodes ApS. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | If a class or struct conforms to *Blobable*, it can return a *Blob* and thus 13 | be used by Blobfish to display an error message. 14 | */ 15 | 16 | public protocol Blobable { 17 | var blob:Blob? { get } 18 | } -------------------------------------------------------------------------------- /Blobfish.xcodeproj/xcuserdata/kasperwelner.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Blobfish.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 30BA21131C957E3100DC883C 16 | 17 | primary 18 | 19 | 20 | 30BA211D1C957E3100DC883C 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Blobfish/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 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nodes Agency - iOS 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 | -------------------------------------------------------------------------------- /Blobfish/Classes/LayoutUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutUtils.swift 3 | // Blobfish 4 | // 5 | // Created by Andrew Lloyd - Nodes on 06/02/2018. 6 | // Copyright © 2018 Nodes. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct LayoutUtils { 12 | 13 | public static func extraLabelHeightForMessageBar() -> CGFloat { 14 | //16.0 is the height of the label in the message bar 15 | return safeAreaTop() > 0.0 ? 16.0 : 0.0 16 | } 17 | 18 | public static func safeAreaTop() -> CGFloat { 19 | if #available(iOS 11.0, *) { 20 | if let window = UIApplication.shared.keyWindow { 21 | return window.safeAreaInsets.top 22 | } 23 | } 24 | 25 | return 0.0 26 | } 27 | 28 | public static func hasTopNotch() -> Bool { 29 | if #available(iOS 11.0, tvOS 11.0, *) { 30 | // with notch: 44.0 on iPhone X, XS, XS Max, XR. 31 | // without notch: 24.0 on iPad Pro 12.9" 3rd generation, 20.0 on iPhone 8 on iOS 12+. 32 | return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 24 33 | } 34 | return false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode10.1 3 | branches: 4 | only: 5 | - master 6 | 7 | env: 8 | global: 9 | - PROJECT_NAME="Blobfish.xcodeproj" 10 | - IOS_FRAMEWORK_SCHEME="Blobfish" 11 | 12 | - IOS_SDK=iphonesimulator12.1 13 | 14 | matrix: 15 | - DESTINATION="OS=10.1,name=iPhone 6S Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="NO" CARTHAGE_PLATFORM="ios" 16 | 17 | install: 18 | # Setup carthage dependencies before build 19 | - carthage bootstrap --platform $CARTHAGE_PLATFORM 20 | 21 | script: 22 | - set -o pipefail 23 | 24 | # Build Framework in Debug and Run Tests if specified 25 | - if [ $RUN_TESTS == "YES" ]; then 26 | xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug test | xcpretty; 27 | else 28 | xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty; 29 | fi 30 | 31 | - xcodebuild -project "$PROJECT_NAME" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty; 32 | after_success: 33 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /Blobfish/Classes/Blob.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // Blobfish 4 | // 5 | // Created by Kasper Welner on 13/03/16. 6 | // Copyright © 2016 Nodes. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /** 13 | A *Blob* is an entitiy that encapsulates all the information needed 14 | by Blobfish to show a meaningful error message. This includes text, style, 15 | button titles and actions. 16 | */ 17 | 18 | public struct Blob { 19 | /** 20 | The displayed title. if *style* is *.Overlay*, this will be all the user sees. 21 | */ 22 | public let title: String 23 | 24 | /** 25 | The display style of the overlay. 26 | */ 27 | public let style: Style 28 | 29 | /** 30 | The default implementation of Blobfish shows a status bar overlay for *.Overlay* 31 | and a native *UIAlertController* alert for the *.Alert* case. 32 | */ 33 | public enum Style { 34 | case overlay 35 | case alert(message:String?, actions: [AlertAction]) 36 | } 37 | 38 | public struct AlertAction { 39 | public typealias Handler = (() -> Void) 40 | 41 | public let title: String 42 | public let handler: Handler? 43 | 44 | public init(title: String, handler: Handler?) { 45 | self.title = title 46 | self.handler = handler 47 | } 48 | } 49 | 50 | public init(title: String, style: Style) { 51 | self.title = title 52 | self.style = style 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .DS_Store 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 | *.xccheckout 24 | *.moved-aside 25 | *.xcuserstate 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 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 | Carthage/ 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/screenshots 65 | -------------------------------------------------------------------------------- /Blobfish/Classes/MessageBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorBar.swift 3 | // NOCore 4 | // 5 | // Created by Kasper Welner on 24/10/15. 6 | // Copyright © 2015 Nodes. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class MessageBar: UIWindow { 12 | public let label = UILabel(frame: CGRect.zero) 13 | 14 | override init(frame: CGRect) { 15 | super.init(frame: frame) 16 | 17 | self.translatesAutoresizingMaskIntoConstraints = false 18 | 19 | label.translatesAutoresizingMaskIntoConstraints = false 20 | label.frame.origin.y = LayoutUtils.safeAreaTop() 21 | label.frame.size.height = 18.0 22 | self.addSubview(label) 23 | 24 | label.textAlignment = NSTextAlignment.center 25 | label.backgroundColor = UIColor.clear 26 | label.adjustsFontSizeToFitWidth = true 27 | if #available(iOS 9, *) { 28 | self.label.allowsDefaultTighteningForTruncation = true 29 | } 30 | label.lineBreakMode = NSLineBreakMode.byTruncatingTail 31 | label.numberOfLines = 1 32 | label.textColor = UIColor.white 33 | label.font = UIFont.preferredFont(forTextStyle: .callout) 34 | 35 | backgroundColor = UIColor.red 36 | isHidden = true 37 | windowLevel = UIWindow.Level.statusBar+1; 38 | } 39 | 40 | public override func layoutSubviews() { 41 | 42 | label.frame = self.bounds.insetBy(dx: 8, dy: 0) 43 | label.frame.origin.y = LayoutUtils.safeAreaTop() 44 | label.frame.size.height = 18.0 45 | 46 | if LayoutUtils.hasTopNotch() { 47 | label.center = CGPoint(x: center.x, y: center.y + 14) 48 | } else { 49 | label.center = self.center 50 | } 51 | 52 | } 53 | 54 | required public init?(coder aDecoder: NSCoder) { 55 | fatalError("init(coder:) has not been implemented") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### This library has been deprecated and the repo has been archived. 2 | ### The code is still here and you can still clone it, however the library will not receive any more updates or support. 3 | 4 |

5 |   Blobfish 6 |

7 | 8 | Easily handle errors and present them to the user in a nice way. 9 | [![Travis](https://travis-ci.org/nodes-ios/Blobfish.svg?branch=master)](https://travis-ci.org/nodes-ios/Blobfish) 10 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 11 | ![Plaform](https://img.shields.io/badge/platform-iOS-lightgrey.svg) 12 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nodes-ios/Policeman/blob/master/LICENSE) 13 | 14 | ## 📦 Installation 15 | 16 | ### Carthage 17 | ~~~bash 18 | github "nodes-ios/Blobfish" ~> 1.0 19 | ~~~ 20 | 21 | > Last versions compatible with lower Swift versions: 22 | > 23 | > **Swift 2.3** 24 | > `github "nodes-ios/Blobfish" == 0.2.0` 25 | > 26 | > **Swift 2.2** 27 | > `github "nodes-ios/Blobfish" == 0.1.2` 28 | 29 | ## 🔧 Setup 30 | 31 | #### Blob & Blobbable 32 | 33 | > **TODO:** Add instructions 34 | 35 | #### Alamofire Extension 36 | 37 | In your AppDelegate's `applicationDidFinishLaunching:launchOptions:` function first do the basic setup of Blobfish: 38 | 39 | ```swift 40 | Blobfish.AlamofireConfig.blobForTokenExpired = { 41 | let action = Blob.AlertAction(title: "Ok", handler: { 42 | // Your custom actions on token expired go here 43 | }) 44 | return Blob(title: "Token Expired", 45 | style: .Alert(message: "Your token has expired. Please log in again.", actions: [action])) 46 | } 47 | 48 | Blobfish.AlamofireConfig.blobForUnknownError = { _, _ in 49 | let action = Blob.AlertAction(title: "Ok", handler: nil) 50 | return Blob(title: "Uknown Error", 51 | style: .Alert(message: "Unknown error happened, please try again.", actions: [action])) 52 | } 53 | 54 | Blobfish.AlamofireConfig.blobForConnectionError = { _ in 55 | return Blob(title: "Connection error, please try again.", style: .Overlay) 56 | } 57 | ``` 58 | 59 | There is an extension to Alamofire `Response` to make it adhere to the `Blobbable` protocol, so handling errors in your callbacks should be a breeze. 60 | 61 | ```swift 62 | func doSomeRequest(completion: Response -> Void) { ... } 63 | // ... 64 | doSomeRequest(completion: { response in 65 | switch response.result { 66 | case .Failure(_): 67 | // First, handle your custom error codes manually 68 | if response.response?.statusCode == 870 { 69 | // Your code to handle a custom error code 70 | } else { 71 | // Fallback to Blobfish 72 | Blobfish.sharedInstance.handle(response) 73 | } 74 | default: break 75 | }) 76 | 77 | ``` 78 | 79 | 80 | ## 👥 Credits 81 | Made with ❤️ at [Nodes](http://nodesagency.com). 82 | 83 | ## 📄 License 84 | **Blobfish** is available under the MIT license. See the [LICENSE](https://github.com/nodes-ios/Blobfish/blob/master/LICENSE) file for more info. 85 | -------------------------------------------------------------------------------- /Blobfish.xcodeproj/xcshareddata/xcschemes/Blobfish.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Blobfish/Classes/AlamofireBlobfishExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSURLResponse+ErrorRepresentable.swift 3 | // Blobfish 4 | // 5 | // Created by Kasper Welner on 28/02/16. 6 | // Copyright © 2016 Nodes ApS. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Alamofire 11 | 12 | private enum ErrorCode: Int { 13 | case zero = 0 14 | case noConnection = 4096 15 | case notConnectedToInternet = -1009 16 | case networkConnectionLost = -1005 17 | case parsingFailed = 2048 18 | case clientTimeOut = -1001 19 | case badRequest = 400 20 | case unauthorized = 401 21 | case forbidden = 403 22 | case notFound = 404 23 | case preconditionFailed = 412 24 | case tooManyRequests = 429 25 | case noAcceptHeader = 440 26 | case noToken = 441 27 | case invalidToken = 442 28 | case expiredToken = 443 29 | case invalid3rdPartyToken = 444 30 | case entityNotFound = 445 31 | case blockedUser = 447 32 | case internalServerError = 500 33 | case notImplemented = 501 34 | case badGateway = 502 35 | case serviceUnavailable = 503 36 | case gatewayTimeout = 504 37 | case unknownError = -1 38 | } 39 | 40 | //This abomination exists because you cannot extend a generic class with static variables yet 41 | extension Blobfish { 42 | 43 | public struct AlamofireConfig { 44 | 45 | /** 46 | Gets the message and titles useful for showing in connection error alert view. 47 | 48 | - returns: A tuple containing message text and alert style. For .Alert style, please pass along text string for 'OK' and optionally 'Retry'. If retry string is nil, alert will only show OK button. 49 | */ 50 | 51 | 52 | public static var blobForConnectionError:(_ code:Int) -> Blob? = { code in 53 | print("Warning! Please assign values to all 'messageFor***' static properties on AlamofireBlobfishConfiguration.. Using default values...") 54 | var title = "_Something went wrong. Please check your connection and try again" 55 | 56 | return Blob(title:title, style: .overlay) 57 | } 58 | 59 | /** 60 | Gets the message and titles for showing unkown error alert view. 61 | 62 | - returns: A tuple containing message text and ok text. 63 | */ 64 | 65 | 66 | public static var blobForUnknownError:(_ code:Int, _ localizedStringForCode:String) -> Blob? = { (code, localizedStringForCode) in 67 | print("Warning! Please assign values to all 'messageFor***' static properties on AlamofireBlobfishConfiguration.. Using default values...") 68 | let title = "_An error occured" 69 | let action = Blob.AlertAction(title: "OK", handler: nil) 70 | return Blob(title: title, style: .alert(message:"(\(code) " + localizedStringForCode + ")", actions: [action])) 71 | } 72 | 73 | /** 74 | Gets the message and titles for showing token expired/missing error alert view. 75 | 76 | - returns: A tuple containing message text and ok text. 77 | */ 78 | 79 | 80 | public static var blobForTokenExpired:() -> Blob? = { 81 | var title = "_You session has expired. Please log in again" 82 | fatalError("errorForTokenExpired is not set on AlamofireBlobfishConfiguration") 83 | } 84 | 85 | /** 86 | This is used if the API you're consuming has set up global error codes. 87 | 88 | **Example:** You api returns *441* whenever you try to make a call with an expired token. 89 | You want to tell the user and log him out, so you return [441 : ErrorCategory.Token]. 90 | 91 | Both HTTP response codes and NSError codes can be specified. 92 | 93 | - note: Error codes unique for specific endpoints should be handled BEFORE passing 94 | the response to Blobfish. 95 | 96 | - returns: A dictionary whose keys are error codes and values are ErrorCategories. 97 | */ 98 | 99 | public static var customStatusCodeMapping:() -> [Int : ErrorCategory] = { 100 | return [:] 101 | } 102 | 103 | public enum ErrorCategory { 104 | case connection 105 | case token 106 | case unknown 107 | case none 108 | } 109 | } 110 | } 111 | 112 | extension Alamofire.DataResponse: Blobable { 113 | 114 | /** 115 | This Blobfish extension allows you to pass a Response object to Blobfish. 116 | It splits the response up in 4 different types: 117 | 118 | - *Connection* - shown as overlay 119 | - *Unknown* - shown as Alert. 120 | - *Token invalid/expired* - shown as alert. 121 | - *None* - show nothing 122 | 123 | Please add the appropriate strings and actions to the AlamofireResponseConfiguration object. 124 | */ 125 | 126 | public var blob:Blob? { 127 | 128 | guard case let .failure(resultError) = result else { return nil } 129 | 130 | let errorCode = (resultError as NSError).code 131 | let statusCode = response?.statusCode ?? errorCode 132 | 133 | switch (self.errorCategory) { 134 | 135 | case .none: 136 | return nil 137 | 138 | case .token: 139 | return Blobfish.AlamofireConfig.blobForTokenExpired() 140 | 141 | case .connection: 142 | return Blobfish.AlamofireConfig.blobForConnectionError(statusCode ) 143 | 144 | default: 145 | var localizedMessageForStatusCode:String = "" 146 | localizedMessageForStatusCode = HTTPURLResponse.localizedString(forStatusCode: statusCode) 147 | 148 | return Blobfish.AlamofireConfig.blobForUnknownError(statusCode , localizedMessageForStatusCode) 149 | } 150 | } 151 | 152 | /** 153 | A overall classification of the response (and error), assigning it to an *ErrorCategory* case. 154 | 155 | - returns: The type of error 156 | */ 157 | 158 | public var errorCategory:Blobfish.AlamofireConfig.ErrorCategory { 159 | 160 | guard case let .failure(resultError) = result else { return .none } 161 | 162 | let errorCode = (resultError as NSError).code 163 | let statusCode = response?.statusCode ?? errorCode 164 | 165 | if let customMapping = Blobfish.AlamofireConfig.customStatusCodeMapping()[statusCode] { 166 | return customMapping 167 | } 168 | 169 | let apiError = ErrorCode(rawValue: statusCode ) ?? .unknownError 170 | switch (apiError) { 171 | 172 | case .unauthorized, .forbidden: 173 | return .token 174 | 175 | case .noConnection, .zero, .clientTimeOut, .notConnectedToInternet, .networkConnectionLost, .invalid3rdPartyToken: 176 | return .connection 177 | 178 | default: 179 | return .unknown 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Blobfish/Classes/Blobfish.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Policeman.swift 3 | // NOCore 4 | // 5 | // Created by Chris Combs/Kasper Welner on 27/07/15. 6 | // Copyright (c) 2015 Nodes. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Alamofire 11 | 12 | /** 13 | Blobfish can present general error messages related to URL Requests in a meaningful way. Pass an object conforming to 14 | the *Blobable* protocol to it whenever you have a request that fails with a non-endpoint-specific error. 15 | */ 16 | public class Blobfish { 17 | 18 | public typealias ErrorHandlerAlertCompletion = (_ retryButtonClicked:Bool) -> Void 19 | public typealias ErrorHandlerShowAlertBlock = (_ title:String, _ message:String?, _ actions:[Blob.AlertAction]) -> Void 20 | 21 | public static let sharedInstance = Blobfish() 22 | 23 | var reachabilityManager: NetworkReachabilityManager? 24 | 25 | //safe area top added for iphone x so message bar isnt displayed under the notch 26 | lazy var overlayBar = MessageBar(frame: CGRect.init(x: UIApplication.shared.statusBarFrame.origin.x, y: UIApplication.shared.statusBarFrame.origin.y, width: UIApplication.shared.statusBarFrame.size.width, height: UIApplication.shared.statusBarFrame.size.height + LayoutUtils.extraLabelHeightForMessageBar())) 27 | 28 | var alertWindow = UIWindow(frame: UIScreen.main.bounds) { 29 | didSet { 30 | alertWindow.windowLevel = UIWindow.Level.alert + 1 31 | } 32 | } 33 | 34 | var alreadyShowingAlert: Bool { 35 | return Blobfish.sharedInstance.alertWindow.isHidden == false 36 | } 37 | 38 | /** 39 | The content of this closure is responsible for showing the UI for an error whose style is MessageStyle.Alert. The default value shows a native alert using UIAlertController. 40 | 41 | Override this to use a custom alert for your app. 42 | */ 43 | 44 | public var showAlertBlock: ErrorHandlerShowAlertBlock = { 45 | (title, message, actions) in 46 | 47 | let alert = UIAlertController(title: title, message:message, preferredStyle: UIAlertController.Style.alert) 48 | for action in actions { 49 | alert.addAction(UIAlertAction(title: action.title, style: .default, handler: { (_) in 50 | Blobfish.hideAlertWindow() 51 | action.handler?() 52 | })) 53 | } 54 | 55 | Blobfish.sharedInstance.presentViewController(alert) 56 | 57 | } 58 | 59 | /** 60 | The content of this closure is responsible for showing the UI for an error whose style is Overlay. The default value shows a native alert using UIAlertController. 61 | 62 | Override this to use a custom alert for your app. 63 | 64 | If you want to customize the appearance of the overlay bar, see the overlayBarConfiguration property. 65 | */ 66 | 67 | public var showOverlayBlock: (_ title:String) -> Void = { message in 68 | Blobfish.sharedInstance.overlayBar.label.text = message 69 | Blobfish.sharedInstance.showOverlayBar() 70 | } 71 | 72 | /** 73 | The content of this closure is responsible for showing the UI for an error whose style is Overlay. The default value shows a native alert using UIAlertController. 74 | 75 | Override this to use a custom alert for your app. 76 | 77 | If you want to customize the appearance of the overlay bar, see the overlayBarConfiguration property. 78 | */ 79 | 80 | public var overlayBarConfiguration:((_ bar:MessageBar) -> Void)? 81 | 82 | // MARK: - Init & Deinit - 83 | 84 | private init() { 85 | setupReachability() 86 | 87 | NotificationCenter.default.addObserver(self, 88 | selector: #selector(Blobfish.aCallWentThrough(_:)), 89 | name: NSNotification.Name(rawValue: "APICallSucceededNotification"), 90 | object: nil) 91 | } 92 | 93 | deinit { 94 | NotificationCenter.default.removeObserver(self) 95 | } 96 | 97 | // MARK: - Reachability - 98 | 99 | private func setupReachability() { 100 | reachabilityManager = NetworkReachabilityManager(host: "http://google.com") 101 | reachabilityManager?.listener = { state in 102 | switch state { 103 | case .reachable(_): 104 | self.hideOverlayBar() 105 | default: 106 | break 107 | } 108 | } 109 | reachabilityManager?.startListening() 110 | } 111 | 112 | // MARK: - Alert - 113 | 114 | /** 115 | This method can be used for presenting a custom viewcontroller while still using the Blobfish boilerplate code for keeping track of already presented alerts. IMPORTANT: If this method is used you must call Blobfish.hideAlertWindow() somewhere in every alert action to regain interaction with app 116 | */ 117 | 118 | public func presentViewController(_ viewController:UIViewController) { 119 | 120 | if Blobfish.sharedInstance.alertWindow.rootViewController == nil { 121 | Blobfish.sharedInstance.alertWindow.rootViewController = UIViewController() 122 | } 123 | 124 | Blobfish.sharedInstance.alertWindow.makeKeyAndVisible() 125 | Blobfish.sharedInstance.alertWindow.rootViewController!.present(viewController, animated: true, completion: nil) 126 | } 127 | 128 | /** 129 | This method is used for manually hiding the window used for displaying alerts. MUST be called after dismissing a custom viewcontroller presented with Blobfish.sharedInstance.presentViewController(viewController:UIViewController) 130 | */ 131 | 132 | public static func hideAlertWindow() { 133 | Blobfish.sharedInstance.alertWindow.isHidden = true 134 | } 135 | 136 | //MARK: - Overlay - 137 | 138 | private func showOverlayBar() { 139 | overlayBarConfiguration?(Blobfish.sharedInstance.overlayBar) 140 | 141 | if (self.overlayBar.isHidden) { // Not already shown 142 | // Do not re-animate 143 | self.overlayBar.frame.origin.y = -overlayBar.frame.height 144 | } 145 | 146 | self.overlayBar.isHidden = false 147 | 148 | UIView.animate(withDuration: 0.5, animations: { () -> Void in 149 | self.overlayBar.frame.origin.y = 0 150 | }) { (finished) -> Void in 151 | 152 | self.statusBarDidChangeFrame() 153 | } 154 | } 155 | 156 | public func hideOverlayBar(_ animated:Bool = true) { 157 | 158 | if !animated || overlayBar.isHidden == true { 159 | self.overlayBar.isHidden = true 160 | return 161 | } 162 | 163 | self.overlayBar.isHidden = false 164 | 165 | UIView.animate(withDuration: 0.5, delay: 0.0, options: UIView.AnimationOptions.beginFromCurrentState, animations: { () -> Void in 166 | 167 | self.overlayBar.frame.origin.y = -self.overlayBar.frame.size.height 168 | 169 | }) { (finished) -> Void in 170 | 171 | self.overlayBar.isHidden = true 172 | } 173 | } 174 | 175 | // MARK: - Notifications - 176 | 177 | private func statusBarDidChangeFrame(_ note: Notification) { 178 | statusBarDidChangeFrame() 179 | } 180 | 181 | public func statusBarDidChangeFrame() { 182 | let orientation = UIApplication.shared.statusBarOrientation 183 | 184 | self.overlayBar.transform = transformForOrientation(orientation) 185 | 186 | //status bar frame with safe area layout 187 | var frame = CGRect.init(x: UIApplication.shared.statusBarFrame.origin.x, y: UIApplication.shared.statusBarFrame.origin.y, width: UIApplication.shared.statusBarFrame.size.width, height: UIApplication.shared.statusBarFrame.size.height + LayoutUtils.extraLabelHeightForMessageBar()) 188 | 189 | if orientation.isLandscape { 190 | frame = frame.rectByReversingSize() 191 | if UIDevice.current.userInterfaceIdiom == .phone { 192 | frame.origin.x = frame.size.width - frame.origin.x 193 | } 194 | else if orientation == UIInterfaceOrientation.landscapeRight { 195 | if let width = UIApplication.shared.keyWindow?.bounds.height { 196 | frame.origin.x = width - frame.size.width 197 | } 198 | } 199 | } 200 | 201 | 202 | self.overlayBar.frame = frame 203 | } 204 | 205 | @objc func aCallWentThrough(_ note: Notification) { 206 | DispatchQueue.main.async(execute: { 207 | if self.reachabilityManager?.isReachable == true { 208 | self.hideOverlayBar() 209 | } 210 | }) 211 | } 212 | 213 | 214 | // MARK: - Blob Handling - 215 | 216 | /** 217 | Takes a *Blobable* object and displays an error message according to the *blob* returned by the object. 218 | 219 | - parameter blobable: An instance conforming to *Blobable* 220 | */ 221 | 222 | public func handle(_ blobable:Blobable) { 223 | guard let blob = blobable.blob else { return } 224 | 225 | switch (blob.style) { 226 | case .overlay: 227 | showOverlayBlock(blob.title) 228 | 229 | case let .alert(message, actions): 230 | showAlertBlock(blob.title, message, actions) 231 | } 232 | } 233 | 234 | // MARK: - Utils - 235 | 236 | private func degreesToRadians(_ degrees: CGFloat) -> CGFloat { 237 | return (degrees * CGFloat(Double.pi) / CGFloat(180.0)) 238 | } 239 | 240 | private func transformForOrientation(_ orientation: UIInterfaceOrientation) -> CGAffineTransform { 241 | 242 | switch (orientation) { 243 | 244 | case UIInterfaceOrientation.landscapeLeft: 245 | return CGAffineTransform(rotationAngle: -degreesToRadians(90)) 246 | 247 | case UIInterfaceOrientation.landscapeRight: 248 | return CGAffineTransform(rotationAngle: degreesToRadians(90)) 249 | 250 | case UIInterfaceOrientation.portraitUpsideDown: 251 | return CGAffineTransform(rotationAngle: degreesToRadians(180)) 252 | 253 | default: 254 | return CGAffineTransform(rotationAngle: degreesToRadians(0)) 255 | } 256 | } 257 | } 258 | 259 | internal extension CGRect { 260 | func rectByReversingSize() -> CGRect { 261 | return CGRect(origin: self.origin, size: CGSize(width: self.size.height, height: self.size.width)) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Blobfish/Classes/Reachability.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Ashley Mills 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | // Reachability.swift version 2.2beta2 29 | 30 | import SystemConfiguration 31 | import Foundation 32 | 33 | public enum ReachabilityError: ErrorProtocol { 34 | case FailedToCreateWithAddress(sockaddr_in) 35 | case FailedToCreateWithHostname(String) 36 | case UnableToSetCallback 37 | case UnableToSetDispatchQueue 38 | } 39 | 40 | public let ReachabilityChangedNotification = "ReachabilityChangedNotification" as NSNotification.Name 41 | 42 | 43 | func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer?) { 44 | 45 | guard let info = info else { return } 46 | 47 | let reachability = Unmanaged.fromOpaque(OpaquePointer(info)).takeUnretainedValue() 48 | 49 | DispatchQueue.main.async { 50 | reachability.reachabilityChanged(flags:flags) 51 | } 52 | } 53 | 54 | public class Reachability: NSObject { 55 | 56 | public typealias NetworkReachable = (Reachability) -> () 57 | public typealias NetworkUnreachable = (Reachability) -> () 58 | 59 | public enum NetworkStatus: CustomStringConvertible { 60 | 61 | case NotReachable, ReachableViaWiFi, ReachableViaWWAN 62 | 63 | public var description: String { 64 | switch self { 65 | case .ReachableViaWWAN: 66 | return "Cellular" 67 | case .ReachableViaWiFi: 68 | return "WiFi" 69 | case .NotReachable: 70 | return "No Connection" 71 | } 72 | } 73 | } 74 | 75 | // MARK: - *** Public properties *** 76 | public var whenReachable: NetworkReachable? 77 | public var whenUnreachable: NetworkUnreachable? 78 | public var reachableOnWWAN: Bool 79 | public var notificationCenter = NotificationCenter.default 80 | 81 | public var currentReachabilityStatus: NetworkStatus { 82 | if isReachable() { 83 | if isReachableViaWiFi() { 84 | return .ReachableViaWiFi 85 | } 86 | if isRunningOnDevice { 87 | return .ReachableViaWWAN 88 | } 89 | } 90 | return .NotReachable 91 | } 92 | 93 | public var currentReachabilityString: String { 94 | return "\(currentReachabilityStatus)" 95 | } 96 | 97 | private var previousFlags: SCNetworkReachabilityFlags? 98 | 99 | // MARK: - *** Initialisation methods *** 100 | 101 | required public init(reachabilityRef: SCNetworkReachability) { 102 | reachableOnWWAN = true 103 | self.reachabilityRef = reachabilityRef 104 | } 105 | 106 | public convenience init(hostname: String) throws { 107 | 108 | guard let nodename = (hostname as NSString).utf8String, 109 | ref = SCNetworkReachabilityCreateWithName(nil, nodename) else { throw ReachabilityError.FailedToCreateWithHostname(hostname) } 110 | 111 | self.init(reachabilityRef: ref) 112 | } 113 | 114 | public class func reachabilityForInternetConnection() throws -> Reachability { 115 | 116 | var zeroAddress = sockaddr_in() 117 | zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) 118 | zeroAddress.sin_family = sa_family_t(AF_INET) 119 | 120 | guard let ref = withUnsafePointer(&zeroAddress, { 121 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) 122 | }) else { throw ReachabilityError.FailedToCreateWithAddress(zeroAddress) } 123 | 124 | return Reachability(reachabilityRef: ref) 125 | } 126 | 127 | public class func reachabilityForLocalWiFi() throws -> Reachability { 128 | 129 | var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) 130 | localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress)) 131 | localWifiAddress.sin_family = sa_family_t(AF_INET) 132 | 133 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 134 | let address: UInt32 = 0xA9FE0000 135 | localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian) 136 | 137 | guard let ref = withUnsafePointer(&localWifiAddress, { 138 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) 139 | }) else { throw ReachabilityError.FailedToCreateWithAddress(localWifiAddress) } 140 | 141 | return Reachability(reachabilityRef: ref) 142 | } 143 | 144 | // MARK: - *** Notifier methods *** 145 | public func startNotifier() throws { 146 | 147 | guard let reachabilityRef = reachabilityRef where !notifierRunning else { return } 148 | 149 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) 150 | context.info = UnsafeMutablePointer(OpaquePointer(bitPattern: Unmanaged.passUnretained(self))) 151 | 152 | if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { 153 | stopNotifier() 154 | throw ReachabilityError.UnableToSetCallback 155 | } 156 | 157 | if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { 158 | stopNotifier() 159 | throw ReachabilityError.UnableToSetDispatchQueue 160 | } 161 | 162 | // Perform an intial check 163 | reachabilitySerialQueue.async { 164 | let flags = self.reachabilityFlags 165 | self.reachabilityChanged(flags: flags) 166 | } 167 | 168 | notifierRunning = true 169 | } 170 | 171 | public func stopNotifier() { 172 | defer { notifierRunning = false } 173 | guard let reachabilityRef = reachabilityRef else { return } 174 | 175 | SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) 176 | SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) 177 | } 178 | 179 | // MARK: - *** Connection test methods *** 180 | public func isReachable() -> Bool { 181 | let flags = reachabilityFlags 182 | return isReachableWithFlags(flags:flags) 183 | } 184 | 185 | public func isReachableViaWWAN() -> Bool { 186 | 187 | let flags = reachabilityFlags 188 | 189 | // Check we're not on the simulator, we're REACHABLE and check we're on WWAN 190 | return isRunningOnDevice && isReachable(flags:flags) && isOnWWAN(flags:flags) 191 | } 192 | 193 | public func isReachableViaWiFi() -> Bool { 194 | 195 | let flags = reachabilityFlags 196 | 197 | // Check we're reachable 198 | if !isReachable(flags:flags) { 199 | return false 200 | } 201 | 202 | // Must be on WiFi if reachable but not on an iOS device (i.e. simulator) 203 | if !isRunningOnDevice { 204 | return true 205 | } 206 | 207 | // Check we're NOT on WWAN 208 | return !isOnWWAN(flags:flags) 209 | } 210 | 211 | // MARK: - *** Private methods *** 212 | private var isRunningOnDevice: Bool = { 213 | #if (arch(i386) || arch(x86_64)) && os(iOS) 214 | return false 215 | #else 216 | return true 217 | #endif 218 | }() 219 | 220 | private var notifierRunning = false 221 | private var reachabilityRef: SCNetworkReachability? 222 | 223 | private let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", attributes: .serial, target: nil) 224 | 225 | private func reachabilityChanged(flags:SCNetworkReachabilityFlags) { 226 | 227 | guard previousFlags != flags else { return } 228 | 229 | if isReachableWithFlags(flags:flags) { 230 | if let block = whenReachable { 231 | block(self) 232 | } 233 | } else { 234 | if let block = whenUnreachable { 235 | block(self) 236 | } 237 | } 238 | 239 | notificationCenter.post(name: ReachabilityChangedNotification, object:self) 240 | 241 | previousFlags = flags 242 | } 243 | 244 | private func isReachableWithFlags(flags:SCNetworkReachabilityFlags) -> Bool { 245 | 246 | if !isReachable(flags: flags) { 247 | return false 248 | } 249 | 250 | if isConnectionRequiredOrTransient(flags: flags) { 251 | return false 252 | } 253 | 254 | if isRunningOnDevice { 255 | if isOnWWAN(flags: flags) && !reachableOnWWAN { 256 | // We don't want to connect when on 3G. 257 | return false 258 | } 259 | } 260 | 261 | return true 262 | } 263 | 264 | // WWAN may be available, but not active until a connection has been established. 265 | // WiFi may require a connection for VPN on Demand. 266 | private func isConnectionRequired() -> Bool { 267 | return connectionRequired() 268 | } 269 | 270 | private func connectionRequired() -> Bool { 271 | let flags = reachabilityFlags 272 | return isConnectionRequired(flags: flags) 273 | } 274 | 275 | // Dynamic, on demand connection? 276 | private func isConnectionOnDemand() -> Bool { 277 | let flags = reachabilityFlags 278 | return isConnectionRequired(flags: flags) && isConnectionOnTrafficOrDemand(flags: flags) 279 | } 280 | 281 | // Is user intervention required? 282 | private func isInterventionRequired() -> Bool { 283 | let flags = reachabilityFlags 284 | return isConnectionRequired(flags: flags) && isInterventionRequired(flags: flags) 285 | } 286 | 287 | private func isOnWWAN(flags:SCNetworkReachabilityFlags) -> Bool { 288 | #if os(iOS) 289 | return flags.contains(.iswwan) 290 | #else 291 | return false 292 | #endif 293 | } 294 | private func isReachable(flags:SCNetworkReachabilityFlags) -> Bool { 295 | return flags.contains(.reachable) 296 | } 297 | private func isConnectionRequired(flags:SCNetworkReachabilityFlags) -> Bool { 298 | return flags.contains(.connectionRequired) 299 | } 300 | private func isInterventionRequired(flags:SCNetworkReachabilityFlags) -> Bool { 301 | return flags.contains(.interventionRequired) 302 | } 303 | private func isConnectionOnTraffic(flags:SCNetworkReachabilityFlags) -> Bool { 304 | return flags.contains(.connectionOnTraffic) 305 | } 306 | private func isConnectionOnDemand(flags:SCNetworkReachabilityFlags) -> Bool { 307 | return flags.contains(.connectionOnDemand) 308 | } 309 | func isConnectionOnTrafficOrDemand(flags:SCNetworkReachabilityFlags) -> Bool { 310 | return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty 311 | } 312 | private func isTransientConnection(flags:SCNetworkReachabilityFlags) -> Bool { 313 | return flags.contains(.transientConnection) 314 | } 315 | private func isLocalAddress(flags:SCNetworkReachabilityFlags) -> Bool { 316 | return flags.contains(.isLocalAddress) 317 | } 318 | private func isDirect(flags:SCNetworkReachabilityFlags) -> Bool { 319 | return flags.contains(.isDirect) 320 | } 321 | private func isConnectionRequiredOrTransient(flags:SCNetworkReachabilityFlags) -> Bool { 322 | let testcase:SCNetworkReachabilityFlags = [.connectionRequired, .transientConnection] 323 | return flags.intersection(testcase) == testcase 324 | } 325 | 326 | private var reachabilityFlags: SCNetworkReachabilityFlags { 327 | 328 | guard let reachabilityRef = reachabilityRef else { return SCNetworkReachabilityFlags() } 329 | 330 | var flags = SCNetworkReachabilityFlags() 331 | let gotFlags = withUnsafeMutablePointer(&flags) { 332 | SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) 333 | } 334 | 335 | if gotFlags { 336 | return flags 337 | } else { 338 | return SCNetworkReachabilityFlags() 339 | } 340 | } 341 | 342 | override public var description: String { 343 | 344 | var W: String 345 | if isRunningOnDevice { 346 | W = isOnWWAN(flags: reachabilityFlags) ? "W" : "-" 347 | } else { 348 | W = "X" 349 | } 350 | let R = isReachable(flags: reachabilityFlags) ? "R" : "-" 351 | let c = isConnectionRequired(flags: reachabilityFlags) ? "c" : "-" 352 | let t = isTransientConnection(flags: reachabilityFlags) ? "t" : "-" 353 | let i = isInterventionRequired(flags: reachabilityFlags) ? "i" : "-" 354 | let C = isConnectionOnTraffic(flags: reachabilityFlags) ? "C" : "-" 355 | let D = isConnectionOnDemand(flags: reachabilityFlags) ? "D" : "-" 356 | let l = isLocalAddress(flags: reachabilityFlags) ? "l" : "-" 357 | let d = isDirect(flags: reachabilityFlags) ? "d" : "-" 358 | 359 | return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" 360 | } 361 | 362 | deinit { 363 | stopNotifier() 364 | 365 | reachabilityRef = nil 366 | whenReachable = nil 367 | whenUnreachable = nil 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /Blobfish.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 01BBF0121DE23979003AC718 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01BBF0081DE23966003AC718 /* Alamofire.framework */; }; 11 | 2341B7262029CC9C00BECE13 /* LayoutUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2341B7252029CC9C00BECE13 /* LayoutUtils.swift */; }; 12 | 307ED5341C98AEE7002B7B74 /* Blobfish.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BA212F1C957F1500DC883C /* Blobfish.swift */; }; 13 | 30925AF81C95892E00895BE6 /* Blobable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BA21301C957F1500DC883C /* Blobable.swift */; }; 14 | 30925AFB1C9589B800895BE6 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30925AFA1C9589B800895BE6 /* Blob.swift */; }; 15 | 30BA21181C957E3100DC883C /* Blobfish.h in Headers */ = {isa = PBXBuildFile; fileRef = 30BA21171C957E3100DC883C /* Blobfish.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 30BA21391C957F7E00DC883C /* MessageBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BA21311C957F1500DC883C /* MessageBar.swift */; }; 17 | 30CC1B1F1C95858900DB923F /* AlamofireBlobfishExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BA21331C957F1500DC883C /* AlamofireBlobfishExtension.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 01BBF0031DE23966003AC718 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Alamofire.framework; sourceTree = ""; }; 22 | 01BBF0081DE23966003AC718 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Alamofire.framework; sourceTree = ""; }; 23 | 01BBF00C1DE23966003AC718 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Alamofire.framework; sourceTree = ""; }; 24 | 01BBF00F1DE23966003AC718 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Alamofire.framework; sourceTree = ""; }; 25 | 2341B7252029CC9C00BECE13 /* LayoutUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutUtils.swift; sourceTree = ""; }; 26 | 30925AFA1C9589B800895BE6 /* Blob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blob.swift; sourceTree = ""; }; 27 | 30BA21141C957E3100DC883C /* Blobfish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Blobfish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 30BA21171C957E3100DC883C /* Blobfish.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Blobfish.h; sourceTree = ""; }; 29 | 30BA21191C957E3100DC883C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | 30BA212F1C957F1500DC883C /* Blobfish.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blobfish.swift; sourceTree = ""; }; 31 | 30BA21301C957F1500DC883C /* Blobable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blobable.swift; sourceTree = ""; }; 32 | 30BA21311C957F1500DC883C /* MessageBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageBar.swift; sourceTree = ""; }; 33 | 30BA21331C957F1500DC883C /* AlamofireBlobfishExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlamofireBlobfishExtension.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 30BA21101C957E3100DC883C /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | 01BBF0121DE23979003AC718 /* Alamofire.framework in Frameworks */, 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | 01BBF0021DE23966003AC718 /* Mac */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | 01BBF0031DE23966003AC718 /* Alamofire.framework */, 52 | ); 53 | name = Mac; 54 | path = Carthage/Build/Mac; 55 | sourceTree = ""; 56 | }; 57 | 01BBF0051DE23966003AC718 /* iOS */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 01BBF0081DE23966003AC718 /* Alamofire.framework */, 61 | ); 62 | name = iOS; 63 | path = Carthage/Build/iOS; 64 | sourceTree = ""; 65 | }; 66 | 01BBF00A1DE23966003AC718 /* tvOS */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 01BBF00C1DE23966003AC718 /* Alamofire.framework */, 70 | ); 71 | name = tvOS; 72 | path = Carthage/Build/tvOS; 73 | sourceTree = ""; 74 | }; 75 | 01BBF00E1DE23966003AC718 /* watchOS */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 01BBF00F1DE23966003AC718 /* Alamofire.framework */, 79 | ); 80 | name = watchOS; 81 | path = Carthage/Build/watchOS; 82 | sourceTree = ""; 83 | }; 84 | 30BA210A1C957E3100DC883C = { 85 | isa = PBXGroup; 86 | children = ( 87 | 30BA21161C957E3100DC883C /* Blobfish */, 88 | 30BA213A1C957FC000DC883C /* Frameworks */, 89 | 30BA21151C957E3100DC883C /* Products */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 30BA21151C957E3100DC883C /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 30BA21141C957E3100DC883C /* Blobfish.framework */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 30BA21161C957E3100DC883C /* Blobfish */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 30BA212E1C957F1500DC883C /* Classes */, 105 | 30BA21171C957E3100DC883C /* Blobfish.h */, 106 | 30BA21191C957E3100DC883C /* Info.plist */, 107 | ); 108 | path = Blobfish; 109 | sourceTree = ""; 110 | }; 111 | 30BA212E1C957F1500DC883C /* Classes */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 30BA212F1C957F1500DC883C /* Blobfish.swift */, 115 | 30BA21311C957F1500DC883C /* MessageBar.swift */, 116 | 30925AFA1C9589B800895BE6 /* Blob.swift */, 117 | 30BA21301C957F1500DC883C /* Blobable.swift */, 118 | 30BA21331C957F1500DC883C /* AlamofireBlobfishExtension.swift */, 119 | 2341B7252029CC9C00BECE13 /* LayoutUtils.swift */, 120 | ); 121 | path = Classes; 122 | sourceTree = ""; 123 | }; 124 | 30BA213A1C957FC000DC883C /* Frameworks */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 01BBF0021DE23966003AC718 /* Mac */, 128 | 01BBF0051DE23966003AC718 /* iOS */, 129 | 01BBF00A1DE23966003AC718 /* tvOS */, 130 | 01BBF00E1DE23966003AC718 /* watchOS */, 131 | ); 132 | name = Frameworks; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXHeadersBuildPhase section */ 138 | 30BA21111C957E3100DC883C /* Headers */ = { 139 | isa = PBXHeadersBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 30BA21181C957E3100DC883C /* Blobfish.h in Headers */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXHeadersBuildPhase section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 30BA21131C957E3100DC883C /* Blobfish */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 30BA21281C957E3100DC883C /* Build configuration list for PBXNativeTarget "Blobfish" */; 152 | buildPhases = ( 153 | 30BA210F1C957E3100DC883C /* Sources */, 154 | 30BA21101C957E3100DC883C /* Frameworks */, 155 | 30BA21111C957E3100DC883C /* Headers */, 156 | 30BA21121C957E3100DC883C /* Resources */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = Blobfish; 163 | productName = Blobfish; 164 | productReference = 30BA21141C957E3100DC883C /* Blobfish.framework */; 165 | productType = "com.apple.product-type.framework"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 30BA210B1C957E3100DC883C /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastSwiftUpdateCheck = 0730; 174 | LastUpgradeCheck = 1010; 175 | ORGANIZATIONNAME = Nodes; 176 | TargetAttributes = { 177 | 30BA21131C957E3100DC883C = { 178 | CreatedOnToolsVersion = 7.3; 179 | LastSwiftMigration = 1010; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 30BA210E1C957E3100DC883C /* Build configuration list for PBXProject "Blobfish" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | ); 190 | mainGroup = 30BA210A1C957E3100DC883C; 191 | productRefGroup = 30BA21151C957E3100DC883C /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | 30BA21131C957E3100DC883C /* Blobfish */, 196 | ); 197 | }; 198 | /* End PBXProject section */ 199 | 200 | /* Begin PBXResourcesBuildPhase section */ 201 | 30BA21121C957E3100DC883C /* Resources */ = { 202 | isa = PBXResourcesBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 30BA210F1C957E3100DC883C /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 30BA21391C957F7E00DC883C /* MessageBar.swift in Sources */, 216 | 2341B7262029CC9C00BECE13 /* LayoutUtils.swift in Sources */, 217 | 30925AF81C95892E00895BE6 /* Blobable.swift in Sources */, 218 | 307ED5341C98AEE7002B7B74 /* Blobfish.swift in Sources */, 219 | 30925AFB1C9589B800895BE6 /* Blob.swift in Sources */, 220 | 30CC1B1F1C95858900DB923F /* AlamofireBlobfishExtension.swift in Sources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXSourcesBuildPhase section */ 225 | 226 | /* Begin XCBuildConfiguration section */ 227 | 30BA21261C957E3100DC883C /* Debug */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | ALWAYS_SEARCH_USER_PATHS = NO; 231 | CLANG_ANALYZER_NONNULL = YES; 232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 233 | CLANG_CXX_LIBRARY = "libc++"; 234 | CLANG_ENABLE_MODULES = YES; 235 | CLANG_ENABLE_OBJC_ARC = YES; 236 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_COMMA = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_EMPTY_BODY = YES; 243 | CLANG_WARN_ENUM_CONVERSION = YES; 244 | CLANG_WARN_INFINITE_RECURSION = YES; 245 | CLANG_WARN_INT_CONVERSION = YES; 246 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 247 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 248 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 251 | CLANG_WARN_STRICT_PROTOTYPES = YES; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 256 | COPY_PHASE_STRIP = NO; 257 | CURRENT_PROJECT_VERSION = 1; 258 | DEBUG_INFORMATION_FORMAT = dwarf; 259 | DEFINES_MODULE = NO; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | ENABLE_TESTABILITY = YES; 262 | FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS"; 263 | GCC_C_LANGUAGE_STANDARD = gnu99; 264 | GCC_DYNAMIC_NO_PIC = NO; 265 | GCC_NO_COMMON_BLOCKS = YES; 266 | GCC_OPTIMIZATION_LEVEL = 0; 267 | GCC_PREPROCESSOR_DEFINITIONS = ( 268 | "DEBUG=1", 269 | "$(inherited)", 270 | ); 271 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 272 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 273 | GCC_WARN_UNDECLARED_SELECTOR = YES; 274 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 275 | GCC_WARN_UNUSED_FUNCTION = YES; 276 | GCC_WARN_UNUSED_VARIABLE = YES; 277 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 278 | MTL_ENABLE_DEBUG_INFO = YES; 279 | ONLY_ACTIVE_ARCH = YES; 280 | SDKROOT = iphoneos; 281 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | VERSIONING_SYSTEM = "apple-generic"; 284 | VERSION_INFO_PREFIX = ""; 285 | }; 286 | name = Debug; 287 | }; 288 | 30BA21271C957E3100DC883C /* Release */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ALWAYS_SEARCH_USER_PATHS = NO; 292 | CLANG_ANALYZER_NONNULL = YES; 293 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 294 | CLANG_CXX_LIBRARY = "libc++"; 295 | CLANG_ENABLE_MODULES = YES; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_COMMA = YES; 300 | CLANG_WARN_CONSTANT_CONVERSION = YES; 301 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 303 | CLANG_WARN_EMPTY_BODY = YES; 304 | CLANG_WARN_ENUM_CONVERSION = YES; 305 | CLANG_WARN_INFINITE_RECURSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNREACHABLE_CODE = YES; 315 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 316 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 317 | COPY_PHASE_STRIP = NO; 318 | CURRENT_PROJECT_VERSION = 1; 319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 320 | DEFINES_MODULE = NO; 321 | ENABLE_NS_ASSERTIONS = NO; 322 | ENABLE_STRICT_OBJC_MSGSEND = YES; 323 | FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/Carthage/Build/iOS"; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 333 | MTL_ENABLE_DEBUG_INFO = NO; 334 | SDKROOT = iphoneos; 335 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 336 | TARGETED_DEVICE_FAMILY = "1,2"; 337 | VALIDATE_PRODUCT = YES; 338 | VERSIONING_SYSTEM = "apple-generic"; 339 | VERSION_INFO_PREFIX = ""; 340 | }; 341 | name = Release; 342 | }; 343 | 30BA21291C957E3100DC883C /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 347 | DEFINES_MODULE = YES; 348 | DYLIB_COMPATIBILITY_VERSION = 1; 349 | DYLIB_CURRENT_VERSION = 1; 350 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 351 | FRAMEWORK_SEARCH_PATHS = ( 352 | "$(inherited)", 353 | "$(PROJECT_DIR)/Carthage/Build/iOS", 354 | ); 355 | INFOPLIST_FILE = Blobfish/Info.plist; 356 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 357 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 358 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/Carthage/Build/iOS"; 359 | PRODUCT_BUNDLE_IDENTIFIER = com.nodes.Blobfish; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | SKIP_INSTALL = YES; 362 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 363 | SWIFT_VERSION = 4.2; 364 | }; 365 | name = Debug; 366 | }; 367 | 30BA212A1C957E3100DC883C /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 371 | DEFINES_MODULE = YES; 372 | DYLIB_COMPATIBILITY_VERSION = 1; 373 | DYLIB_CURRENT_VERSION = 1; 374 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 375 | FRAMEWORK_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Carthage/Build/iOS", 378 | ); 379 | INFOPLIST_FILE = Blobfish/Info.plist; 380 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 381 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 382 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/Carthage/Build/iOS"; 383 | PRODUCT_BUNDLE_IDENTIFIER = com.nodes.Blobfish; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | SKIP_INSTALL = YES; 386 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 387 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 388 | SWIFT_VERSION = 4.2; 389 | }; 390 | name = Release; 391 | }; 392 | /* End XCBuildConfiguration section */ 393 | 394 | /* Begin XCConfigurationList section */ 395 | 30BA210E1C957E3100DC883C /* Build configuration list for PBXProject "Blobfish" */ = { 396 | isa = XCConfigurationList; 397 | buildConfigurations = ( 398 | 30BA21261C957E3100DC883C /* Debug */, 399 | 30BA21271C957E3100DC883C /* Release */, 400 | ); 401 | defaultConfigurationIsVisible = 0; 402 | defaultConfigurationName = Release; 403 | }; 404 | 30BA21281C957E3100DC883C /* Build configuration list for PBXNativeTarget "Blobfish" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | 30BA21291C957E3100DC883C /* Debug */, 408 | 30BA212A1C957E3100DC883C /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | /* End XCConfigurationList section */ 414 | }; 415 | rootObject = 30BA210B1C957E3100DC883C /* Project object */; 416 | } 417 | --------------------------------------------------------------------------------