diff --git a/PianoAnalytics-AppExtension.podspec b/PianoAnalytics-AppExtension.podspec index 3dba4f0..ed5ec3f 100644 --- a/PianoAnalytics-AppExtension.podspec +++ b/PianoAnalytics-AppExtension.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'PianoAnalytics-AppExtension' - s.version = '3.0.1' + s.version = '3.0.2' s.summary = 'Piano Analytics solution for extension Apple devices' s.homepage = 'https://github.com/at-internet/piano-analytics-apple' s.documentation_url = 'https://developers.atinternet-solutions.com/piano-analytics' diff --git a/PianoAnalytics.podspec b/PianoAnalytics.podspec index 3ceff7b..4c51662 100644 --- a/PianoAnalytics.podspec +++ b/PianoAnalytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'PianoAnalytics' - s.version = '3.0.1' + s.version = '3.0.2' s.summary = 'Piano Analytics library for Apple devices' s.homepage = 'https://github.com/at-internet/piano-analytics-apple' s.documentation_url = 'https://developers.atinternet-solutions.com/piano-analytics' diff --git a/Sources/PianoAnalytics/Configuration.swift b/Sources/PianoAnalytics/Configuration.swift index ca47fd5..44275d0 100644 --- a/Sources/PianoAnalytics/Configuration.swift +++ b/Sources/PianoAnalytics/Configuration.swift @@ -319,7 +319,7 @@ public final class Configuration { if path == nil { // used to access the default.json file with SPM #if SPM - path = Bundle.module.url(forResource: PA.Configuration.Default, withExtension: PA.Configuration.Extension)?.absoluteString.deletePrefix("file://") + path = Bundle.module.path(forResource: PA.Configuration.Default, ofType: PA.Configuration.Extension) #endif } guard let path = path, diff --git a/Sources/PianoAnalytics/Core/Reachability.swift b/Sources/PianoAnalytics/Core/Reachability.swift index 8ee6f72..a339ddf 100644 --- a/Sources/PianoAnalytics/Core/Reachability.swift +++ b/Sources/PianoAnalytics/Core/Reachability.swift @@ -34,340 +34,303 @@ import SystemConfiguration import Foundation public let ReachabilityChangedNotification = "ReachabilityChangedNotification" - -public class Reachability: NSObject { - - 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 extension PianoAnalytics { + class Reachability: NSObject { + 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" + } } } - } - - // MARK: - *** Public properties *** - public var whenReachable: ((Reachability) -> Void)? - public var whenUnreachable: ((Reachability) -> Void)? - public var reachableOnWWAN: Bool - public var notificationCenter = NotificationCenter.default + // MARK: - *** Public properties *** - public var currentReachabilityStatus: NetworkStatus { - if isReachable() { - if isReachableViaWiFi() { - return .reachableViaWiFi - } - if isRunningOnDevice { - return .reachableViaWWAN + public var whenReachable: ((Reachability) -> Void)? + public var whenUnreachable: ((Reachability) -> Void)? + public var reachableOnWWAN: Bool + public var notificationCenter = NotificationCenter.default + public var currentReachabilityStatus: NetworkStatus { + if isReachable() { + if isReachableViaWiFi() { + return .reachableViaWiFi + } + if isRunningOnDevice { + return .reachableViaWWAN + } } + return .notReachable } - return .notReachable - } - - public var currentReachabilityString: String { - return "\(currentReachabilityStatus)" - } - - // MARK: - *** Initialisation methods *** - - required public init?(reachabilityRef: SCNetworkReachability?) { - reachableOnWWAN = true - self.reachabilityRef = reachabilityRef - } + public var currentReachabilityString: String { + return "\(currentReachabilityStatus)" + } - public convenience init?(hostname: String) { + // MARK: - *** Initialisation methods *** - let nodename = (hostname as NSString).utf8String - let ref = SCNetworkReachabilityCreateWithName(nil, nodename!) - self.init(reachabilityRef: ref) - } + required public init?(reachabilityRef: SCNetworkReachability?) { + reachableOnWWAN = true + self.reachabilityRef = reachabilityRef + } - public class func reachabilityForInternetConnection() -> Reachability? { - if #available(iOS 9.0, *) { - var zeroAddress = sockaddr_in6() - zeroAddress.sin6_len = UInt8(MemoryLayout.size) - zeroAddress.sin6_family = sa_family_t(AF_INET6) + public convenience init?(hostname: String) { + let nodename = (hostname as NSString).utf8String + let ref = SCNetworkReachabilityCreateWithName(nil, nodename!) + self.init(reachabilityRef: ref) + } - let ref = withUnsafePointer(to: &zeroAddress) { - $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in - SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) + public class func reachabilityForInternetConnection() -> Reachability? { + if #available(iOS 9.0, *) { + var zeroAddress = sockaddr_in6() + zeroAddress.sin6_len = UInt8(MemoryLayout.size) + zeroAddress.sin6_family = sa_family_t(AF_INET6) + let ref = withUnsafePointer(to: &zeroAddress) { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in + SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) + } } - } - return Reachability(reachabilityRef: ref) - } else { - var zeroAddress = sockaddr_in() - zeroAddress.sin_len = UInt8(MemoryLayout.size) - zeroAddress.sin_family = sa_family_t(AF_INET) - - let ref = withUnsafePointer(to: &zeroAddress) { - $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in - SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) + return Reachability(reachabilityRef: ref) + } else { + var zeroAddress = sockaddr_in() + zeroAddress.sin_len = UInt8(MemoryLayout.size) + zeroAddress.sin_family = sa_family_t(AF_INET) + let ref = withUnsafePointer(to: &zeroAddress) { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in + SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) + } } + return Reachability(reachabilityRef: ref) } - return Reachability(reachabilityRef: ref) } - } - // MARK: - *** Notifier methods *** - public func startNotifier() -> Bool { + // MARK: - *** Notifier methods *** + public func startNotifier() -> Bool { + if notifierRunning { return true } + var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + SCNetworkReachabilitySetCallback(reachabilityRef!, { (_, flags, info) in + let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() - if notifierRunning { return true } - - var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) - context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) - - SCNetworkReachabilitySetCallback(reachabilityRef!, { (_, flags, info) in - let reachability = Unmanaged.fromOpaque(info!).takeUnretainedValue() - - DispatchQueue.main.async { - reachability.reachabilityChanged(flags: flags) - } + DispatchQueue.main.async { + reachability.reachabilityChanged(flags: flags) + } }, &context) + if SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) { + notifierRunning = true + return true + } + stopNotifier() - if SCNetworkReachabilitySetDispatchQueue(reachabilityRef!, reachabilitySerialQueue) { - notifierRunning = true - return true + return false } - stopNotifier() - - return false - } - - public func stopNotifier() { - if let reachabilityRef = reachabilityRef { - SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) + public func stopNotifier() { + if let reachabilityRef = reachabilityRef { + SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) + } + notifierRunning = false } - notifierRunning = false - } - // MARK: - *** Connection test methods *** - public func isReachable() -> Bool { - return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in - return self.isReachableWithFlags(flags: flags) - }) - } - - public func isReachableViaWWAN() -> Bool { - - if isRunningOnDevice { - return isReachableWithTest { flags -> Bool in - // Check we're REACHABLE - if self.isReachable(flags: flags) { + // MARK: - *** Connection test methods *** + public func isReachable() -> Bool { + return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isReachableWithFlags(flags: flags) + }) + } - // Now, check we're on WWAN - if self.isOnWWAN(flags: flags) { - return true + public func isReachableViaWWAN() -> Bool { + if isRunningOnDevice { + return isReachableWithTest { flags -> Bool in + // Check we're REACHABLE + if self.isReachable(flags: flags) { + // Now, check we're on WWAN + if self.isOnWWAN(flags: flags) { + return true + } } + return false } - return false } + return false } - return false - } - - public func isReachableViaWiFi() -> Bool { - - return isReachableWithTest { flags -> Bool in - // Check we're reachable - if self.isReachable(flags: flags) { - - if self.isRunningOnDevice { - // Check we're NOT on WWAN - if self.isOnWWAN(flags: flags) { - return false + public func isReachableViaWiFi() -> Bool { + return isReachableWithTest { flags -> Bool in + // Check we're reachable + if self.isReachable(flags: flags) { + if self.isRunningOnDevice { + // Check we're NOT on WWAN + if self.isOnWWAN(flags: flags) { + return false + } } + return true } - return true + return false } - - return false } - } - // MARK: - *** Private methods *** - private var isRunningOnDevice: Bool = { - #if targetEnvironment(simulator) + // MARK: - *** Private methods *** + private var isRunningOnDevice: Bool = { + #if targetEnvironment(simulator) return false - #else + #else return true - #endif - }() - - private var notifierRunning = false - private var reachabilityRef: SCNetworkReachability? - private let reachabilitySerialQueue = DispatchQueue.init(label: "uk.co.ashleymills.reachability") + #endif + }() + private var notifierRunning = false + private var reachabilityRef: SCNetworkReachability? + private let reachabilitySerialQueue = DispatchQueue.init(label: "uk.co.ashleymills.reachability") + private func reachabilityChanged(flags: SCNetworkReachabilityFlags) { + if isReachableWithFlags(flags: flags) { + if let block = whenReachable { + block(self) + } + } else { + if let block = whenUnreachable { + block(self) + } + } + notificationCenter.post(name: NSNotification.Name(rawValue: ReachabilityChangedNotification), object: self) + } - private func reachabilityChanged(flags: SCNetworkReachabilityFlags) { - if isReachableWithFlags(flags: flags) { - if let block = whenReachable { - block(self) + private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool { + let reachable = isReachable(flags: flags) + if !reachable { + return false } - } else { - if let block = whenUnreachable { - block(self) + if isConnectionRequiredOrTransient(flags: flags) { + return false } + if isRunningOnDevice { + if isOnWWAN(flags: flags) && !reachableOnWWAN { + // We don't want to connect when on 3G. + return false + } + } + return true } - notificationCenter.post(name: NSNotification.Name(rawValue: ReachabilityChangedNotification), object: self) - } - - private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool { - - let reachable = isReachable(flags: flags) - - if !reachable { + private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool { + if let reachabilityRef = reachabilityRef { + var flags = SCNetworkReachabilityFlags(rawValue: 0) + let gotFlags = withUnsafeMutablePointer(to: &flags) { + SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + } + if gotFlags { + return test(flags) + } + } return false } - if isConnectionRequiredOrTransient(flags: flags) { - return false + // WWAN may be available, but not active until a connection has been established. + // WiFi may require a connection for VPN on Demand. + private func isConnectionRequired() -> Bool { + return connectionRequired() } - if isRunningOnDevice { - if isOnWWAN(flags: flags) && !reachableOnWWAN { - // We don't want to connect when on 3G. - return false - } + private func connectionRequired() -> Bool { + return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags: flags) + }) } - return true - } - - private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool { - - if let reachabilityRef = reachabilityRef { - - var flags = SCNetworkReachabilityFlags(rawValue: 0) - let gotFlags = withUnsafeMutablePointer(to: &flags) { - SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) - } - - if gotFlags { - return test(flags) - } + // Dynamic, on demand connection? + private func isConnectionOnDemand() -> Bool { + return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags: flags) && self.isConnectionOnTrafficOrDemand(flags: flags) + }) } - return false - } - - // WWAN may be available, but not active until a connection has been established. - // WiFi may require a connection for VPN on Demand. - private func isConnectionRequired() -> Bool { - return connectionRequired() - } - - private func connectionRequired() -> Bool { - return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in - return self.isConnectionRequired(flags: flags) - }) - } - - // Dynamic, on demand connection? - private func isConnectionOnDemand() -> Bool { - return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in - return self.isConnectionRequired(flags: flags) && self.isConnectionOnTrafficOrDemand(flags: flags) - }) - } - - // Is user intervention required? - private func isInterventionRequired() -> Bool { - return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in - return self.isConnectionRequired(flags: flags) && self.isInterventionRequired(flags: flags) - }) - } + // Is user intervention required? + private func isInterventionRequired() -> Bool { + return isReachableWithTest(test: { (flags: SCNetworkReachabilityFlags) -> (Bool) in + return self.isConnectionRequired(flags: flags) && self.isInterventionRequired(flags: flags) + }) + } - private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool { - #if os(iOS) + private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool { + #if os(iOS) return flags.contains(.isWWAN) - #else + #else return false - #endif - } - private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.reachable) - } - private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.connectionRequired) - } - private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.interventionRequired) - } - private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.connectionOnTraffic) - } - private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.connectionOnDemand) - } - func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool { - return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty - } - private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.transientConnection) - } - private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.isLocalAddress) - } - private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool { - return flags.contains(.isDirect) - } - private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool { - let testcase: SCNetworkReachabilityFlags = [.connectionRequired, .transientConnection] - return flags.intersection(testcase) == testcase - } - - private var reachabilityFlags: SCNetworkReachabilityFlags { - if let reachabilityRef = reachabilityRef { + #endif + } + private func isReachable(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.reachable) + } + private func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.connectionRequired) + } + private func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.interventionRequired) + } + private func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.connectionOnTraffic) + } + private func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.connectionOnDemand) + } + func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool { + return !flags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty + } + private func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.transientConnection) + } + private func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.isLocalAddress) + } + private func isDirect(flags: SCNetworkReachabilityFlags) -> Bool { + return flags.contains(.isDirect) + } + private func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool { + let testcase: SCNetworkReachabilityFlags = [.connectionRequired, .transientConnection] + return flags.intersection(testcase) == testcase + } - var flags = SCNetworkReachabilityFlags(rawValue: 0) - let gotFlags = withUnsafeMutablePointer(to: &flags) { - SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + private var reachabilityFlags: SCNetworkReachabilityFlags { + if let reachabilityRef = reachabilityRef { + var flags = SCNetworkReachabilityFlags(rawValue: 0) + let gotFlags = withUnsafeMutablePointer(to: &flags) { + SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + } + if gotFlags { + return flags + } } + return [] + } - if gotFlags { - return flags + override public var description: String { + var W: String + if isRunningOnDevice { + W = isOnWWAN(flags: reachabilityFlags) ? "W" : "-" + } else { + W = "X" } + let R = isReachable(flags: reachabilityFlags) ? "R" : "-" + let c = isConnectionRequired(flags: reachabilityFlags) ? "c" : "-" + let t = isTransientConnection(flags: reachabilityFlags) ? "t" : "-" + let i = isInterventionRequired(flags: reachabilityFlags) ? "i" : "-" + let C = isConnectionOnTraffic(flags: reachabilityFlags) ? "C" : "-" + let D = isConnectionOnDemand(flags: reachabilityFlags) ? "D" : "-" + let l = isLocalAddress(flags: reachabilityFlags) ? "l" : "-" + let d = isDirect(flags: reachabilityFlags) ? "d" : "-" + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" + } + deinit { + stopNotifier() + reachabilityRef = nil + whenReachable = nil + whenUnreachable = nil } - - return [] - } - - override public var description: String { - - var W: String - if isRunningOnDevice { - W = isOnWWAN(flags: reachabilityFlags) ? "W" : "-" - } else { - W = "X" - } - let R = isReachable(flags: reachabilityFlags) ? "R" : "-" - let c = isConnectionRequired(flags: reachabilityFlags) ? "c" : "-" - let t = isTransientConnection(flags: reachabilityFlags) ? "t" : "-" - let i = isInterventionRequired(flags: reachabilityFlags) ? "i" : "-" - let C = isConnectionOnTraffic(flags: reachabilityFlags) ? "C" : "-" - let D = isConnectionOnDemand(flags: reachabilityFlags) ? "D" : "-" - let l = isLocalAddress(flags: reachabilityFlags) ? "l" : "-" - let d = isDirect(flags: reachabilityFlags) ? "d" : "-" - - return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" - } - - deinit { - stopNotifier() - - reachabilityRef = nil - whenReachable = nil - whenUnreachable = nil } } #endif diff --git a/Sources/PianoAnalytics/Core/Utils.swift b/Sources/PianoAnalytics/Core/Utils.swift index d07025b..24efd60 100644 --- a/Sources/PianoAnalytics/Core/Utils.swift +++ b/Sources/PianoAnalytics/Core/Utils.swift @@ -89,12 +89,12 @@ final class PianoAnalyticsUtils { #else static var connectionType: ConnectionType { get { - let reachability = Reachability.reachabilityForInternetConnection() + let reachability = PianoAnalytics.Reachability.reachabilityForInternetConnection() if let optReachability = reachability { - if optReachability.currentReachabilityStatus == Reachability.NetworkStatus.reachableViaWiFi { + if optReachability.currentReachabilityStatus == PianoAnalytics.Reachability.NetworkStatus.reachableViaWiFi { return ConnectionType.Wifi - } else if optReachability.currentReachabilityStatus == Reachability.NetworkStatus.notReachable { + } else if optReachability.currentReachabilityStatus == PianoAnalytics.Reachability.NetworkStatus.notReachable { return ConnectionType.Offline } else { #if os(iOS) && canImport(CoreTelephony) diff --git a/Sources/PianoAnalytics/Steps/InternalContextPropertiesStep.swift b/Sources/PianoAnalytics/Steps/InternalContextPropertiesStep.swift index 6df2f86..3b65461 100644 --- a/Sources/PianoAnalytics/Steps/InternalContextPropertiesStep.swift +++ b/Sources/PianoAnalytics/Steps/InternalContextPropertiesStep.swift @@ -123,7 +123,7 @@ final class InternalContextPropertiesStep: Step { static let BrowserLanguagePropertiesFormat = "browser_language%@" private static let Manufacturer = "Apple" - private static let EventCollectionVersion = "3.0.1" + private static let EventCollectionVersion = "3.0.2" #if os(tvOS) private static let Platform = "tvOS"