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" : [ |
||||
{ |
||||
"size" : "20x20", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-20x20@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "20x20" |
||||
}, |
||||
{ |
||||
"size" : "20x20", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-20x20@3x.png", |
||||
"scale" : "3x" |
||||
"scale" : "3x", |
||||
"size" : "20x20" |
||||
}, |
||||
{ |
||||
"size" : "29x29", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-29x29@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "29x29" |
||||
}, |
||||
{ |
||||
"size" : "29x29", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-29x29@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "29x29" |
||||
}, |
||||
{ |
||||
"size" : "29x29", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-29x29@3x.png", |
||||
"scale" : "3x" |
||||
"scale" : "3x", |
||||
"size" : "29x29" |
||||
}, |
||||
{ |
||||
"size" : "40x40", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-40x40@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "40x40" |
||||
}, |
||||
{ |
||||
"size" : "40x40", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-40x40@3x.png", |
||||
"scale" : "3x" |
||||
"scale" : "3x", |
||||
"size" : "40x40" |
||||
}, |
||||
{ |
||||
"size" : "60x60", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-60x60@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "60x60" |
||||
}, |
||||
{ |
||||
"size" : "60x60", |
||||
"idiom" : "iphone", |
||||
"filename" : "Icon-App-60x60@3x.png", |
||||
"scale" : "3x" |
||||
"scale" : "3x", |
||||
"size" : "60x60" |
||||
}, |
||||
{ |
||||
"size" : "20x20", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-20x20@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "20x20" |
||||
}, |
||||
{ |
||||
"size" : "20x20", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-20x20@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "20x20" |
||||
}, |
||||
{ |
||||
"size" : "29x29", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-29x29@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "29x29" |
||||
}, |
||||
{ |
||||
"size" : "29x29", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-29x29@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "29x29" |
||||
}, |
||||
{ |
||||
"size" : "40x40", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-40x40@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "40x40" |
||||
}, |
||||
{ |
||||
"size" : "40x40", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-40x40@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "40x40" |
||||
}, |
||||
{ |
||||
"size" : "76x76", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-76x76@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "76x76" |
||||
}, |
||||
{ |
||||
"size" : "76x76", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-76x76@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "76x76" |
||||
}, |
||||
{ |
||||
"size" : "83.5x83.5", |
||||
"idiom" : "ipad", |
||||
"filename" : "Icon-App-83.5x83.5@2x.png", |
||||
"scale" : "2x" |
||||
"scale" : "2x", |
||||
"size" : "83.5x83.5" |
||||
}, |
||||
{ |
||||
"size" : "1024x1024", |
||||
"idiom" : "ios-marketing", |
||||
"filename" : "Icon-App-1024x1024@1x.png", |
||||
"scale" : "1x" |
||||
"scale" : "1x", |
||||
"size" : "1024x1024" |
||||
} |
||||
], |
||||
"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