diff --git a/Package.swift b/Package.swift index 6fc6464..b220a41 100644 --- a/Package.swift +++ b/Package.swift @@ -12,7 +12,7 @@ let package = Package( .library( name: "Analytics", targets: ["Analytics"] - ), + ) ], dependencies: [ .package(url: "https://github.com/apple/swift-http-types", from: "1.3.0"), @@ -35,9 +35,6 @@ let package = Package( dependencies: [ .target(name: "Analytics"), .product(name: "HTTPTypesFoundation", package: "swift-http-types"), - ], - resources: [ - .process("Resources") ] ), ] diff --git a/README.md b/README.md index fd747f3..541ca9f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,93 @@ # firebaseanalytics-swift -Firebase Analytics client for Swift. +> [!IMPORTANT] +> This project is under development + +Firebase Analytics Client for Swift. + +```swift +let clientInformation: ClientInformation = .init( + os: "ios", + osVersion: "18.3", + deviceModel: "iPhone17,3", + locale: "ja-jp", + localeId: 540, + installMethod: "manual_install", + bundleId: "com.apple.SampleApp", + appVersion: "1.0", + googleAppId: "1:21332412389:ios:11681asd2a3cd5d8" +) + +let session: Session = .init( + firstInteraction: false, + firstOpenTime: 1_737_550_800_666, + nonPersonalizedAds: false, + sessionId: 1_737_555_666, + sessionNumber: 2, + lifetimeEngagement: 1_175_666, + sessionEngagement: 10055, + userId: "user-id" +) + +let payload = Payload( + value: 1, + events: [ + EventPayload( + eventName: "login", + parameters: [ + .method: "Google", + .platform: "app", + ], + previousTimestampMillis: UInt(Date.now.timeIntervalSince1970 * 100), + timestampMillis: UInt(Date.now.timeIntervalSince1970 * 100) + ), + EventPayload( + eventName: "_e", + parameters: [ + "_et": .uint(891), + .platform: "auto" + ], + previousTimestampMillis: UInt(Date.now.timeIntervalSince1970 * 100), + timestampMillis: UInt(Date.now.timeIntervalSince1970 * 100) + ), + ], + sessionInformations: session.parameters, + value4: UInt(Date.now.timeIntervalSince1970 * 100), // upload_timestamp_millis + value5: UInt(Date.now.timeIntervalSince1970 * 100), // start_timestamp_millis + value6: UInt(Date.now.timeIntervalSince1970 * 100), // end_timestamp_millis + value7: UInt(Date.now.timeIntervalSince1970 * 100), // previous_bundle_end_timestamp_millis + os: clientInformation.os, + osVersion: clientInformation.osVersion, + deviceModel: clientInformation.deviceModel, + value11: clientInformation.locale, + value12: clientInformation.localeId, + installMethod: clientInformation.installMethod, + bundleId: clientInformation.bundleId, + appVersion: clientInformation.appVersion, + value17: 110700, // gmp_version + value18: 110700, // uploading_gmp_version + value21: "BC75E87831231231B7A179248F24080C", // fix // app_instance_id + value23: 25, // count up // bundle_sequential_index + googleAppId: clientInformation.googleAppId, + value26: UInt(Date.now.timeIntervalSince1970 * 100), // free // previous_bundle_start_timestamp_millis + value27: "CAIIJF38-E407-4954-BC16-F693A22F9FA9", // fix // resettable_device_id + value30: "d4Xw8qsfsadBoMYlsYHnot", // fix // firebase_instance_id + value31: 1, // app_version_major + value35: 1_727_127_969_769_945, // fix // config_version + value45: 42_820_019, // fix + value52: "G1--", // fix // consent_signals + value64: "google_signals", // fix + value71: "19911", // fix // consent_diagnostics + value72: 0, // fix + value77: 13 // count up +) + +let analytics = Analytics( + httpClient: .urlSession(.shared), + userAgent: "SampleApp/1 CFNetwork/3826.400.120 Darwin/24.3.0", + clientInformation: clientInformation, + session: session +) + +try await analytics.log(payload: payload) +``` diff --git a/Sources/Analytics/Analytics.swift b/Sources/Analytics/Analytics.swift index 787963a..66531b7 100644 --- a/Sources/Analytics/Analytics.swift +++ b/Sources/Analytics/Analytics.swift @@ -21,7 +21,7 @@ public struct Analytics { self.clientInformation = clientInformation self.session = session } - + public func log(payload: Payload) async throws { let payloadData = try ProtobufEncoder.encoding(NestedValue(value: payload)) try await log(bodyData: payloadData) @@ -60,23 +60,23 @@ public struct Analytics { value: 1, events: [ EventPayload( - parameters: parameters.merging([.platform: "app"]) { $1 }.compactMapValues { $0 }, eventName: eventName, + parameters: parameters.merging([.platform: "app"]) { $1 }.compactMapValues { $0 }, previousTimestampMillis: UInt(Date.now.timeIntervalSince1970 * 100), timestampMillis: UInt(Date.now.timeIntervalSince1970 * 100) ), EventPayload( - parameters: ["_et": .uint(891), .platform: "auto"], eventName: "_e", + parameters: ["_et": .uint(891), .platform: "auto"], previousTimestampMillis: UInt(Date.now.timeIntervalSince1970 * 100), timestampMillis: UInt(Date.now.timeIntervalSince1970 * 100) ), ], sessionInformations: session.parameters, - value4: UInt(Date.now.timeIntervalSince1970 * 100), // upload_timestamp_millis - value5: UInt(Date.now.timeIntervalSince1970 * 100), // start_timestamp_millis - value6: UInt(Date.now.timeIntervalSince1970 * 100), // end_timestamp_millis - value7: UInt(Date.now.timeIntervalSince1970 * 100), // previous_bundle_end_timestamp_millis + value4: UInt(Date.now.timeIntervalSince1970 * 100), // upload_timestamp_millis + value5: UInt(Date.now.timeIntervalSince1970 * 100), // start_timestamp_millis + value6: UInt(Date.now.timeIntervalSince1970 * 100), // end_timestamp_millis + value7: UInt(Date.now.timeIntervalSince1970 * 100), // previous_bundle_end_timestamp_millis os: clientInformation.os, osVersion: clientInformation.osVersion, deviceModel: clientInformation.deviceModel, @@ -85,15 +85,16 @@ public struct Analytics { installMethod: clientInformation.installMethod, bundleId: clientInformation.bundleId, appVersion: clientInformation.appVersion, - value17: 110700, // gmp_version - value18: 110700, // uploading_gmp_version + value17: 110700, // gmp_version + value18: 110700, // uploading_gmp_version value21: "BC75E8783E8141ECB7A179248F24080C", // fix // app_instance_id value23: 25, // count up // bundle_sequential_index googleAppId: clientInformation.googleAppId, - value26: UInt(Date.now.timeIntervalSince1970 * 100), // free // previous_bundle_start_timestamp_millis + // free // previous_bundle_start_timestamp_millis + value26: UInt(Date.now.timeIntervalSince1970 * 100), value27: "C86A6B98-E407-4954-BC16-F693A22F9FA9", // fix // resettable_device_id value30: "d4Xw8qsiRUIBoMYlsYHnot", // fix // firebase_instance_id - value31: 1, // app_version_major + value31: 1, // app_version_major value35: 1_727_127_969_769_945, // fix // config_version value45: 42_820_019, // fix value52: "G1--", // fix // consent_signals diff --git a/Sources/Analytics/Parameter.swift b/Sources/Analytics/Parameter.swift index fbd9796..6a0230b 100644 --- a/Sources/Analytics/Parameter.swift +++ b/Sources/Analytics/Parameter.swift @@ -19,7 +19,7 @@ public enum Parameter { static func bool(_ value: Bool) -> Self { .uint(value ? 1 : 0) } - + public init(from decoder: inout ProtobufDecoder) throws { var value: Parameter.Value? @@ -58,17 +58,17 @@ public enum Parameter { public func encode(to encoder: inout ProtobufEncoder) throws { switch self { - case let .string(value): + case .string(let value): try encoder.stringField(1, value, defaultValue: nil) - case let .uint(value): + case .uint(let value): encoder.uintField(2, value, defaultValue: nil) - case let .float(value): + case .float(let value): encoder.doubleField(5, value, defaultValue: nil) - case let .dictionary(value): + case .dictionary(let value): for (key, value) in value { try encoder.messageField(6, KeyValue(key: key, value: value)) } - case let .array(values): + case .array(let values): for item in values { try encoder.messageField(6, item) } diff --git a/Sources/Analytics/Payload/EventPayload.swift b/Sources/Analytics/Payload/EventPayload.swift index 761e797..aae5127 100644 --- a/Sources/Analytics/Payload/EventPayload.swift +++ b/Sources/Analytics/Payload/EventPayload.swift @@ -1,14 +1,14 @@ import ProtobufKit public struct EventPayload: ProtobufMessage, Equatable { - public var parameters: [Parameter.Key: Parameter.Value] public var eventName: Event.Name + public var parameters: [Parameter.Key: Parameter.Value] public var previousTimestampMillis: UInt? public var timestampMillis: UInt public init( - parameters: [Parameter.Key: Parameter.Value], eventName: Event.Name, + parameters: [Parameter.Key: Parameter.Value], previousTimestampMillis: UInt?, timestampMillis: UInt ) { @@ -46,8 +46,8 @@ public struct EventPayload: ProtobufMessage, Equatable { if let eventName, let timestampMillis { self.init( - parameters: parameters, eventName: eventName, + parameters: parameters, previousTimestampMillis: previousTimestampMillis, timestampMillis: timestampMillis ) @@ -99,16 +99,16 @@ struct KeyValue: ProtobufMessage { func encode(to encoder: inout ProtobufEncoder) throws { try encoder.stringField(1, key.rawValue, defaultValue: nil) switch value { - case let .string(value): + case .string(let value): try encoder.stringField(2, value, defaultValue: nil) - case let .uint(value): + case .uint(let value): encoder.uintField(3, value, defaultValue: nil) - case let .float(value): + case .float(let value): encoder.doubleField(5, value, defaultValue: nil) - case let .dictionary(values): + case .dictionary(let values): let arayValue = Array6(value: .dictionary(values)) try encoder.messageField(6, arayValue) - case let .array(values): + case .array(let values): for value in values { let arayValue = Array6(value: value) try encoder.messageField(6, arayValue) diff --git a/Sources/Analytics/Payload/ValueKeyValue.swift b/Sources/Analytics/Payload/ValueKeyValue.swift index 5f4c684..a114c63 100644 --- a/Sources/Analytics/Payload/ValueKeyValue.swift +++ b/Sources/Analytics/Payload/ValueKeyValue.swift @@ -61,17 +61,17 @@ public struct ValueKeyValue: ProtobufMessage, Equatable { encoder.uintField(1, date, defaultValue: nil) try encoder.stringField(2, key, defaultValue: nil) switch value { - case let .string(value): + case .string(let value): try encoder.stringField(3, value, defaultValue: nil) - case let .uint(value): + case .uint(let value): encoder.uintField(4, value, defaultValue: nil) - case let .float(value): + case .float(let value): encoder.doubleField(6, value, defaultValue: nil) - case let .dictionary(values): + case .dictionary(let values): for item in values { try encoder.messageField(7, KeyValue(key: item.key, value: item.value)) } - case let .array(values): + case .array(let values): for item in values { try encoder.messageField(7, item) } diff --git a/Tests/AnalyticsTests/AnalyticsTests.swift b/Tests/AnalyticsTests/AnalyticsTests.swift new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/Tests/AnalyticsTests/AnalyticsTests.swift @@ -0,0 +1 @@ +