Skip to content

Commit abf2940

Browse files
authored
[Sessions] Add SessionStartEvent wrapper for building the nanopb proto (#10311)
1 parent 7ab2b94 commit abf2940

20 files changed

+736
-40
lines changed

FirebaseSessions.podspec

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,22 @@ Pod::Spec.new do |s|
3535

3636
base_dir = "FirebaseSessions/"
3737
s.source_files = [
38-
base_dir + 'Sources/**/*.swift',
38+
base_dir + 'Sources/**/*.{swift,h,m}',
39+
base_dir + 'Protogen/**/*.{c,h,m,mm}',
3940
]
4041

4142
s.dependency 'FirebaseCore', '~> 10.0'
4243
s.dependency 'FirebaseCoreExtension', '~> 10.0'
4344
s.dependency 'FirebaseInstallations', '~> 10.0'
45+
s.dependency 'GoogleDataTransport', '~> 9.2'
46+
s.dependency 'nanopb', '>= 2.30908.0', '< 2.30910.0'
4447

4548
s.pod_target_xcconfig = {
4649
'GCC_C_LANGUAGE_STANDARD' => 'c99',
47-
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"'
50+
'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}"',
51+
'GCC_PREPROCESSOR_DEFINITIONS' =>
52+
# For nanopb:
53+
'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1 PB_ENABLE_MALLOC=1',
4854
}
4955

5056
s.test_spec 'unit' do |unit_tests|
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import Foundation
17+
18+
@_implementationOnly import GoogleDataTransport
19+
20+
protocol EventGDTLoggerProtocol {
21+
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void)
22+
}
23+
24+
///
25+
/// EventGDTLogger is responsible for
26+
/// 1) Creating GDT Events and logging them to the GoogleDataTransport SDK
27+
/// 2) Handling debugging situations (eg. running in Simulator or printing the event to console)
28+
///
29+
class EventGDTLogger: EventGDTLoggerProtocol {
30+
let googleDataTransport: GoogleDataTransportProtocol
31+
32+
init(googleDataTransport: GoogleDataTransportProtocol) {
33+
self.googleDataTransport = googleDataTransport
34+
}
35+
36+
/// Logs the event to FireLog, taking into account debugging cases such as running
37+
/// in simulator.
38+
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void) {
39+
let gdtEvent = googleDataTransport.eventForTransport()
40+
gdtEvent.dataObject = event
41+
gdtEvent.qosTier = GDTCOREventQoS.qosDefault
42+
#if targetEnvironment(simulator)
43+
Logger.logDebug("Logging events using fast QOS due to running on a simulator")
44+
gdtEvent.qosTier = GDTCOREventQoS.qoSFast
45+
#endif // targetEnvironment(simulator)
46+
47+
googleDataTransport.logGDTEvent(event: gdtEvent, completion: completion)
48+
}
49+
}

FirebaseSessions/Sources/FirebaseSessions.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414

1515
import Foundation
1616

17-
import FirebaseCore
18-
import FirebaseInstallations
19-
2017
// Avoids exposing internal FirebaseCore APIs to Swift users.
2118
@_implementationOnly import FirebaseCoreExtension
19+
@_implementationOnly import FirebaseInstallations
20+
@_implementationOnly import GoogleDataTransport
21+
22+
private enum GoogleDataTransportConfig {
23+
static let sessionsLogSource = "1974"
24+
static let sessionsTarget = GDTCORTarget.FLL
25+
}
2226

2327
@objc(FIRSessionsProvider)
2428
protocol SessionsProvider {
@@ -38,9 +42,18 @@ protocol SessionsProvider {
3842

3943
// MARK: - Initializers
4044

45+
// Initializes the SDK and top-level classes
4146
required convenience init(appID: String, installations: InstallationsProtocol) {
47+
let googleDataTransport = GDTCORTransport(
48+
mappingID: GoogleDataTransportConfig.sessionsLogSource,
49+
transformers: nil,
50+
target: GoogleDataTransportConfig.sessionsTarget
51+
)
52+
53+
let fireLogger = EventGDTLogger(googleDataTransport: googleDataTransport!)
54+
4255
let identifiers = Identifiers(installations: installations)
43-
let coordinator = SessionCoordinator(identifiers: identifiers)
56+
let coordinator = SessionCoordinator(identifiers: identifiers, fireLogger: fireLogger)
4457
let initiator = SessionInitiator()
4558

4659
self.init(appID: appID,
@@ -49,6 +62,7 @@ protocol SessionsProvider {
4962
initiator: initiator)
5063
}
5164

65+
// Initializes the SDK and begines the process of listening for lifecycle events and logging events
5266
init(appID: String, identifiers: Identifiers, coordinator: SessionCoordinator,
5367
initiator: SessionInitiator) {
5468
self.appID = appID
@@ -61,7 +75,11 @@ protocol SessionsProvider {
6175

6276
self.initiator.beginListening {
6377
self.identifiers.generateNewSessionID()
64-
self.coordinator.runMain()
78+
let event = SessionStartEvent(identifiers: self.identifiers)
79+
DispatchQueue.global().async {
80+
self.coordinator.attemptLoggingSessionStart(event: event) { result in
81+
}
82+
}
6583
}
6684
}
6785

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import Foundation
17+
18+
@_implementationOnly import GoogleDataTransport
19+
20+
enum GoogleDataTransportProtocolErrors: Error {
21+
case writeFailure
22+
}
23+
24+
protocol GoogleDataTransportProtocol {
25+
func logGDTEvent(event: GDTCOREvent, completion: @escaping (Result<Void, Error>) -> Void)
26+
func eventForTransport() -> GDTCOREvent
27+
}
28+
29+
extension GDTCORTransport: GoogleDataTransportProtocol {
30+
func logGDTEvent(event: GDTCOREvent, completion: @escaping (Result<Void, Error>) -> Void) {
31+
sendDataEvent(event) { wasWritten, error in
32+
if let error = error {
33+
completion(.failure(error))
34+
} else if !wasWritten {
35+
completion(.failure(GoogleDataTransportProtocolErrors.writeFailure))
36+
} else {
37+
completion(.success(()))
38+
}
39+
}
40+
}
41+
}

FirebaseSessions/Sources/Identifiers.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
// limitations under the License.
1515

1616
import Foundation
17-
import FirebaseInstallations
17+
18+
@_implementationOnly import FirebaseInstallations
1819

1920
let sessionIDUserDefaultsKey = "com.firebase.sessions.sessionID"
2021
let lastSessionIDUserDefaultsKey = "com.firebase.sessions.lastSessionID"
@@ -28,7 +29,7 @@ protocol IdentifierProvider {
2829
get
2930
}
3031

31-
var lastSessionID: String {
32+
var previousSessionID: String {
3233
get
3334
}
3435
}
@@ -43,24 +44,26 @@ protocol IdentifierProvider {
4344
class Identifiers: IdentifierProvider {
4445
private let installations: InstallationsProtocol
4546

46-
private var uuid: UUID
47+
private var _sessionID: UUID
4748

4849
init(installations: InstallationsProtocol) {
4950
self.installations = installations
50-
uuid = UUID()
51+
_sessionID = UUID()
5152
}
5253

54+
// Generates a new Session ID. If there was already a generated Session ID
55+
// from the last session during the app's lifecycle, it will also set the last Session ID
5356
func generateNewSessionID() {
54-
uuid = UUID()
57+
_sessionID = UUID()
5558

5659
let lastStoredSessionID = UserDefaults.standard.string(forKey: sessionIDUserDefaultsKey) ?? ""
5760
UserDefaults.standard.set(lastStoredSessionID, forKey: lastSessionIDUserDefaultsKey)
5861

59-
let newSessionID = uuid.uuidString.replacingOccurrences(of: "-", with: "").lowercased()
62+
let newSessionID = _sessionID.uuidString.replacingOccurrences(of: "-", with: "").lowercased()
6063
UserDefaults.standard.set(newSessionID, forKey: sessionIDUserDefaultsKey)
6164
}
6265

63-
// This method must be run on a background thread due to how Firebase Installations
66+
// Fetches the Installation ID from Firebase Installation Service. This method must be run on a background thread due to how Firebase Installations
6467
// handles threading.
6568
var installationID: String {
6669
if Thread.isMainThread {
@@ -103,7 +106,7 @@ class Identifiers: IdentifierProvider {
103106
return UserDefaults.standard.string(forKey: sessionIDUserDefaultsKey) ?? ""
104107
}
105108

106-
var lastSessionID: String {
109+
var previousSessionID: String {
107110
return UserDefaults.standard.string(forKey: lastSessionIDUserDefaultsKey) ?? ""
108111
}
109112
}

FirebaseSessions/Sources/Installations+InstallationsProtocol.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
// limitations under the License.
1515

1616
import Foundation
17-
import FirebaseInstallations
17+
18+
@_implementationOnly import FirebaseInstallations
1819

1920
protocol InstallationsProtocol {
2021
func installationID(completion: @escaping (Result<String, Error>) -> Void)

FirebaseSessions/Sources/Logger.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import Foundation
1717

1818
@_implementationOnly import FirebaseCoreExtension
1919

20+
///
21+
/// Logger is responsible for printing console logs
22+
///
2023
enum Logger {
2124
private static let logServiceTag = "[FirebaseSessions]"
2225
private static let logCode = "I-SES000000"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//
2+
// Copyright 2022 Google LLC
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
#ifndef FIRSESNanoPBHelpers_h
17+
#define FIRSESNanoPBHelpers_h
18+
19+
#import <nanopb/pb.h>
20+
#import <nanopb/pb_decode.h>
21+
#import <nanopb/pb_encode.h>
22+
23+
NS_ASSUME_NONNULL_BEGIN
24+
25+
// It seems impossible to specify the nullability of the `fields` parameter below,
26+
// yet the compiler complains that it's missing a nullability specifier. Google
27+
// yields no results at this time.
28+
#pragma clang diagnostic push
29+
#pragma clang diagnostic ignored "-Wnullability-completeness"
30+
NSData* _Nullable FIRSESEncodeProto(const pb_field_t fields[],
31+
const void* _Nonnull proto,
32+
NSError** error);
33+
#pragma clang diagnostic pop
34+
35+
/// Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
36+
/// @note Memory needs to be freed manually, through pb_free or pb_release.
37+
/// @param data The data to copy into the new bytes array.
38+
pb_bytes_array_t* _Nullable FIRSESEncodeData(NSData* _Nullable data);
39+
40+
/// Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
41+
/// @note Memory needs to be freed manually, through pb_free or pb_release.
42+
/// @param string The string to encode as pb_bytes.
43+
pb_bytes_array_t* _Nullable FIRSESEncodeString(NSString* _Nullable string);
44+
45+
/// Checks if 2 nanopb arrays are equal
46+
/// @param array array to check
47+
/// @param expected expected value of the array
48+
BOOL FIRSESIsPBArrayEqual(pb_bytes_array_t* _Nullable array, pb_bytes_array_t* _Nullable expected);
49+
50+
/// Checks if a nanopb string is equal to an NSString
51+
/// @param pbString nanopb string to check
52+
/// @param str NSString that's expected
53+
BOOL FIRSESIsPBStringEqual(pb_bytes_array_t* _Nullable pbString, NSString* _Nullable str);
54+
55+
/// Checks if a nanopb array is equal to NSData
56+
/// @param pbArray nanopb array to check
57+
/// @param data NSData that's expected
58+
BOOL FIRSESIsPBDataEqual(pb_bytes_array_t* _Nullable pbArray, NSData* _Nullable data);
59+
60+
NS_ASSUME_NONNULL_END
61+
62+
#endif /* FIRSESNanoPBHelpers_h */

0 commit comments

Comments
 (0)