parent
70a46ed151
commit
ce0c6ff832
@ -0,0 +1,29 @@ |
|||||||
|
PODS: |
||||||
|
- Flutter (1.0.0) |
||||||
|
- flutter_openim_sdk (0.0.1): |
||||||
|
- Flutter |
||||||
|
- OpenIMSDKCore (= 3.0.0-beta) |
||||||
|
- OpenIMSDKCore (3.0.0-beta) |
||||||
|
|
||||||
|
DEPENDENCIES: |
||||||
|
- Flutter (from `Flutter`) |
||||||
|
- flutter_openim_sdk (from `.symlinks/plugins/flutter_openim_sdk/ios`) |
||||||
|
|
||||||
|
SPEC REPOS: |
||||||
|
trunk: |
||||||
|
- OpenIMSDKCore |
||||||
|
|
||||||
|
EXTERNAL SOURCES: |
||||||
|
Flutter: |
||||||
|
:path: Flutter |
||||||
|
flutter_openim_sdk: |
||||||
|
:path: ".symlinks/plugins/flutter_openim_sdk/ios" |
||||||
|
|
||||||
|
SPEC CHECKSUMS: |
||||||
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 |
||||||
|
flutter_openim_sdk: ed909a3ad758f605a188acd96d6f38f27953016f |
||||||
|
OpenIMSDKCore: 4f3815fe8e0a6ef4130894afd2e8ad4d343df66e |
||||||
|
|
||||||
|
PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d |
||||||
|
|
||||||
|
COCOAPODS: 1.11.3 |
@ -1,122 +1,103 @@ |
|||||||
{ |
{ |
||||||
"images" : [ |
"images" : [ |
||||||
{ |
{ |
||||||
"size" : "20x20", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-20x20@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "20x20" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "20x20", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-20x20@3x.png", |
"scale" : "3x", |
||||||
"scale" : "3x" |
"size" : "20x20" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "29x29", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-29x29@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "29x29" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "29x29", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-29x29@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "29x29" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "29x29", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-29x29@3x.png", |
"scale" : "3x", |
||||||
"scale" : "3x" |
"size" : "29x29" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "40x40", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-40x40@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "40x40" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "40x40", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-40x40@3x.png", |
"scale" : "3x", |
||||||
"scale" : "3x" |
"size" : "40x40" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "60x60", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-60x60@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "60x60" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "60x60", |
|
||||||
"idiom" : "iphone", |
"idiom" : "iphone", |
||||||
"filename" : "Icon-App-60x60@3x.png", |
"scale" : "3x", |
||||||
"scale" : "3x" |
"size" : "60x60" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "20x20", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-20x20@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "20x20" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "20x20", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-20x20@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "20x20" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "29x29", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-29x29@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "29x29" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "29x29", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-29x29@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "29x29" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "40x40", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-40x40@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "40x40" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "40x40", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-40x40@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "40x40" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "76x76", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-76x76@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "76x76" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "76x76", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-76x76@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "76x76" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "83.5x83.5", |
|
||||||
"idiom" : "ipad", |
"idiom" : "ipad", |
||||||
"filename" : "Icon-App-83.5x83.5@2x.png", |
"scale" : "2x", |
||||||
"scale" : "2x" |
"size" : "83.5x83.5" |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
"size" : "1024x1024", |
|
||||||
"idiom" : "ios-marketing", |
"idiom" : "ios-marketing", |
||||||
"filename" : "Icon-App-1024x1024@1x.png", |
"scale" : "1x", |
||||||
"scale" : "1x" |
"size" : "1024x1024" |
||||||
} |
} |
||||||
], |
], |
||||||
"info" : { |
"info" : { |
||||||
"version" : 1, |
"author" : "xcode", |
||||||
"author" : "xcode" |
"version" : 1 |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,405 @@ |
|||||||
|
/* |
||||||
|
Copyright (c) 2014, Ashley Mills |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this |
||||||
|
list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, |
||||||
|
this list of conditions and the following disclaimer in the documentation |
||||||
|
and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||||
|
POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
import SystemConfiguration |
||||||
|
import Foundation |
||||||
|
|
||||||
|
public enum ReachabilityError: Error { |
||||||
|
case failedToCreateWithAddress(sockaddr, Int32) |
||||||
|
case failedToCreateWithHostname(String, Int32) |
||||||
|
case unableToSetCallback(Int32) |
||||||
|
case unableToSetDispatchQueue(Int32) |
||||||
|
case unableToGetFlags(Int32) |
||||||
|
} |
||||||
|
|
||||||
|
@available(*, unavailable, renamed: "Notification.Name.reachabilityChanged") |
||||||
|
public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") |
||||||
|
|
||||||
|
public extension Notification.Name { |
||||||
|
static let reachabilityChanged = Notification.Name("reachabilityChanged") |
||||||
|
} |
||||||
|
|
||||||
|
public class Reachability { |
||||||
|
|
||||||
|
public typealias NetworkReachable = (Reachability) -> () |
||||||
|
public typealias NetworkUnreachable = (Reachability) -> () |
||||||
|
|
||||||
|
@available(*, unavailable, renamed: "Connection") |
||||||
|
public enum NetworkStatus: CustomStringConvertible { |
||||||
|
case notReachable, reachableViaWiFi, reachableViaWWAN |
||||||
|
public var description: String { |
||||||
|
switch self { |
||||||
|
case .reachableViaWWAN: return "Cellular" |
||||||
|
case .reachableViaWiFi: return "WiFi" |
||||||
|
case .notReachable: return "No Connection" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public enum Connection: CustomStringConvertible { |
||||||
|
case unavailable, wifi, cellular |
||||||
|
public var description: String { |
||||||
|
switch self { |
||||||
|
case .cellular: return "Cellular" |
||||||
|
case .wifi: return "WiFi" |
||||||
|
case .unavailable: return "No Connection" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "unavailable") |
||||||
|
public static let none: Connection = .unavailable |
||||||
|
} |
||||||
|
|
||||||
|
public var whenReachable: NetworkReachable? |
||||||
|
public var whenUnreachable: NetworkUnreachable? |
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "allowsCellularConnection") |
||||||
|
public let reachableOnWWAN: Bool = true |
||||||
|
|
||||||
|
/// Set to `false` to force Reachability.connection to .none when on cellular connection (default value `true`) |
||||||
|
public var allowsCellularConnection: Bool |
||||||
|
|
||||||
|
// The notification center on which "reachability changed" events are being posted |
||||||
|
public var notificationCenter: NotificationCenter = NotificationCenter.default |
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "connection.description") |
||||||
|
public var currentReachabilityString: String { |
||||||
|
return "\(connection)" |
||||||
|
} |
||||||
|
|
||||||
|
@available(*, unavailable, renamed: "connection") |
||||||
|
public var currentReachabilityStatus: Connection { |
||||||
|
return connection |
||||||
|
} |
||||||
|
|
||||||
|
public var connection: Connection { |
||||||
|
if flags == nil { |
||||||
|
try? setReachabilityFlags() |
||||||
|
} |
||||||
|
|
||||||
|
switch flags?.connection { |
||||||
|
case .unavailable?, nil: return .unavailable |
||||||
|
case .cellular?: return allowsCellularConnection ? .cellular : .unavailable |
||||||
|
case .wifi?: return .wifi |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate var isRunningOnDevice: Bool = { |
||||||
|
#if targetEnvironment(simulator) |
||||||
|
return false |
||||||
|
#else |
||||||
|
return true |
||||||
|
#endif |
||||||
|
}() |
||||||
|
|
||||||
|
fileprivate(set) var notifierRunning = false |
||||||
|
fileprivate let reachabilityRef: SCNetworkReachability |
||||||
|
fileprivate let reachabilitySerialQueue: DispatchQueue |
||||||
|
fileprivate let notificationQueue: DispatchQueue? |
||||||
|
fileprivate(set) var flags: SCNetworkReachabilityFlags? { |
||||||
|
didSet { |
||||||
|
guard flags != oldValue else { return } |
||||||
|
notifyReachabilityChanged() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
required public init(reachabilityRef: SCNetworkReachability, |
||||||
|
queueQoS: DispatchQoS = .default, |
||||||
|
targetQueue: DispatchQueue? = nil, |
||||||
|
notificationQueue: DispatchQueue? = .main) { |
||||||
|
self.allowsCellularConnection = true |
||||||
|
self.reachabilityRef = reachabilityRef |
||||||
|
self.reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability", qos: queueQoS, target: targetQueue) |
||||||
|
self.notificationQueue = notificationQueue |
||||||
|
} |
||||||
|
|
||||||
|
public convenience init(hostname: String, |
||||||
|
queueQoS: DispatchQoS = .default, |
||||||
|
targetQueue: DispatchQueue? = nil, |
||||||
|
notificationQueue: DispatchQueue? = .main) throws { |
||||||
|
guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { |
||||||
|
throw ReachabilityError.failedToCreateWithHostname(hostname, SCError()) |
||||||
|
} |
||||||
|
self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) |
||||||
|
} |
||||||
|
|
||||||
|
public convenience init(queueQoS: DispatchQoS = .default, |
||||||
|
targetQueue: DispatchQueue? = nil, |
||||||
|
notificationQueue: DispatchQueue? = .main) throws { |
||||||
|
var zeroAddress = sockaddr() |
||||||
|
zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size) |
||||||
|
zeroAddress.sa_family = sa_family_t(AF_INET) |
||||||
|
|
||||||
|
guard let ref = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { |
||||||
|
throw ReachabilityError.failedToCreateWithAddress(zeroAddress, SCError()) |
||||||
|
} |
||||||
|
|
||||||
|
self.init(reachabilityRef: ref, queueQoS: queueQoS, targetQueue: targetQueue, notificationQueue: notificationQueue) |
||||||
|
} |
||||||
|
|
||||||
|
deinit { |
||||||
|
stopNotifier() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public extension Reachability { |
||||||
|
|
||||||
|
// MARK: - *** Notifier methods *** |
||||||
|
func startNotifier() throws { |
||||||
|
guard !notifierRunning else { return } |
||||||
|
|
||||||
|
let callback: SCNetworkReachabilityCallBack = { (reachability, flags, info) in |
||||||
|
guard let info = info else { return } |
||||||
|
|
||||||
|
// `weakifiedReachability` is guaranteed to exist by virtue of our |
||||||
|
// retain/release callbacks which we provided to the `SCNetworkReachabilityContext`. |
||||||
|
let weakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info).takeUnretainedValue() |
||||||
|
|
||||||
|
// The weak `reachability` _may_ no longer exist if the `Reachability` |
||||||
|
// object has since been deallocated but a callback was already in flight. |
||||||
|
weakifiedReachability.reachability?.flags = flags |
||||||
|
} |
||||||
|
|
||||||
|
let weakifiedReachability = ReachabilityWeakifier(reachability: self) |
||||||
|
let opaqueWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.passUnretained(weakifiedReachability).toOpaque() |
||||||
|
|
||||||
|
var context = SCNetworkReachabilityContext( |
||||||
|
version: 0, |
||||||
|
info: UnsafeMutableRawPointer(opaqueWeakifiedReachability), |
||||||
|
retain: { (info: UnsafeRawPointer) -> UnsafeRawPointer in |
||||||
|
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info) |
||||||
|
_ = unmanagedWeakifiedReachability.retain() |
||||||
|
return UnsafeRawPointer(unmanagedWeakifiedReachability.toOpaque()) |
||||||
|
}, |
||||||
|
release: { (info: UnsafeRawPointer) -> Void in |
||||||
|
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info) |
||||||
|
unmanagedWeakifiedReachability.release() |
||||||
|
}, |
||||||
|
copyDescription: { (info: UnsafeRawPointer) -> Unmanaged<CFString> in |
||||||
|
let unmanagedWeakifiedReachability = Unmanaged<ReachabilityWeakifier>.fromOpaque(info) |
||||||
|
let weakifiedReachability = unmanagedWeakifiedReachability.takeUnretainedValue() |
||||||
|
let description = weakifiedReachability.reachability?.description ?? "nil" |
||||||
|
return Unmanaged.passRetained(description as CFString) |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { |
||||||
|
stopNotifier() |
||||||
|
throw ReachabilityError.unableToSetCallback(SCError()) |
||||||
|
} |
||||||
|
|
||||||
|
if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { |
||||||
|
stopNotifier() |
||||||
|
throw ReachabilityError.unableToSetDispatchQueue(SCError()) |
||||||
|
} |
||||||
|
|
||||||
|
// Perform an initial check |
||||||
|
try setReachabilityFlags() |
||||||
|
|
||||||
|
notifierRunning = true |
||||||
|
} |
||||||
|
|
||||||
|
func stopNotifier() { |
||||||
|
defer { notifierRunning = false } |
||||||
|
|
||||||
|
SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) |
||||||
|
SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) |
||||||
|
} |
||||||
|
|
||||||
|
// MARK: - *** Connection test methods *** |
||||||
|
@available(*, deprecated, message: "Please use `connection != .none`") |
||||||
|
var isReachable: Bool { |
||||||
|
return connection != .unavailable |
||||||
|
} |
||||||
|
|
||||||
|
@available(*, deprecated, message: "Please use `connection == .cellular`") |
||||||
|
var isReachableViaWWAN: Bool { |
||||||
|
// Check we're not on the simulator, we're REACHABLE and check we're on WWAN |
||||||
|
return connection == .cellular |
||||||
|
} |
||||||
|
|
||||||
|
@available(*, deprecated, message: "Please use `connection == .wifi`") |
||||||
|
var isReachableViaWiFi: Bool { |
||||||
|
return connection == .wifi |
||||||
|
} |
||||||
|
|
||||||
|
var description: String { |
||||||
|
return flags?.description ?? "unavailable flags" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fileprivate extension Reachability { |
||||||
|
|
||||||
|
func setReachabilityFlags() throws { |
||||||
|
try reachabilitySerialQueue.sync { [unowned self] in |
||||||
|
var flags = SCNetworkReachabilityFlags() |
||||||
|
if !SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags) { |
||||||
|
self.stopNotifier() |
||||||
|
throw ReachabilityError.unableToGetFlags(SCError()) |
||||||
|
} |
||||||
|
|
||||||
|
self.flags = flags |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
func notifyReachabilityChanged() { |
||||||
|
let notify = { [weak self] in |
||||||
|
guard let self = self else { return } |
||||||
|
self.connection != .unavailable ? self.whenReachable?(self) : self.whenUnreachable?(self) |
||||||
|
self.notificationCenter.post(name: .reachabilityChanged, object: self) |
||||||
|
} |
||||||
|
|
||||||
|
// notify on the configured `notificationQueue`, or the caller's (i.e. `reachabilitySerialQueue`) |
||||||
|
notificationQueue?.async(execute: notify) ?? notify() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
extension SCNetworkReachabilityFlags { |
||||||
|
|
||||||
|
typealias Connection = Reachability.Connection |
||||||
|
|
||||||
|
var connection: Connection { |
||||||
|
guard isReachableFlagSet else { return .unavailable } |
||||||
|
|
||||||
|
// If we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi |
||||||
|
#if targetEnvironment(simulator) |
||||||
|
return .wifi |
||||||
|
#else |
||||||
|
var connection = Connection.unavailable |
||||||
|
|
||||||
|
if !isConnectionRequiredFlagSet { |
||||||
|
connection = .wifi |
||||||
|
} |
||||||
|
|
||||||
|
if isConnectionOnTrafficOrDemandFlagSet { |
||||||
|
if !isInterventionRequiredFlagSet { |
||||||
|
connection = .wifi |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if isOnWWANFlagSet { |
||||||
|
connection = .cellular |
||||||
|
} |
||||||
|
|
||||||
|
return connection |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
var isOnWWANFlagSet: Bool { |
||||||
|
#if os(iOS) |
||||||
|
return contains(.isWWAN) |
||||||
|
#else |
||||||
|
return false |
||||||
|
#endif |
||||||
|
} |
||||||
|
var isReachableFlagSet: Bool { |
||||||
|
return contains(.reachable) |
||||||
|
} |
||||||
|
var isConnectionRequiredFlagSet: Bool { |
||||||
|
return contains(.connectionRequired) |
||||||
|
} |
||||||
|
var isInterventionRequiredFlagSet: Bool { |
||||||
|
return contains(.interventionRequired) |
||||||
|
} |
||||||
|
var isConnectionOnTrafficFlagSet: Bool { |
||||||
|
return contains(.connectionOnTraffic) |
||||||
|
} |
||||||
|
var isConnectionOnDemandFlagSet: Bool { |
||||||
|
return contains(.connectionOnDemand) |
||||||
|
} |
||||||
|
var isConnectionOnTrafficOrDemandFlagSet: Bool { |
||||||
|
return !intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty |
||||||
|
} |
||||||
|
var isTransientConnectionFlagSet: Bool { |
||||||
|
return contains(.transientConnection) |
||||||
|
} |
||||||
|
var isLocalAddressFlagSet: Bool { |
||||||
|
return contains(.isLocalAddress) |
||||||
|
} |
||||||
|
var isDirectFlagSet: Bool { |
||||||
|
return contains(.isDirect) |
||||||
|
} |
||||||
|
var isConnectionRequiredAndTransientFlagSet: Bool { |
||||||
|
return intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] |
||||||
|
} |
||||||
|
|
||||||
|
var description: String { |
||||||
|
let W = isOnWWANFlagSet ? "W" : "-" |
||||||
|
let R = isReachableFlagSet ? "R" : "-" |
||||||
|
let c = isConnectionRequiredFlagSet ? "c" : "-" |
||||||
|
let t = isTransientConnectionFlagSet ? "t" : "-" |
||||||
|
let i = isInterventionRequiredFlagSet ? "i" : "-" |
||||||
|
let C = isConnectionOnTrafficFlagSet ? "C" : "-" |
||||||
|
let D = isConnectionOnDemandFlagSet ? "D" : "-" |
||||||
|
let l = isLocalAddressFlagSet ? "l" : "-" |
||||||
|
let d = isDirectFlagSet ? "d" : "-" |
||||||
|
|
||||||
|
return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
`ReachabilityWeakifier` weakly wraps the `Reachability` class |
||||||
|
in order to break retain cycles when interacting with CoreFoundation. |
||||||
|
|
||||||
|
CoreFoundation callbacks expect a pair of retain/release whenever an |
||||||
|
opaque `info` parameter is provided. These callbacks exist to guard |
||||||
|
against memory management race conditions when invoking the callbacks. |
||||||
|
|
||||||
|
#### Race Condition |
||||||
|
|
||||||
|
If we passed `SCNetworkReachabilitySetCallback` a direct reference to our |
||||||
|
`Reachability` class without also providing corresponding retain/release |
||||||
|
callbacks, then a race condition can lead to crashes when: |
||||||
|
- `Reachability` is deallocated on thread X |
||||||
|
- A `SCNetworkReachability` callback(s) is already in flight on thread Y |
||||||
|
|
||||||
|
#### Retain Cycle |
||||||
|
|
||||||
|
If we pass `Reachability` to CoreFoundtion while also providing retain/ |
||||||
|
release callbacks, we would create a retain cycle once CoreFoundation |
||||||
|
retains our `Reachability` class. This fixes the crashes and his how |
||||||
|
CoreFoundation expects the API to be used, but doesn't play nicely with |
||||||
|
Swift/ARC. This cycle would only be broken after manually calling |
||||||
|
`stopNotifier()` — `deinit` would never be called. |
||||||
|
|
||||||
|
#### ReachabilityWeakifier |
||||||
|
|
||||||
|
By providing both retain/release callbacks and wrapping `Reachability` in |
||||||
|
a weak wrapper, we: |
||||||
|
- interact correctly with CoreFoundation, thereby avoiding a crash. |
||||||
|
See "Memory Management Programming Guide for Core Foundation". |
||||||
|
- don't alter the public API of `Reachability.swift` in any way |
||||||
|
- still allow for automatic stopping of the notifier on `deinit`. |
||||||
|
*/ |
||||||
|
private class ReachabilityWeakifier { |
||||||
|
weak var reachability: Reachability? |
||||||
|
init(reachability: Reachability) { |
||||||
|
self.reachability = reachability |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue