diff --git a/README.md b/README.md index 1628a71..bda718e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ > Telemetry is an open-source SDK for Google Analytics. We believe in transparency and security, hence the open-source approach. - ## Installation You can add Telemetry to your `Package.swift` file: @@ -42,7 +41,6 @@ Telemetry.screenView("Cheers") ### Sessions By calling `session(start: true)` when the application opens and `session(start: false)` when it closes, you can track individual user sessions. Here's an example of how to do this in your `UIApplicationDelegate` application: - ```swift Telemetry.trackerID = "UA-XXXXX-XX") Telemetry.session(start: true) // applicationDidBecomeActive @@ -79,7 +77,6 @@ Telemetry.timing(category: "Database", variable: "Fetch", time: elapsedTime, lab - When setting up your Google Analytics account, ensure to use the legacy `Universal Analytics property` and not GA4. This legacy option is under the advanced menu during account setup. - Why are closed-source SDKs a concern? According to Apple's app-review guidelines: `Ensure that all software frameworks and dependencies also adhere to the App Store Review Guidelines`. - ### Resources: - Anonymous GA: https://stackoverflow.com/questions/50392242/how-anonymize-google-analytics-for-ios-for-gdpr-rgpd-purpose - Guide on fingerprinting in iOS: https://nshipster.com/device-identifiers/ @@ -88,6 +85,5 @@ Telemetry.timing(category: "Database", variable: "Fetch", time: elapsedTime, lab - Another noteworthy tracker project: https://github.com/devxoul/Umbrella - Using Google Analytics for Tracking SaaS: https://reflectivedata.com/using-google-analytics-for-tracking-saas/ - ### Todo: - Add info to this readme on how to setup Google analytics for your google account etc 🚧 diff --git a/Sources/Telemetry/Telemetry+Action.swift b/Sources/Telemetry/Telemetry+Action.swift index 01ee1a6..772305f 100644 --- a/Sources/Telemetry/Telemetry+Action.swift +++ b/Sources/Telemetry/Telemetry+Action.swift @@ -46,7 +46,7 @@ extension Telemetry { } // If custom dimensions are not nil, merge them into the query arguments if let customDim: [String: String] = self.customDimArgs { - queryArgs.merge(customDim) { _, new in new } + queryArgs.merge(customDim) { (_: String, new: String) in new } } // Update the "aip" key in the query arguments based on the anonymizeIP flag queryArgs["aip"] = anonymizeIP ? "1" : nil @@ -55,9 +55,9 @@ extension Telemetry { // Generate a URL with the arguments, return if URL generation fails guard let url: URL = Self.getURL(with: arguments) else { return } // Create a data task with the URL - let task = session.dataTask(with: url) { _, _, error in + let task: URLSessionDataTask = session.dataTask(with: url) { (_ : Data?, _ : URLResponse?, error: Error?) in // If there is an error, print it and call the completion handler with false - if let errorResponse = error?.localizedDescription { + if let errorResponse: String = error?.localizedDescription { Swift.print("⚠️️ Failed to deliver GA Request. ", errorResponse) complete?(false) } diff --git a/Sources/Telemetry/Telemetry+Const.swift b/Sources/Telemetry/Telemetry+Const.swift index e4beac0..10e4dea 100644 --- a/Sources/Telemetry/Telemetry+Const.swift +++ b/Sources/Telemetry/Telemetry+Const.swift @@ -21,15 +21,13 @@ extension Telemetry { * Flag to anonymize user's IP * - Description: To ensure GDPR compliance, Telemetry requests Google Analytics to anonymize user IPs by default. Set this to false to opt-out. */ - public static var anonymizeIP = true - + public static var anonymizeIP: Bool = true /** * Google Analytics Identifier (Tracker ID) * - Remark: This token can be obtained from the Google Analytics entity's admin page. * - Remark: A valid Google Analytics tracker ID (format: UA-XXXXX-XX) must be set before reporting any events. */ public static var trackerId: String = "UA-XXXXX-XX" - /** * Custom dimension arguments * - Description: A dictionary of custom key-value pairs to be added to every query. @@ -47,7 +45,7 @@ extension Telemetry { * Network session * - Remark: Consider renaming to urlSession */ - public static let session = URLSession.shared + public static let session: URLSession = .shared /** * Telemetry type * - Description: Allows switching between ga-endpoint and aggregator-endpoint diff --git a/Sources/Telemetry/ext/Dict+Ext.swift b/Sources/Telemetry/ext/Dict+Ext.swift index aead603..37f64a2 100644 --- a/Sources/Telemetry/ext/Dict+Ext.swift +++ b/Sources/Telemetry/ext/Dict+Ext.swift @@ -9,7 +9,7 @@ extension Dictionary { * - Returns: A new dictionary that contains the combined key-value pairs. */ func combinedWith(_ other: [Key: Value]) -> [Key: Value] { - var dict = self // Initializes a new dictionary with the current dictionary + var dict: [Key : Value] = self // Initializes a new dictionary with the current dictionary for (key, value) in other { // Loops through the key-value pairs in the other dictionary dict[key] = value // Adds the key-value pair to the new dictionary } diff --git a/Sources/Telemetry/ext/Keychain.swift b/Sources/Telemetry/ext/Keychain.swift index f5dbbea..0963940 100644 --- a/Sources/Telemetry/ext/Keychain.swift +++ b/Sources/Telemetry/ext/Keychain.swift @@ -14,7 +14,7 @@ internal class Keychain { */ internal static func set(key: String, value: String) throws { // Convert the string value to data - guard let valueData = value.data(using: .utf8) else { + guard let valueData: Data = value.data(using: .utf8) else { Swift.print("Keychain: Unable to store data, invalid input - key: \(key), value: \(value)") return } @@ -54,7 +54,7 @@ internal class Keychain { ] var result: AnyObject? // Load the item from the keychain - let resultCodeLoad = withUnsafeMutablePointer(to: &result) { + let resultCodeLoad: OSStatus = withUnsafeMutablePointer(to: &result) { SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0)) } if resultCodeLoad != 0 { // Checks if the result code for loading the keychain data is not 0 @@ -62,7 +62,8 @@ internal class Keychain { return nil // Returns nil if the keychain data cannot be loaded } // Convert the data to a string - guard let resultVal = result as? NSData, let keyValue = NSString(data: resultVal as Data, encoding: String.Encoding.utf8.rawValue) as String? else { + guard let resultVal: NSData = result as? NSData, + let keyValue: String = NSString(data: resultVal as Data, encoding: String.Encoding.utf8.rawValue) as String? else { print("Keychain: error parsing keychain result - \(resultCodeLoad)") return nil } @@ -85,7 +86,7 @@ extension Keychain { kSecAttrAccount as String: itemKey as AnyObject // Define the account attribute of the item to be deleted ] // Delete the item from the keychain - let resultCodeDelete = SecItemDelete(queryDelete as CFDictionary) + let resultCodeDelete: OSStatus = SecItemDelete(queryDelete as CFDictionary) if resultCodeDelete != 0 { // Checks if the result code for deleting the keychain item is not 0 print("Keychain: unable to delete from keychain: \(resultCodeDelete)") // Prints an error message with the result code if the keychain item cannot be deleted } else { diff --git a/Sources/Telemetry/util/System.swift b/Sources/Telemetry/util/System.swift index 13ab98e..80022bd 100644 --- a/Sources/Telemetry/util/System.swift +++ b/Sources/Telemetry/util/System.swift @@ -44,7 +44,7 @@ internal class System { * TODO: Consider handling different language formats (e.g., en-US, en-GB). */ internal static let userLanguage: String = { - guard let locale = Locale.preferredLanguages.first, !locale.isEmpty else { + guard let locale: String = Locale.preferredLanguages.first, !locale.isEmpty else { return "(not set)" } return locale @@ -73,11 +73,11 @@ internal class System { // Check if the OS is macOS #if os(macOS) // Get the OS version - let osVersion = ProcessInfo.processInfo.operatingSystemVersionString + let osVersion: String = ProcessInfo.processInfo.operatingSystemVersionString // Replace "." with "_" in the version string - let versionString = osVersion.replacingOccurrences(of: ".", with: "_") + let versionString: String = osVersion.replacingOccurrences(of: ".", with: "_") // Define the user agent for macOS - let fallbackAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X \(versionString)) AppleWebKit/603.2.4 (KHTML, like Gecko) \(appName)/\(appVersion)" // swiftlint:disable:this line_length + let fallbackAgent: String = "Mozilla/5.0 (Macintosh; Intel Mac OS X \(versionString)) AppleWebKit/603.2.4 (KHTML, like Gecko) \(appName)/\(appVersion)" // swiftlint:disable:this line_length #else // If not macOS, then it's iOS. Get the device details let currentDevice = UIDevice.current