├── 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 |
6 |
7 |
8 | Easily handle errors and present them to the user in a nice way.
9 | [](https://travis-ci.org/nodes-ios/Blobfish)
10 | [](https://github.com/Carthage/Carthage)
11 | 
12 | [](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 |
--------------------------------------------------------------------------------