From 3262e24b4842b50b0e53be7893831a54f7dd9340 Mon Sep 17 00:00:00 2001 From: Varun Santhanam Date: Sun, 31 Mar 2024 09:05:21 -0700 Subject: [PATCH] Delete ReachabilityMonitor (#49) - Move to the Swift 5.9 Toolchain - Delete `ReachabilityMonitor`, and drop support for iOS 11, watchOS 5, tvOS 11, macOS 10.13 - Move CI to Xcode 15.2 --- .github/workflows/spm-build-test.yml | 2 +- .swiftformat | 2 +- Package.resolved | 4 +- Package.swift | 12 +- .../Extensions/NotificationExtensions.swift | 8 +- .../API/Extensions/PublishersExtensions.swift | 4 - .../NetworkMonitor+Combine.swift | 10 +- .../API/NetworkMonitor/NetworkMonitor.swift | 24 +- .../ReachabilityMonitor/Reachability.swift | 127 ---- .../ReachabilityMonitor+Combine.swift | 172 ----- .../ReachabilityMonitor+Concurrency.swift | 115 ---- .../ReachabilityMonitor+Error.swift | 122 ---- .../ReachabilityMonitor+Notification.swift | 36 - .../ReachabilityMonitor.swift | 643 ------------------ .../ReachabilityMonitorDelegate.swift | 44 -- .../DispatchQueue+NetworkReachability.swift | 11 +- ...workReachability+NetworkReachability.swift | 55 -- ...eachabilityFlags+NetworkReachability.swift | 114 ---- .../Articles/GettingStarted.md | 16 +- .../Articles/ReachabilityMonitorGuide.md | 204 ------ .../NetworkReachability.md | 7 - .../Snippets/.swiftformat | 2 +- .../Snippets/Tutorials/ComputeColor.swift | 16 +- .../Snippets/Tutorials/ComputeSymbol.swift | 10 +- .../Snippets/Tutorials/FormatSymbol.swift | 16 +- .../Snippets/Tutorials/ImplementManager.swift | 12 +- .../Snippets/Tutorials/UseColor.swift | 16 +- .../Snippets/Tutorials/UseSymbol.swift | 16 +- .../ReachabilityMonitorCombineTests.swift | 57 -- .../ReachabilityMonitorConcurrencyTests.swift | 53 -- .../ReachabilityMonitorTests.swift | 137 ---- 31 files changed, 76 insertions(+), 1991 deletions(-) delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/Reachability.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Combine.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Concurrency.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Error.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Notification.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor.swift delete mode 100644 Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitorDelegate.swift delete mode 100644 Sources/NetworkReachability/Internal/SCNetworkReachability+NetworkReachability.swift delete mode 100644 Sources/NetworkReachability/Internal/SCNetworkReachabilityFlags+NetworkReachability.swift delete mode 100644 Sources/NetworkReachability/NetworkReachability.docc/Articles/ReachabilityMonitorGuide.md delete mode 100644 Tests/NetworkReachabilityTests/ReachabilityMonitorCombineTests.swift delete mode 100644 Tests/NetworkReachabilityTests/ReachabilityMonitorConcurrencyTests.swift delete mode 100644 Tests/NetworkReachabilityTests/ReachabilityMonitorTests.swift diff --git a/.github/workflows/spm-build-test.yml b/.github/workflows/spm-build-test.yml index 3cf80c51..535fc73b 100644 --- a/.github/workflows/spm-build-test.yml +++ b/.github/workflows/spm-build-test.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Xcode Version - run: sudo xcode-select --switch /Applications/Xcode_14.3.app + run: sudo xcode-select --switch /Applications/Xcode_15.2.app - name: Lint run: swift package plugin --allow-writing-to-package-directory swiftformat --lint - name: Build diff --git a/.swiftformat b/.swiftformat index 704813f4..0a062156 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,4 +1,4 @@ --enable isEmpty --disable blankLinesAtEndOfScope, blankLinesAtStartOfScope, redundantNilInit, unusedArguments, redundantParens, wrapMultilineStatementBraces, trailingCommas, braces ---swiftversion 5.6 +--swiftversion 5.9 --header "NetworkReachability\n{file}\n\nMIT License\n\nCopyright (c) 2021 Varun Santhanam\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\n\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." \ No newline at end of file diff --git a/Package.resolved b/Package.resolved index c3ac26c3..41f2ca8b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nicklockwood/SwiftFormat", "state" : { - "revision" : "fee7e1a1ac9518cfe3c4673382368641ccbdc62c", - "version" : "0.51.7" + "revision" : "9e5d0d588ab6e271fe9887ec3dde21d544d4b080", + "version" : "0.53.5" } } ], diff --git a/Package.swift b/Package.swift index cd9ea56f..7ff4646b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.8 +// swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,10 +6,10 @@ import PackageDescription let package = Package( name: "NetworkReachability", platforms: [ - .iOS(.v11), - .macOS(.v10_13), - .tvOS(.v11), - .watchOS(.v5) + .iOS(.v12), + .macOS(.v10_14), + .tvOS(.v12), + .watchOS(.v6) ], products: [ .library( @@ -19,7 +19,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(url: "https://github.com/nicklockwood/SwiftFormat", exact: "0.51.7") + .package(url: "https://github.com/nicklockwood/SwiftFormat", exact: "0.53.5") ], targets: [ .target( diff --git a/Sources/NetworkReachability/API/Extensions/NotificationExtensions.swift b/Sources/NetworkReachability/API/Extensions/NotificationExtensions.swift index 3c73b190..c62265b3 100644 --- a/Sources/NetworkReachability/API/Extensions/NotificationExtensions.swift +++ b/Sources/NetworkReachability/API/Extensions/NotificationExtensions.swift @@ -25,15 +25,9 @@ import Foundation -@available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) +@available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) public extension Notification.Name { /// A notification posted by a ``NetworkMonitor`` when its network path changes. - @available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) static let networkPathChanged: Notification.Name = NetworkMonitor.networkPathChangedNotificationName - - #if !os(watchOS) - /// A notification posted by a ``ReachabilityMonitor`` when its reachability gchanges. - static let reachabilityChanged: Notification.Name = ReachabilityMonitor.reachabilityChangedNotificationName - #endif } diff --git a/Sources/NetworkReachability/API/Extensions/PublishersExtensions.swift b/Sources/NetworkReachability/API/Extensions/PublishersExtensions.swift index bcfe5781..0e01c8e3 100644 --- a/Sources/NetworkReachability/API/Extensions/PublishersExtensions.swift +++ b/Sources/NetworkReachability/API/Extensions/PublishersExtensions.swift @@ -32,9 +32,5 @@ /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) of network path updates typealias NetworkPathPublisher = NetworkMonitor.Publisher - #if !os(watchOS) - /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) of reachability updates - typealias ReachabilityPublisher = ReachabilityMonitor.Publisher - #endif } #endif diff --git a/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor+Combine.swift b/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor+Combine.swift index 718bb258..62b98891 100644 --- a/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor+Combine.swift +++ b/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor+Combine.swift @@ -128,12 +128,12 @@ func request(_ demand: Subscribers.Demand) { requested += 1 monitor = .withContinuation(pathMonitor: pathMonitor) { [weak self] path in - guard let self = self, - let subscriber = self.subscriber, - self.requested > .none else { return } - self.requested -= .max(1) + guard let self, + let subscriber, + requested > .none else { return } + requested -= .max(1) let newDemand = subscriber.receive(path) - self.requested += newDemand + requested += newDemand } } diff --git a/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor.swift b/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor.swift index 854119b7..c9636667 100644 --- a/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor.swift +++ b/Sources/NetworkReachability/API/NetworkMonitor/NetworkMonitor.swift @@ -87,7 +87,7 @@ import Network /// - ``networkPathPublisher(requiringInterfaceType:)`` /// - ``networkPathPublisher(prohibitingInterfaceTypes:)`` /// - ``Publisher`` -@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) +@available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) public final class NetworkMonitor { // MARK: - Initializers @@ -340,7 +340,7 @@ public final class NetworkMonitor { let monitor = NWPathMonitor() monitor.pathUpdateHandler = { path in monitor.cancel() - if let dispatchQueue = dispatchQueue { + if let dispatchQueue { dispatchQueue.async { completionHandler(path) } @@ -418,14 +418,14 @@ public final class NetworkMonitor { } private func forward(path: NWPath) { - if let continuation = continuation { + if let continuation { continuation(path) - } else if let updateQueue = updateQueue { + } else if let updateQueue { updateQueue.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.updateDelegate(path: path) - self.updateHandler?(self, path) + guard let self else { return } + postNotification() + updateDelegate(path: path) + updateHandler?(self, path) } } else if #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) { Task { @@ -437,10 +437,10 @@ public final class NetworkMonitor { } } else { DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.updateDelegate(path: path) - self.updateHandler?(self, path) + guard let self else { return } + postNotification() + updateDelegate(path: path) + updateHandler?(self, path) } } } diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/Reachability.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/Reachability.swift deleted file mode 100644 index 9bc3f033..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/Reachability.swift +++ /dev/null @@ -1,127 +0,0 @@ -// NetworkReachability -// Reachability.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import SystemConfiguration - - /// A value type representing network reachability - @available(macOS 10.13, iOS 11.0, tvOS 11.0, *) - public struct Reachability: Equatable, Hashable, Sendable, CustomStringConvertible { - - /// Create a reachability wrapper from system configuration reachability flags - /// - Parameter flags: The flags to wrap - public init(flags: SCNetworkReachabilityFlags?) { - self.flags = flags - } - - /// A reachability that cannot be determined - public static let unknown: Reachability = .init(flags: nil) - - /// An enumeration representing the various connection status options of a ``Reachability`` - public enum Status: Equatable, Hashable, Sendable, CustomStringConvertible { - - // MARK: - Cases - - /// The reachability is unknown - case unknown - - /// The network is unavailable - case unavailable - - /// The network is available via a cellular wwan connection - case wwan - - /// The network is available via a local wlan connection - case wlan - - // MARK: - API - - /// Whether or not the status is reachable - public var isReachable: Bool { - switch self { - case .wlan, .wwan: - return true - case .unavailable, .unknown: - return false - } - } - - // MARK: - CustomStringConvertible - - /// A textual representation of this instance. - public var description: String { - switch self { - case .unknown: - return "Unknown Reachability Status" - case .unavailable: - return "Network Offline" - case .wwan: - return "Cellular Network Online" - case .wlan: - return "Local Network Online" - } - } - } - - /// The `SCNetworkReachabilityFlag` that this reachability is based on - public let flags: SCNetworkReachabilityFlags? - - /// The connection status of the reachability - public var status: Status { - flags?.status ?? .unknown - } - - // MARK: - Equatable - - /// Returns a Boolean value indicating whether Reachability values are equal. - /// - /// Equality is the inverse of inequality. For any values a and b, a == b implies that a != b is false. - /// - /// - Parameters: - /// - lhs: A value to compare - /// - rhs: Another value to compare - /// - /// - Returns: `true` if the values are equal, otherwise `false` - public static func == (lhs: Reachability, rhs: Reachability) -> Bool { - lhs.flags == rhs.flags - } - - // MARK: - Hashable - - /// Hashes the essential components of this value by feeding them into the given hasher. - /// - Parameter hasher: The hasher to use when combining the components of this instance. - public func hash(into hasher: inout Hasher) { - hasher.combine(flags?.copyDescription ?? "") - } - - // MARK: - CustomStringConvertible - - /// A textual representation of this instance. - public var description: String { - (flags?.copyDescription ?? "No Flags") + " | " + status.description - } - - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Combine.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Combine.swift deleted file mode 100644 index ac19b6ce..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Combine.swift +++ /dev/null @@ -1,172 +0,0 @@ -// NetworkReachability -// ReachabilityMonitor+Combine.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if canImport(Combine) && !os(watchOS) - import Combine - import Darwin - import SystemConfiguration - - @available(macOS 10.15, iOS 13, tvOS 13, *) - public extension ReachabilityMonitor { - - /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) of reachability updates - /// - /// Use this property to observe reachability updates with [Combine](https://developer.apple.com/documentation/combine). - /// - /// ```swift - /// let cancellable = ReachabilityMonitor.reachabilityPublisher - /// .map(\.status.isReachable) - /// .removeDuplicates() - /// .replaceError(with: false) - /// .sink { isReachable in - /// // Do something with `isReachable` - /// } - /// ``` - static var reachabilityPublisher: Publishers.ReachabilityPublisher { - .init { try .general } - } - - /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) of reachability updates for a specific host - /// - /// Use this property to observe reachability updates with [Combine](https://developer.apple.com/documentation/combine). - /// - /// ```swift - /// let cancellable = ReachabilityMonitor.reachabilityPublisher(forHost: "www.apple.com") - /// .map(\.status.isReachable) - /// .removeDuplicates() - /// .replaceError(with: false) - /// .sink { isReachable in - /// // Do something with `isReachable` - /// } - /// ``` - /// - /// - /// - Parameter host: The host you want to monitor - /// - Returns: A publisher of reachability updates for the given host - static func reachabilityPublisher(forHost host: String) -> Publishers.ReachabilityPublisher { - .init { try .forHost(host) } - } - - /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) of reachability updates for a specific socket address - /// - /// Use this property to observe reachability updates with [Combine](https://developer.apple.com/documentation/combine). - /// - /// ```swift - /// let cancellable = ReachabilityMonitor.reachabilityPublisher(forHost: "www.apple.com") - /// .map(\.status.isReachable) - /// .removeDuplicates() - /// .replaceError(with: false) - /// .sink { isReachable in - /// // Do something with `isReachable` - /// } - /// ``` - /// - /// - /// - Parameter address: The socket address you want to monitor - /// - Returns: A publisher of reachability updates for the given host - static func reachabilityPublisher(forAddress address: sockaddr) -> Publishers.ReachabilityPublisher { - .init { try .forAddress(address) } - } - - /// A [`Publisher`](https://developer.apple.com/documentation/combine/publisher) used to observe reachability updates for use with [Combine](https://developer.apple.com/documentation/combine). - /// - /// See ``reachabilityPublisher`` for usage. - struct Publisher: Combine.Publisher { - - // MARK: - Initializers - - init(_ refBuilder: @escaping () throws -> SCNetworkReachability) { - self.refBuilder = refBuilder - } - - // MARK: - Publisher - - /// The kind of values published by this publisher. - public typealias Output = Reachability - - /// The kind of errors this publisher might publish. - public typealias Failure = Swift.Error - - /// Attaches the specified subscriber to this publisher. - /// - Parameter subscriber: The subscriber to attach to this [`Publisher`](https://developer.apple.com/documentation/combine/publisher), after which it can receive values. - public func receive(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output { - let subscription = ReachabilitySubscription(subscriber: subscriber, refBuilder: refBuilder) - subscriber.receive(subscription: subscription) - } - - // MARK: - Private - - private let refBuilder: () throws -> SCNetworkReachability - - } - - private final class ReachabilitySubscription: Subscription where S.Input == Reachability, S.Failure == Swift.Error { - - // MARK: - Initializers - - init(subscriber: S?, - refBuilder: @escaping () throws -> SCNetworkReachability) { - self.subscriber = subscriber - self.refBuilder = refBuilder - } - - // MARK: - Subscription - - func request(_ demand: Subscribers.Demand) { - requested += 1 - do { - networkMonitor = try .withContinuation(refBuilder()) { [weak self] result in - guard let self = self, - let subscriber = self.subscriber, - self.requested > .none else { return } - self.requested -= .max(1) - do { - let reachability = try result.get() - let newDemand = subscriber.receive(reachability) - self.requested += newDemand - } catch { - subscriber.receive(completion: .failure(error)) - } - } - } catch { - subscriber?.receive(completion: .failure(error)) - } - } - - func cancel() { - networkMonitor = nil - } - - // MARK: - Private - - private var subscriber: S? - private var networkMonitor: ReachabilityMonitor? - private var requested: Subscribers.Demand = .none - private var refBuilder: () throws -> SCNetworkReachability - - } - - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Concurrency.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Concurrency.swift deleted file mode 100644 index 027c551b..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Concurrency.swift +++ /dev/null @@ -1,115 +0,0 @@ -// NetworkReachability -// ReachabilityMonitor+Concurrency.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import Darwin - import SystemConfiguration - - @available(macOS 10.15, iOS 13.0, tvOS 13.0, *) - public extension ReachabilityMonitor { - - /// An [`AsyncSequence`](https://developer.apple.com/documentation/swift/asyncsequence) of reachability updates - /// - /// Use [Swift Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) to iterate over reachability updates in an asynchronous context. - /// - /// ```swift - /// func observe() async throws { - /// do { - /// for try await reachability in NetworkMonitor.reachabilityUpdates { - /// // Do something with `reachability` - /// } - /// } catch { - /// // Handle error - /// } - /// } - /// ``` - static var reachabilityUpdates: AsyncThrowingStream { - stream { try .general } - } - - /// An [`AsyncSequence`](https://developer.apple.com/documentation/swift/asyncsequence) of reachability updates for a specific host - /// - /// Use [Swift Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) to iterate over reachability updates in an asynchronous context. - /// - /// ```swift - /// func observe() async throws { - /// do { - /// for try await reachability in NetworkMonitor.reachabilityUpdates(forHost: www.apple.com) { - /// // Do something with `reachability` - /// } - /// } catch { - /// // Handle error - /// } - /// } - /// ``` - /// - /// - Parameter host: The host you want to monitor - /// - Returns: An `AsyncSequence` of reachability updates for a given host - static func reachabilityUpdates(forHost host: String) -> AsyncThrowingStream { - stream { try .forHost(host) } - } - - /// An [`AsyncSequence`](https://developer.apple.com/documentation/swift/asyncsequence) of reachability updates for a specific socket address - /// - /// Use [Swift Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) to iterate over reachability updates in an asynchronous context. - /// - /// ```swift - /// func observe() async throws { - /// do { - /// for try await reachability in NetworkMonitor.reachabilityUpdates(forAddress: myAddress) { - /// // Do something with `reachability` - /// } - /// } catch { - /// // Handle error - /// } - /// } - /// ``` - /// - /// - Parameter address: The socket address you want to monitor - /// - Returns: An `AsyncSequence` of reachability updates for a given host - static func reachabilityUpdates(forAddress address: sockaddr) -> AsyncThrowingStream { - stream { try .forAddress(address) } - } - - } - - @available(macOS 10.15, iOS 13.0, tvOS 13.0, *) - private func stream(_ refBuilder: () throws -> SCNetworkReachability) -> AsyncThrowingStream { - .init(bufferingPolicy: .bufferingNewest(1)) { continuation in - do { - _ = try ReachabilityMonitor.withContinuation(refBuilder()) { result in - do { - let reachability = try result.get() - continuation.yield(reachability) - } catch { - continuation.finish(throwing: error) - } - } - } catch { - continuation.finish(throwing: error) - } - } - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Error.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Error.swift deleted file mode 100644 index f5d7904f..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Error.swift +++ /dev/null @@ -1,122 +0,0 @@ -// NetworkReachability -// ReachabilityMonitor+Error.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import Foundation - - @available(macOS 10.13, iOS 11.0, tvOS 11.0, *) - public extension ReachabilityMonitor { - - /// Errors that could cause a ``ReachabilityMonitor`` to fail - enum Error: LocalizedError, Equatable, Hashable, Sendable, CustomStringConvertible { - - // MARK: - Cases - - /// An error indicating the SystemConfiguration reachability monitor could not be initialized - case failedToCreate(Int32) - - /// An error indicating the reachability callback could not be configured - case failedToStartCallback(Int32) - - /// An error indicating the rachability observation could not be scheduled - case failedToSetDispatchQueue(Int32) - - /// An error indicating the reachability couldn't be obtained from the system - case failedToGetFlags(Int32) - - /// An unknown error - case unknown - - // MARK: - API - - /// The SCError Code associated with the error - public var code: Int32 { - switch self { - case let .failedToCreate(code): - return code - case let .failedToStartCallback(code): - return code - case let .failedToSetDispatchQueue(code): - return code - case let .failedToGetFlags(code): - return code - case .unknown: - return -1 - } - } - - // MARK: - LocalizedError - - /// A localized message describing what error occurred. - public var errorDescription: String? { - switch self { - case .failedToCreate: - return "Couldn't create system reachability reference" - case .failedToStartCallback: - return "Couldn't start system observability callback" - case .failedToSetDispatchQueue: - return "Couldn't schedule system observability callback" - case .failedToGetFlags: - return "Couldn't get system reachability flags" - case .unknown: - return "Unknown ReachabilityMonitor Failure" - } - } - - /// A localized message describing the reason for the failure. - public var failureReason: String? { - switch self { - case let .failedToCreate(code): - return "SCError Code \(code)" - case let .failedToStartCallback(code): - return "SCError Code \(code)" - case let .failedToSetDispatchQueue(code): - return "SCError Code \(code)" - case let .failedToGetFlags(code): - return "SCError Code \(code)" - case .unknown: - return "SCError Code Unknown" - } - } - - /// A localized message describing how one might recover from the failure. - public var recoverySuggestion: String? { - "There is not a lot you can do with this error at runtime, but it can help you with debugging" - } - - /// A localized message providing "help" text if the user requests help. - public var helpAnchor: String? { - "If you think error was caused by a bug in the library, create an issue on GitHub and include this information." - } - - // MARK: - CustomStringConvertible - - /// A textual representation of this instance. - public var description: String { - [errorDescription!, " ", failureReason!].joined() - } - } - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Notification.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Notification.swift deleted file mode 100644 index 359a2302..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor+Notification.swift +++ /dev/null @@ -1,36 +0,0 @@ -// NetworkReachability -// ReachabilityMonitor+Notification.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import Foundation - - @available(macOS 10.13, iOS 11.0, tvOS 11.0, *) - public extension ReachabilityMonitor { - - /// A notification posted by a ``ReachabilityMonitor`` when its reachability changes. - static let reachabilityChangedNotificationName = Notification.Name("reachabilityChanged") - - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor.swift deleted file mode 100644 index 5f6c58b6..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitor.swift +++ /dev/null @@ -1,643 +0,0 @@ -// NetworkReachability -// ReachabilityMonitor.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import Darwin - import Foundation - import SystemConfiguration - - /// A class used to observe network reachability changes - /// - /// Create an instance of this object and retain it in memory. - /// - /// You can observe reachability changes in several ways: - /// - Synchronously, using the ``currentReachability`` instance property or the ``reachability`` static property. - /// - Using delegation via ``ReachabilityMonitorDelegate``. - /// - Using [structured concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) via the ``reachabilityUpdates`` properties - /// - Using [Combine](https://developer.apple.com/documentation/combine), via the ``reachabilityPublisher`` property. - /// - Using a provided closure via the ``updateHandler-swift.property`` property. - /// - Using notification observers on [`NotificationCenter.default`](https://developer.apple.com/documentation/foundation/notificationcenter). - /// - /// ## Topics - /// - /// ### Initializers - /// - /// - ``init()`` - /// - ``init(host:)`` - /// - ``init(address:)`` - /// - ``init(updateHandler:)`` - /// - ``init(updateQueue:updateHandler:)`` - /// - ``init(host:updateHandler:)`` - /// - ``init(host:updateQueue:updateHandler:)`` - /// - ``init(address:updateHandler:)`` - /// - ``init(address:updateQueue:updateHandler:)`` - /// - ``init(delegate:)`` - /// - ``init(updateQueue:delegate:)`` - /// - ``init(host:delegate:)`` - /// - ``init(host:updateQueue:delegate:)`` - /// - ``init(address:delegate:)`` - /// - ``init(address:updateQueue:delegate:)`` - /// - /// ### Closure Callbacks - /// - /// - ``updateHandler-swift.property`` - /// - ``UpdateHandler-swift.typealias`` - /// - ``Result`` - /// - ``Error`` - /// - /// ### Swift Concurrency - /// - /// - ``reachability`` - /// - ``reachabilityUpdates`` - /// - ``reachabilityUpdates(forHost:)`` - /// - ``reachabilityUpdates(forAddress:)`` - /// - /// ### Delegation - /// - /// - ``Delegate-swift.typealias`` - /// - ``delegate-swift.property`` - /// - /// ### Notifications - /// - /// - ``reachabilityChangedNotificationName`` - /// - /// ### Combine - /// - /// - ``reachabilityPublisher`` - /// - ``reachabilityPublisher(forHost:)`` - /// - ``reachabilityPublisher(forAddress:)`` - /// - ``Publisher`` - /// - /// ### Synchronous - /// - /// - ``currentReachability`` - /// - ``reachability`` - /// - ``reachability(forHost:)`` - /// - ``reachability(forAddress:)`` - @available(macOS 10.13, iOS 11, tvOS 11, *) - public final class ReachabilityMonitor { - - // MARK: - Initializers - - /// Create a reachability monitor - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init() throws { - try self.init(ref: .general, - updateHandler: nil, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachablity monitor for a specific host - /// - /// - Parameter host: The host to monitor - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(host: String) throws { - try self.init(ref: .forHost(host), - updateHandler: nil, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachablity monitor for a socket address - /// - /// - Parameter address: The socket address to monitor - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(address: sockaddr) throws { - try self.init(ref: .forAddress(address), - updateHandler: nil, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachability monitor with a closure used to respond to reachability changes - /// - /// - Parameter updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .general, - updateHandler: updateHandler, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachability monitor with a closure used to respond to reachability changes on a specific queue - /// - /// - Parameters: - /// - updateQueue: Dispatch queue used to invoke the update handler - /// - updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error - public convenience init(updateQueue: DispatchQueue, - updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .general, - updateHandler: updateHandler, - delegate: nil, - updateQueue: updateQueue) - } - - /// Create a reachability monitor for a specific host with a closure used to respond to reachability changes - /// - /// - Parameters: - /// - host: The host to monitor - /// - updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(host: String, - updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .forHost(host), - updateHandler: updateHandler, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachability monitor for a specific host with a closure used to respond to reachability changes on a specific queue - /// - /// - Parameters: - /// - host: The host to monitor - /// - updateQueue: Dispatch queue used to invoke the update handler - /// - updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - - public convenience init(host: String, - updateQueue: DispatchQueue, - updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .forHost(host), - updateHandler: updateHandler, - delegate: nil, - updateQueue: updateQueue) - } - - /// Create a reachability monitor for a specific socket address with a closure used to respond to reachability changes - /// - /// - Parameters: - /// - address: The socket address to monitor - /// - updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(address: sockaddr, - updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .forAddress(address), - updateHandler: updateHandler, - delegate: nil, - updateQueue: nil) - } - - /// Create a reachability monitor for a specific socket address with a closure used to respond to reachability changes on a specific queue - /// - /// - Parameters: - /// - address: The socket address to monitor - /// - updateQueue: Dispatch queue used to invoke the update handler - /// - updateHandler: The closure used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(address: sockaddr, - updateQueue: DispatchQueue, - updateHandler: @escaping UpdateHandler) throws { - try self.init(ref: .forAddress(address), - updateHandler: updateHandler, - delegate: nil, - updateQueue: updateQueue) - } - - /// Create a reachability monitor with a delegate object used to respond to reachability changes - /// - /// - Parameter delegate: The delegate object used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(delegate: any Delegate) throws { - try self.init(ref: .general, - updateHandler: nil, - delegate: delegate, - updateQueue: nil) - } - - /// Create a reachability monitor with a delegate object used to respond to reachability changes - /// - /// - Parameters: - /// - updateQueue: Dispatch queue used to invoke the delegate callbacks - /// - delegate: The delegate object used to observe reachability updates - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(updateQueue: DispatchQueue, - delegate: any Delegate) throws { - try self.init(ref: .general, - updateHandler: nil, - delegate: delegate, - updateQueue: updateQueue) - } - - /// Create a reachability monitor for a specific host with a delegate object used to respond to reachability changes - /// - /// - Parameters: - /// - host: The host to monitor - /// - delegate: The delegate object used to observe reachability update - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(host: String, - delegate: any Delegate) throws { - try self.init(ref: .forHost(host), - updateHandler: nil, - delegate: delegate, - updateQueue: nil) - } - - /// Create a reachability monitor for a specific host with a delegate object used to respond to reachability changes - /// - /// - Parameters: - /// - host: The host to monitor - /// - updateQueue: Dispatch queue used to invoke the delegate callbacks - /// - delegate: The delegate object used to observe reachability update - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(host: String, - updateQueue: DispatchQueue, - delegate: any Delegate) throws { - try self.init(ref: .forHost(host), - updateHandler: nil, - delegate: delegate, - updateQueue: updateQueue) - } - - /// Create a reachability monitor for a specific socket address with a delegate object used to respond to reachability changes - /// - /// - Parameters: - /// - address: The socket address to monitor - /// - delegate: The delegate object used to observe reachability update - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(address: sockaddr, - delegate: any Delegate) throws { - try self.init(ref: .forAddress(address), - updateHandler: nil, - delegate: delegate, - updateQueue: nil) - } - - /// Create a reachability monitor for a specific socket address with a delegate object used to respond to reachability changes - /// - /// - Parameters: - /// - address: The socket address to monitor - /// - updateQueue: Dispatch queue used to invoke the delegate callbacks - /// - delegate: The delegate object used to observe reachability update - /// - /// - Note: A reachability monitor begins observing and publishing reachability updates immediately after initialization. - /// - /// - Throws: An error of type ``Error`` - public convenience init(address: sockaddr, - updateQueue: DispatchQueue, - delegate: any Delegate) throws { - try self.init(ref: .forAddress(address), - updateHandler: nil, - delegate: delegate, - updateQueue: updateQueue) - } - - // MARK: - API - - /// Specialized [`Result`](https://developer.apple.com/documentation/swift/result) delivered by a ``ReachabilityMonitor`` to it's ``updateHandler-swift.property`` - public typealias Result = Swift.Result - - /// The closure type used to observe reachability updates - public typealias UpdateHandler = (ReachabilityMonitor, ReachabilityMonitor.Result) -> Void - - /// A protocol used to observe network path changes from a ``ReachabilityMonitor`` - public typealias Delegate = ReachabilityMonitorDelegate - - /// The closure used to observe reachability updates - /// - /// The handler is fed `Result` types and should be used to handle reachability changes as well as errors - /// - /// ```swift - /// func setUpdateHandler(on monitor: ReachabilityMonitor) { - /// let updateHandler: ReachabilityMonitor.UpdateHandler = { (monitor: ReachabilityMonitor, result: ReachabilityMonitor.Result) in - /// do { - /// let reachability = try result.get() - /// // Do something with `reachability` - /// } catch { - /// // Handle error - /// } - /// } - /// monitor.updateHandler = updateHandler - /// } - /// ``` - /// - /// - Tip: The closure only recieves status changes that occured after it was assigned. To recieve every status update, including the reachability status at the time the monitor was initialized, pass in the closure on initialization of the monitor. - /// - /// - Note: Instances of ``ReachabilityMonitor`` will always invoke this closure on the ``updateQueue`` - public var updateHandler: UpdateHandler? - - /// The delegate object used to observe reachability updates - /// - /// See ``Delegate-swift.typealias`` for more information. - /// - /// - Tip: The delegate only recieves status changes that occured after it was assigned. To recieve every status update, including the reachability status at the time the monitor was initialized, pass in the delegate on initialization of the monitor. - /// - /// - Important: Instances of ``ReachabilityMonitor`` will perform delegate callbacks on the ``updateQueue`` - public weak var delegate: (any Delegate)? - - /// The dispatch queue used to send closure callbacks, delegate callbacks, and notifications. - /// - /// Set this value to `nil` to use the main thread. - public var updateQueue: DispatchQueue? - - /// The current reachability status - /// - /// Use this property to retrieve the current reachability reachability in a synchronous context: - /// - /// ```swift - /// do { - /// let reachability = try monitor.reachability - /// // Do something with `reachability` - /// } catch { - /// // Handle error - /// } - /// ``` - /// - /// - Throws: An error of type ``Error`` - public var currentReachability: Reachability { - get throws { - refreshFlags() - return try Reachability(flags: flags.get()) - } - } - - /// Retrieve the latest known reachability. - /// - /// ```swift - /// func updateReachability() throws { - /// let reachability = try ReachabilityMonitor.reachability - /// // Do something with `reachability` - /// } - /// ``` - /// - /// - Throws: An error of type ``Error`` - public static var reachability: Reachability { - get throws { - let monitor = try ReachabilityMonitor() - return try monitor.currentReachability - } - } - - /// Retrieve the latest known reachability for a specific host - /// - /// ``` - /// func updateReachability() throws { - /// let reachability = try ReachabilityMonitor.reachability(forHost: "www.apple.com") - /// // Do something with `reachability` - /// } - /// ``` - /// - /// - Parameter host: The host you want to monitor - /// - /// - Returns: The latest known reachability for the provided host - /// - /// - Throws: An error of type ``Error`` - public static func reachability(forHost host: String) throws -> Reachability { - let monitor = try ReachabilityMonitor(host: host) - return try monitor.currentReachability - } - - /// Retrieve the latest known reachability for a specific socket address - /// - /// ``` - /// func updateReachability() throws { - /// let reachability = try ReachabilityMonitor.reachability(forAddress: myAddress) - /// // Do something with `reachability` - /// } - /// ``` - /// - /// - Parameter host: The socket address you want to monitor - /// - /// - Returns: The latest known reachability for the provided host - /// - /// - Throws: An error of type ``Error`` - public static func reachability(forAddress address: sockaddr) throws -> Reachability { - let monitor = try ReachabilityMonitor(address: address) - return try monitor.currentReachability - } - - // MARK: - Private - - static func withContinuation(_ ref: SCNetworkReachability, - continuation: @escaping (Result) -> Void) -> ReachabilityMonitor { - ReachabilityMonitor(ref: ref, - updateHandler: nil, - delegate: nil, - continuation: continuation, - updateQueue: nil) - } - - private init(ref: SCNetworkReachability, - updateHandler: UpdateHandler?, - delegate: (any Delegate)?, - continuation: ((Result) -> Void)? = nil, - updateQueue: DispatchQueue?) { - self.ref = ref - self.updateHandler = updateHandler - self.delegate = delegate - self.continuation = continuation - self.updateQueue = updateQueue - setUp() - } - - private var ref: SCNetworkReachability - private var continuation: ((Result) -> Void)? - - private var flags: Swift.Result = .success(nil) { - didSet { - if flags != oldValue { - do { - let flags = try flags.get() - succeed(with: flags) - } catch { - if let error = error as? Error { - fail(with: error) - } else { - fail(with: .unknown) - } - } - } - } - } - - private func setUp() { - let callback: SCNetworkReachabilityCallBack = { (reachability, flags, info) in - guard let info = info else { return } - let weakMonitor = Unmanaged>.fromOpaque(info).takeUnretainedValue() - weakMonitor.obj?.flags = .success(flags) - } - - let weakMonitor = weak(self) - let opaqueMonitor = Unmanaged>.passUnretained(weakMonitor).toOpaque() - - var context = SCNetworkReachabilityContext( - version: 0, - info: UnsafeMutableRawPointer(opaqueMonitor), - retain: { info in - let unmanaged = Unmanaged>.fromOpaque(info) - _ = unmanaged.retain() - return UnsafeRawPointer(unmanaged.toOpaque()) - }, - release: { info in - let unmanaged = Unmanaged>.fromOpaque(info) - unmanaged.release() - }, - copyDescription: { info in - let unmanagedMonitor = Unmanaged>.fromOpaque(info) - let weakMonitor = unmanagedMonitor.takeUnretainedValue() - let copyDescription = try? weakMonitor.obj?.flags.get()?.copyDescription ?? "none" - return Unmanaged.passRetained(copyDescription! as CFString) - } - ) - - if !SCNetworkReachabilitySetCallback(ref, callback, &context) { - flags = .failure(.failedToStartCallback(SCError())) - } else if !SCNetworkReachabilitySetDispatchQueue(ref, .reachabilityMonitorQueue) { - flags = .failure(.failedToSetDispatchQueue(SCError())) - } else { - refreshFlags() - } - } - - private func refreshFlags() { - var flags = SCNetworkReachabilityFlags() - if SCNetworkReachabilityGetFlags(ref, &flags) { - self.flags = .success(flags) - } else { - self.flags = .failure(.failedToGetFlags(SCError())) - } - } - - private func succeed(with flags: SCNetworkReachabilityFlags?) { - let reachability = Reachability(flags: flags) - if let continuation = continuation { - continuation(.success(reachability)) - } else if let updateQueue = updateQueue { - updateQueue.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.updateDelegate(reachability: reachability) - self.updateHandler?(self, .success(reachability)) - } - } else if #available(macOS 10.15, iOS 13, tvOS 13, *) { - Task { - await MainActor.run { - postNotification() - updateDelegate(reachability: reachability) - updateHandler?(self, .success(reachability)) - } - } - } else { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.updateDelegate(reachability: reachability) - self.updateHandler?(self, .success(reachability)) - } - } - } - - private func fail(with error: Error) { - if let continuation = continuation { - continuation(.failure(error)) - } else if let updateQueue = updateQueue { - updateQueue.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.failDelegate(error) - self.updateHandler?(self, .failure(error)) - } - } else if #available(macOS 10.15, iOS 13, tvOS 13, *) { - Task { - await MainActor.run { - postNotification() - failDelegate(error) - updateHandler?(self, .failure(error)) - } - } - } else { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - self.postNotification() - self.failDelegate(error) - self.updateHandler?(self, .failure(error)) - } - } - } - - private func postNotification() { - NotificationCenter.default.post(name: .reachabilityChanged, object: self) - } - - private func updateDelegate(reachability: Reachability) { - delegate?.reachabilityMonitor(self, didUpdateReachability: reachability) - } - - private func failDelegate(_ error: Error) { - delegate?.reachabilityMonitor(self, didFailWithError: error) - } - - // MARK: - Deinit - - deinit { - SCNetworkReachabilitySetCallback(ref, nil, nil) - SCNetworkReachabilitySetDispatchQueue(ref, nil) - } - } -#endif diff --git a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitorDelegate.swift b/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitorDelegate.swift deleted file mode 100644 index 297d478d..00000000 --- a/Sources/NetworkReachability/API/ReachabilityMonitor/ReachabilityMonitorDelegate.swift +++ /dev/null @@ -1,44 +0,0 @@ -// NetworkReachability -// ReachabilityMonitorDelegate.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - - /// A protocol used to observe network reachability changes from a ``ReachabilityMonitor`` - @available(macOS 10.13, iOS 11.0, tvOS 11.0, *) - public protocol ReachabilityMonitorDelegate: AnyObject { - - /// Sent to the delegate when the reachability changes - /// - Parameters: - /// - monitor: The reachability monitor who's eachability changed - /// - reachability: The new reachability - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didUpdateReachability reachability: Reachability) - - /// Sent to the delegate when the network monitor failed with an error - /// - Parameters: - /// - monitor: The reachability monitor that failed - /// - error: The error that caused the monitor to fail - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didFailWithError error: Error) - } -#endif diff --git a/Sources/NetworkReachability/Internal/DispatchQueue+NetworkReachability.swift b/Sources/NetworkReachability/Internal/DispatchQueue+NetworkReachability.swift index 78701ebf..aa4d72f3 100644 --- a/Sources/NetworkReachability/Internal/DispatchQueue+NetworkReachability.swift +++ b/Sources/NetworkReachability/Internal/DispatchQueue+NetworkReachability.swift @@ -28,11 +28,10 @@ import Foundation extension DispatchQueue { private static func frameworkQueue(_ name: String, fallback: String) -> DispatchQueue { - let label: String - if let bundleIdentifier = Bundle.main.bundleIdentifier { - label = [bundleIdentifier, name].joined(separator: ".") + let label: String = if let bundleIdentifier = Bundle.main.bundleIdentifier { + [bundleIdentifier, name].joined(separator: ".") } else { - label = [fallback, name].joined(separator: ".") + [fallback, name].joined(separator: ".") } let queue = DispatchQueue(label: label) @@ -47,8 +46,4 @@ extension DispatchQueue { networkReachabilityQueue(name: "NetworkMonitor") } - static var reachabilityMonitorQueue: DispatchQueue { - networkReachabilityQueue(name: "ReachabilityMonitor") - } - } diff --git a/Sources/NetworkReachability/Internal/SCNetworkReachability+NetworkReachability.swift b/Sources/NetworkReachability/Internal/SCNetworkReachability+NetworkReachability.swift deleted file mode 100644 index ada8bae5..00000000 --- a/Sources/NetworkReachability/Internal/SCNetworkReachability+NetworkReachability.swift +++ /dev/null @@ -1,55 +0,0 @@ -// NetworkReachability -// SCNetworkReachability+NetworkReachability.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import SystemConfiguration - - extension SCNetworkReachability { - - static var general: SCNetworkReachability { - get throws { - var zeroAddress = sockaddr() - zeroAddress.sa_len = UInt8(MemoryLayout.size) - zeroAddress.sa_family = sa_family_t(AF_INET) - return try .forAddress(zeroAddress) - } - } - - static func forAddress(_ address: sockaddr) throws -> SCNetworkReachability { - var address = address - guard let reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, &address) else { - throw ReachabilityMonitor.Error.failedToCreate(SCError()) - } - return reachability - } - - static func forHost(_ host: String) throws -> SCNetworkReachability { - guard let reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, host) else { - throw ReachabilityMonitor.Error.failedToCreate(SCError()) - } - return reachability - } - } -#endif diff --git a/Sources/NetworkReachability/Internal/SCNetworkReachabilityFlags+NetworkReachability.swift b/Sources/NetworkReachability/Internal/SCNetworkReachabilityFlags+NetworkReachability.swift deleted file mode 100644 index 58d95d13..00000000 --- a/Sources/NetworkReachability/Internal/SCNetworkReachabilityFlags+NetworkReachability.swift +++ /dev/null @@ -1,114 +0,0 @@ -// NetworkReachability -// SCNetworkReachabilityFlags+NetworkReachability.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - import SystemConfiguration - - extension SCNetworkReachabilityFlags { - - var status: Reachability.Status { - guard isReachableFlagSet else { return .unavailable } - var connection = Reachability.Status.unavailable - - if !isConnectionRequiredFlagSet { - connection = .wlan - } - - if isConnectionOnTrafficOrDemandFlagSet { - if !isInterventionRequiredFlagSet { - connection = .wlan - } - } - - if isOnWWANFlagSet { - connection = .wwan - } - - return connection - } - - var isOnWWANFlagSet: Bool { - #if os(iOS) - contains(.isWWAN) - #else - false - #endif - } - - var isReachableFlagSet: Bool { - contains(.reachable) - } - - var isConnectionRequiredFlagSet: Bool { - contains(.connectionRequired) - } - - var isInterventionRequiredFlagSet: Bool { - contains(.interventionRequired) - } - - var isConnectionOnTrafficFlagSet: Bool { - contains(.connectionOnTraffic) - } - - var isConnectionOnDemandFlagSet: Bool { - contains(.connectionOnDemand) - } - - var isConnectionOnTrafficOrDemandFlagSet: Bool { - !intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty - } - - var isTransientConnectionFlagSet: Bool { - contains(.transientConnection) - } - - var isLocalAddressFlagSet: Bool { - contains(.isLocalAddress) - } - - var isDirectFlagSet: Bool { - contains(.isDirect) - } - - var isConnectionRequiredAndTransientFlagSet: Bool { - intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] - } - - var copyDescription: 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)" - } - } -#endif diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Articles/GettingStarted.md b/Sources/NetworkReachability/NetworkReachability.docc/Articles/GettingStarted.md index 68f17a9f..7758c9ed 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Articles/GettingStarted.md +++ b/Sources/NetworkReachability/NetworkReachability.docc/Articles/GettingStarted.md @@ -4,22 +4,9 @@ Choose between the two APIs included with NetworkReachability ## Overview -NetworkReachability consists of two main classes, ``NetworkMonitor`` and ``ReachabilityMonitor`` - ``NetworkMonitor`` is built on Apple's [Network](https://developer.apple.com/documentation/network) framework. As such, it requires iOS 12 and returns [`NWPath`](https://developer.apple.com/documentation/network/nwpath) types. -``ReachabilityMonitor`` is built on [SystemConfiguration](https://developer.apple.com/documentation/systemconfiguration), and returns a ``Reachability`` struct which wraps [``SCNetworkReachabilityFlags``](https://developer.apple.com/documentation/systemconfiguration/scnetworkreachabilityflags). As such, it does not support watchOS. - -These APIs are very similar, and can generally be used interchangeably. Like Apple's APIs, ``ReachabilityMonitor`` offers a synchronous & asynchronous APIs, and is capable of throwing errors. ``NetworkMonitor`` monitor is simpler and more powerful, but does not offer a reliable synchronous API. - -| API | Single Value | Observing Values | Throws Errors | macOS | iOS | watchOS | tvOS | -| ------------------------- | ------------ | ----------------- | ------------- | ------ | ----- | ------- | ----- | -| ``NetworkMonitor`` | Async | Async | No | 10.14+ | 12.0+ | 5.0+ | 12.0+ | -| ``ReachabilityMonitor`` | Sync | Async | Yes | 10.13+ | 11.0+ | N/A | 11.0+ | - -I recommend that you use ``NetworkMonitor`` as it both simpler and more robust, unless you need to target iOS 11 or you absolutely need a synchronous API. - -Both APIs still offer the same observability mechanisms: +The APIs still offer the same observability mechanisms: * [Delegation](https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html) * [Closures](https://docs.swift.org/swift-book/LanguageGuide/Closures.html) @@ -30,4 +17,3 @@ Both APIs still offer the same observability mechanisms: ### Learn More * To learn more about ``NetworkMonitor``, see the . -* To learn more about ``ReachabilityMonitor``, see the diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Articles/ReachabilityMonitorGuide.md b/Sources/NetworkReachability/NetworkReachability.docc/Articles/ReachabilityMonitorGuide.md deleted file mode 100644 index 43110968..00000000 --- a/Sources/NetworkReachability/NetworkReachability.docc/Articles/ReachabilityMonitorGuide.md +++ /dev/null @@ -1,204 +0,0 @@ -# Reachability Monitor Programming Guide - -Learn how to use Reaachability Monitor APIs - -## Overview - -The easiest way use ``ReachabilityMonitor`` is to use its static `reachability` property - -```swift -import NetworkReachability - -do { - let reachability = try ReachabilityMonitor.reachability - // Do something with `reachability` -} catch { - // Handle errors -} -``` - -Unlike ``NetworkMonitor``, ``ReachabilityMonitor``'s synchronous API is gauranteed to provide up-date-values. - -### Observing reachability updates - -If you need to observe all reachability changes, ``ReachabilityMonitor`` provides several asynchronous APIs that will allow you to integrate reachability data into any existing pipeline. - -##### Closures - -You can use a closure to observe reachability over time. You can pass in the closure on initialization, or add one later using the `updateHandler` property. The closure returns result types, which means they could contain errors instead of values. - -```swift -import NetworkReachability - -final class MyClass { - - var monitor: ReachabilityMonitor? - - func startMonitoring() { - stopMonitoring() - monitor = ReachabilityMonitor(updateHandler: { (monitor: ReachabilityMonitor, result: ReachabilityMonitor.Result) in - do { - let reachability = try result.get() - // Do something with `monitor` or `reachability` - } catch { - // Handle error - } - } - } - - func stopMonitoring() { - monitor = nil - } - -} -``` - -- Important: By default, instances of ``ReachabilityMonitor`` always call their update handlers on the main thread. You can change this behavior by setting the monitor's `.updateQueue` property. - -##### Swift Concurrency - -You can use an `AsyncSequence` to observe reachability updates over time using Swift Concurrency - -```swift -import NetworkReachability - -final class MyClass { - - var monitorTask: Task? - - func startObserving() { - stopObserving() - monitorTask = Task { - do { - for try await reachability in ReachabilityMonitor.reachabilityMonitorUpdates { - // Do something with `reachability` - } - } catch { - // Handle error - } - } - } - - func startObserving() { - monitorTask?.cancel() - monitorTask = nil - } -} -``` - -- Note: This API requires iOS 13, macOS 10.15, tvOS 13, or watchOS 6 - -##### Delegation - -You can use ``ReachabilityMonitorDelegate`` to recieve callbacks when the reachability changes. You can pass in a delegate object when the monitor is initialized, or you can assign one later. - -```swift -import NetworkReachability - -final class MyClass: ReachabilityMonitorDelegate { - - var monitor: ReachabilityMonitor? - - func startMonitoring() { - stopMonitoring() - monitor = ReachabilityMonitor(delegate: self) - } - - func stopMonitoring() { - monitor = nil - } - - // MARK: - ReachabilityMonitorDelegate - - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didUpdateReachability reachability: Reachability) - // Do something with `reachability` - } - - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didFailWithError error: Error) { - // Handle error - } - -} -``` - -- Important: By default, instances of ``ReachabilityMonitor`` always execute their delegate callbacks on the main thread. You can change this behavior by setting the monitor's `.updateQueue` property. - -##### NotificationCenter - -If you have retained an instance of ``ReachabilityMonitor`` in memory, but do not have access to it in the part of your code that needs reachability updates, you can -observe reachability changes by observing notifications with the name `Notification.Name.reachabilityChanged` on the default notification center. The notification's `.object` property will contain the ``ReachabilityMonitor``. From there, you can use `currentReachability` property of the monitor, which you now know will be up-to-date thanks to the notification. - -```swift -import Foundation -import NetworkReachability - -final class MyClass { - - var monitor: ReachabilityMonitor? - - func startMonitoring() { - stopMonitoring() - NotificationCenter.default.addObserver(self, selector: #selector(handleUpdate:), name: .reachabilityhChanged) - monitor = ReachabilityMonitor() - } - - func stopMonitoring() { - monitor = nil - NotificationCenter.default.removeObserver(self, name: .reachabilityChanged) - } - - @objc - func handleUpdate(_ notification: Notification) { - guard let monitor = notification.object as? ReachabilityMonitor else { - return - } - do { - let reachability = try monitor.currentReachability - } catch { - // Do something with `reachability` - } - } - - deinit { - NotificationCenter.default.removeObserver(self, name: .reachabilityChanged) - } - -} -``` - -- Important: By default, instances of ``ReachabilityMonitor`` always post their notifications on the main thread. You can change this behavior by setting the monitor's `.updateQueue` property. - -##### Combine - -You can observe reachability changes using a [Combine](https://developer.apple.com/documentation/combine) with the `reachabilityPublisher` static property. - -```swift -import Combine -import NetworkReachability - -final class MyClass { - - var monitorCancellable: AnyCancellable? - - func startObserving() { - stopObserving() - monitorCancellable = ReachabilityMonitor.reachabilityPublisher - .map(\.status.isReachable) - .replaceError(with: false) - .sink { isReachable in - // Do something with `isReachable` - } - } - - func startObserving() { - monitorCancellable?.cancel() - monitorCancellable = nil - } - - deinit { - monitorCancellable?.cancel() - } -} -``` - -- Note: This API requires iOS 13, macOS 10.15, tvOS 13, or watchOS 6 diff --git a/Sources/NetworkReachability/NetworkReachability.docc/NetworkReachability.md b/Sources/NetworkReachability/NetworkReachability.docc/NetworkReachability.md index 5a3aa8e6..322f0bd0 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/NetworkReachability.md +++ b/Sources/NetworkReachability/NetworkReachability.docc/NetworkReachability.md @@ -17,13 +17,6 @@ NetworkReachability is a replacement for Apple's [SystemConfiguration](https://d - ``NetworkMonitor`` - ``NetworkMonitorDelegate`` -### Reachability Monitor - -- -- ``ReachabilityMonitor`` -- ``ReachabilityMonitorDelegate`` -- ``Reachability`` - ### Other - diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/.swiftformat b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/.swiftformat index 4f1e3042..c9f1bfa8 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/.swiftformat +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/.swiftformat @@ -1,4 +1,4 @@ --enable isEmpty --disable blankLinesAtEndOfScope, blankLinesAtStartOfScope, redundantNilInit, unusedArguments, redundantParens, wrapMultilineStatementBraces, trailingCommas, braces ---swiftversion 5.6 +--swiftversion 5.9 --header "" diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeColor.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeColor.swift index 93169904..a77dbdb8 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeColor.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeColor.swift @@ -13,26 +13,26 @@ struct ContentView: View { private var symbolName: String { switch reachabilityManager.reachability { case .wifi: - return "wifi.circle.fill" + "wifi.circle.fill" case .ethernet: - return "cable.connector" + "cable.connector" case .unknown: - return "questionmark.circle.fill" + "questionmark.circle.fill" case .cellular: - return "antenna.radiowaves.left.and.right.circle.fill" + "antenna.radiowaves.left.and.right.circle.fill" case .disconnected: - return "xmark.circle.fill" + "xmark.circle.fill" } } private var backgroundColor: Color { switch reachabilityManager.reachability { case .disconnected: - return .red + .red case .unknown: - return .yellow + .yellow case .wifi, .ethernet, .cellular: - return .green + .green } } } diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeSymbol.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeSymbol.swift index 2312385f..92ea3b07 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeSymbol.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ComputeSymbol.swift @@ -13,15 +13,15 @@ struct ContentView: View { private var symbolName: String { switch reachabilityManager.reachability { case .wifi: - return "wifi.circle.fill" + "wifi.circle.fill" case .ethernet: - return "cable.connector" + "cable.connector" case .unknown: - return "questionmark.circle.fill" + "questionmark.circle.fill" case .cellular: - return "antenna.radiowaves.left.and.right.circle.fill" + "antenna.radiowaves.left.and.right.circle.fill" case .disconnected: - return "xmark.circle.fill" + "xmark.circle.fill" } } } diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/FormatSymbol.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/FormatSymbol.swift index b64e6ff1..d899dd84 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/FormatSymbol.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/FormatSymbol.swift @@ -14,26 +14,26 @@ struct ContentView: View { private var symbolName: String { switch reachabilityManager.reachability { case .wifi: - return "wifi.circle.fill" + "wifi.circle.fill" case .ethernet: - return "cable.connector" + "cable.connector" case .unknown: - return "questionmark.circle.fill" + "questionmark.circle.fill" case .cellular: - return "antenna.radiowaves.left.and.right.circle.fill" + "antenna.radiowaves.left.and.right.circle.fill" case .disconnected: - return "xmark.circle.fill" + "xmark.circle.fill" } } private var backgroundColor: Color { switch reachabilityManager.reachability { case .disconnected: - return .red + .red case .unknown: - return .yellow + .yellow case .wifi, .ethernet, .cellular: - return .green + .green } } } diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ImplementManager.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ImplementManager.swift index 675dd33e..98111dbc 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ImplementManager.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/ImplementManager.swift @@ -23,17 +23,17 @@ final class ReachabilityManager: ObservableObject { private func setUp() { monitor = .init() { [weak self] _, networkPath in - guard let self = self else { return } + guard let self else { return } if networkPath.usesInterfaceType(.wiredEthernet) { - self.reachability = .ethernet + reachability = .ethernet } else if networkPath.usesInterfaceType(.wifi) { - self.reachability = .wifi + reachability = .wifi } else if networkPath.usesInterfaceType(.cellular) { - self.reachability = .cellular + reachability = .cellular } else if networkPath.status == .satisfied { - self.reachability = .unknown + reachability = .unknown } else { - self.reachability = .disconnected + reachability = .disconnected } } } diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseColor.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseColor.swift index 33f1edc9..8f4c430b 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseColor.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseColor.swift @@ -19,26 +19,26 @@ struct ContentView: View { private var symbolName: String { switch reachabilityManager.reachability { case .wifi: - return "wifi.circle.fill" + "wifi.circle.fill" case .ethernet: - return "cable.connector" + "cable.connector" case .unknown: - return "questionmark.circle.fill" + "questionmark.circle.fill" case .cellular: - return "antenna.radiowaves.left.and.right.circle.fill" + "antenna.radiowaves.left.and.right.circle.fill" case .disconnected: - return "xmark.circle.fill" + "xmark.circle.fill" } } private var backgroundColor: Color { switch reachabilityManager.reachability { case .disconnected: - return .red + .red case .unknown: - return .yellow + .yellow case .wifi, .ethernet, .cellular: - return .green + .green } } } diff --git a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseSymbol.swift b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseSymbol.swift index cac8a525..1b6f42ee 100644 --- a/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseSymbol.swift +++ b/Sources/NetworkReachability/NetworkReachability.docc/Snippets/Tutorials/UseSymbol.swift @@ -12,26 +12,26 @@ struct ContentView: View { private var symbolName: String { switch reachabilityManager.reachability { case .wifi: - return "wifi.circle.fill" + "wifi.circle.fill" case .ethernet: - return "cable.connector" + "cable.connector" case .unknown: - return "questionmark.circle.fill" + "questionmark.circle.fill" case .cellular: - return "antenna.radiowaves.left.and.right.circle.fill" + "antenna.radiowaves.left.and.right.circle.fill" case .disconnected: - return "xmark.circle.fill" + "xmark.circle.fill" } } private var backgroundColor: Color { switch reachabilityManager.reachability { case .disconnected: - return .red + .red case .unknown: - return .yellow + .yellow case .wifi, .ethernet, .cellular: - return .green + .green } } } diff --git a/Tests/NetworkReachabilityTests/ReachabilityMonitorCombineTests.swift b/Tests/NetworkReachabilityTests/ReachabilityMonitorCombineTests.swift deleted file mode 100644 index dfcaf18a..00000000 --- a/Tests/NetworkReachabilityTests/ReachabilityMonitorCombineTests.swift +++ /dev/null @@ -1,57 +0,0 @@ -// NetworkReachability -// ReachabilityMonitorCombineTests.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if canImport(Combine) && !os(watchOS) - - import Combine - @testable import NetworkReachability - import XCTest - - @available(macOS 10.15, iOS 13.0, tvOS 13.0, *) - final class ReachabilityMonitorConcurrencyTests: XCTestCase { - - var cancellable: AnyCancellable? - - func test_observe_combine() { - let expectation = expectation(description: "pass") - cancellable = ReachabilityMonitor - .reachabilityPublisher - .map(\.status.isReachable) - .replaceError(with: false) - .removeDuplicates() - .sink { isReachable in - XCTAssertTrue(isReachable) - expectation.fulfill() - } - waitForExpectations(timeout: 5) - } - - deinit { - cancellable?.cancel() - } - - } - -#endif diff --git a/Tests/NetworkReachabilityTests/ReachabilityMonitorConcurrencyTests.swift b/Tests/NetworkReachabilityTests/ReachabilityMonitorConcurrencyTests.swift deleted file mode 100644 index 3cc753d2..00000000 --- a/Tests/NetworkReachabilityTests/ReachabilityMonitorConcurrencyTests.swift +++ /dev/null @@ -1,53 +0,0 @@ -// NetworkReachability -// ReachabilityMonitorConcurrencyTests.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - @testable import NetworkReachability - import XCTest - - @available(macOS 10.15, iOS 13.0, tvOS 13.0, *) - final class ReachabiltyMonitorConcurrencyTests: XCTestCase { - - func test_observe_concurrency() { - let expectation = expectation(description: "pass") - - Task { - do { - for try await isReachable in ReachabilityMonitor.reachabilityUpdates.map(\.status.isReachable) { - if isReachable { - expectation.fulfill() - } else { - XCTFail() - } - } - } catch { - XCTFail() - } - } - waitForExpectations(timeout: 5, handler: nil) - } - - } -#endif diff --git a/Tests/NetworkReachabilityTests/ReachabilityMonitorTests.swift b/Tests/NetworkReachabilityTests/ReachabilityMonitorTests.swift deleted file mode 100644 index e31812f0..00000000 --- a/Tests/NetworkReachabilityTests/ReachabilityMonitorTests.swift +++ /dev/null @@ -1,137 +0,0 @@ -// NetworkReachability -// ReachabilityMonitorTests.swift -// -// MIT License -// -// Copyright (c) 2021 Varun Santhanam -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the Software), to deal -// -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !os(watchOS) - @testable import NetworkReachability - import XCTest - - @available(macOS 10.13, iOS 11.0, tvOS 11.0, *) - final class ReachabilityMonitorTests: XCTestCase { - - func test_get_sync() { - do { - let reachability = try ReachabilityMonitor.reachability - XCTAssertTrue(reachability.status.isReachable) - } catch { - XCTFail() - } - } - - func test_observe_closure() { - let expectation = expectation(description: "pass") - do { - try withExtendedLifetime(ReachabilityMonitor(updateHandler: { _, reachability in - do { - if try reachability.get().status.isReachable { - expectation.fulfill() - } else { - XCTFail() - } - } catch { - XCTFail() - } - })) { - waitForExpectations(timeout: 5, handler: nil) - } - } catch { - XCTFail() - } - } - - func test_observe_delegate() { - let expectation = expectation(description: "pass") - - final class Delegate: ReachabilityMonitorDelegate { - init(_ expectation: XCTestExpectation) { - self.expectation = expectation - } - - let expectation: XCTestExpectation - - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didUpdateReachability reachability: Reachability) { - XCTAssertTrue(reachability.status.isReachable) - expectation.fulfill() - } - - func reachabilityMonitor(_ monitor: ReachabilityMonitor, didFailWithError error: Error) { - XCTFail() - } - } - - let delegate = Delegate(expectation) - - do { - let monitor = try ReachabilityMonitor(delegate: delegate) - withExtendedLifetime(monitor) { - waitForExpectations(timeout: 5) - } - } catch { - XCTFail() - } - } - - func test_observe_notification() { - class Observer { - - init(_ expectation: XCTestExpectation) { - self.expectation = expectation - observe() - } - - func observe() { - NotificationCenter.default.addObserver(self, selector: #selector(fulfill(_:)), name: .reachabilityChanged, object: nil) - } - - @objc - func fulfill(_ notification: Notification) { - guard let monitor = notification.object as? ReachabilityMonitor, - let isReachable = try? monitor.currentReachability.status.isReachable else { - XCTFail() - return - } - XCTAssertTrue(isReachable) - expectation.fulfill() - } - - let expectation: XCTestExpectation - - deinit { - NotificationCenter.default.removeObserver(self) - } - } - - let expectation = expectation(description: "pass") - withExtendedLifetime(Observer(expectation)) { - do { - try withExtendedLifetime(ReachabilityMonitor()) { - waitForExpectations(timeout: 5) - } - } catch { - XCTFail() - } - } - } - } -#endif