firebase log level

This commit is contained in:
oscarz
2024-08-29 18:25:13 +08:00
parent 8500300d18
commit 27c160beaf
1165 changed files with 122916 additions and 1 deletions

View File

@ -0,0 +1,120 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import FirebaseCore
#if SWIFT_PACKAGE
import FirebaseSessionsObjC
#endif // SWIFT_PACKAGE
#if SWIFT_PACKAGE
@_implementationOnly import GoogleUtilities_Environment
#else
@_implementationOnly import GoogleUtilities
#endif // SWIFT_PACKAGE
/// Development environment for the application.
enum DevEnvironment: String {
case prod // Prod environment
case staging // Staging environment
case autopush // Autopush environment
}
protocol ApplicationInfoProtocol {
/// Google App ID / GMP App ID
var appID: String { get }
/// Version of the Firebase SDK
var sdkVersion: String { get }
/// Crashlytics-specific device / OS filter values.
var osName: String { get }
/// Model of the device
var deviceModel: String { get }
/// Network information for the application
var networkInfo: NetworkInfoProtocol { get }
/// Development environment on which the application is running.
var environment: DevEnvironment { get }
var appBuildVersion: String { get }
var appDisplayVersion: String { get }
var osBuildVersion: String { get }
var osDisplayVersion: String { get }
}
class ApplicationInfo: ApplicationInfoProtocol {
let appID: String
private let networkInformation: NetworkInfoProtocol
private let envParams: [String: String]
private let infoDict: [String: Any]?
init(appID: String, networkInfo: NetworkInfoProtocol = NetworkInfo(),
envParams: [String: String] = ProcessInfo.processInfo.environment,
infoDict: [String: Any]? = Bundle.main.infoDictionary) {
self.appID = appID
networkInformation = networkInfo
self.envParams = envParams
self.infoDict = infoDict
}
var sdkVersion: String {
return FirebaseVersion()
}
var osName: String {
return GULAppEnvironmentUtil.appleDevicePlatform()
}
var deviceModel: String {
return GULAppEnvironmentUtil.deviceSimulatorModel() ?? ""
}
var networkInfo: NetworkInfoProtocol {
return networkInformation
}
var environment: DevEnvironment {
if let environment = envParams["FirebaseSessionsRunEnvironment"] {
return DevEnvironment(rawValue: environment.trimmingCharacters(in: .whitespaces).lowercased())
?? DevEnvironment.prod
}
return DevEnvironment.prod
}
var appBuildVersion: String {
return infoDict?["CFBundleVersion"] as? String ?? ""
}
var appDisplayVersion: String {
return infoDict?["CFBundleShortVersionString"] as? String ?? ""
}
var osBuildVersion: String {
return FIRSESGetSysctlEntry("kern.osversion") ?? ""
}
var osDisplayVersion: String {
return GULAppEnvironmentUtil.systemVersion()
}
}

View File

@ -0,0 +1,75 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if SWIFT_PACKAGE
import FirebaseSessionsObjC
#endif // SWIFT_PACKAGE
class DevEventConsoleLogger: EventGDTLoggerProtocol {
private let commandLineArgument = "-FIRSessionsDebugEvents"
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void) {
if !ProcessInfo.processInfo.arguments.contains(commandLineArgument) {
return
}
let proto = event.encodeDecodeEvent()
prettyPrint(proto: proto)
}
func prettyPrint(proto: firebase_appquality_sessions_SessionEvent) {
let logOutput = """
Printing Session Event due to \"\(commandLineArgument)\" command line argument
Session Event:
event_type: \(proto.event_type)
session_data
session_id: \(proto.session_data.session_id.description)
first_session_id: \(proto.session_data.first_session_id.description)
session_index: \(proto.session_data.session_index)
event_timestamp_us: \(proto.session_data.event_timestamp_us)
firebase_installation_id: \(proto.session_data.firebase_installation_id.description)
firebase_authentication_token:
\(proto.session_data.firebase_authentication_token.description)
data_collection_status
crashlytics: \(proto.session_data.data_collection_status.crashlytics)
performance: \(proto.session_data.data_collection_status.performance)
session_sampling_rate: \(proto.session_data.data_collection_status.session_sampling_rate)
application_info
app_id: \(proto.application_info.app_id.description)
session_sdk_version: \(proto.application_info.session_sdk_version.description)
os_version: \(proto.application_info.os_version.description)
device_model: \(proto.application_info.device_model.description)
development_platform_name: \(proto.application_info.development_platform_name.description)
development_platform_version: \(proto.application_info.development_platform_version
.description)
session_sdk_version: \(proto.application_info.session_sdk_version.description)
apple_app_info
bundle_short_version: \(proto.application_info.apple_app_info.bundle_short_version
.description)
app_build_version: \(proto.application_info.apple_app_info.app_build_version.description)
network_connection_info
network_type: \(proto.application_info.apple_app_info.network_connection_info
.network_type.rawValue)
mobile_subtype: \(proto.application_info.apple_app_info.network_connection_info
.mobile_subtype.rawValue)
os_name: \(proto.application_info.apple_app_info.os_name.description)
log_environment: \(proto.application_info.log_environment)
"""
Logger.logInfo(logOutput)
}
}

View File

@ -0,0 +1,131 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if SWIFT_PACKAGE
import FirebaseSessionsObjC
#endif // SWIFT_PACKAGE
///
/// These extensions allows us to console log properties of our Session Events
/// proto for development and debugging purposes without having to call decode
/// on each field manually. Instead you can read `<field>.description`.
///
extension firebase_appquality_sessions_EventType: CustomStringConvertible {
public var description: String {
switch self {
case firebase_appquality_sessions_EventType_SESSION_START:
return "SESSION_START"
case firebase_appquality_sessions_EventType_EVENT_TYPE_UNKNOWN:
return "UNKNOWN"
default:
return "Unrecognized EventType. Please update the firebase_appquality_sessions_EventType CustomStringConvertible extension"
}
}
}
extension firebase_appquality_sessions_DataCollectionState: CustomStringConvertible {
public var description: String {
switch self {
case firebase_appquality_sessions_DataCollectionState_COLLECTION_ENABLED:
return "ENABLED"
case firebase_appquality_sessions_DataCollectionState_COLLECTION_SAMPLED:
return "SAMPLED"
case firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN:
return "UNKNOWN"
case firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED:
return "DISABLED"
case firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED_REMOTE:
return "DISABLED_REMOTE"
case firebase_appquality_sessions_DataCollectionState_COLLECTION_SDK_NOT_INSTALLED:
return "SDK_NOT_INSTALLED"
default:
return "Unrecognized DataCollectionState. Please update the firebase_appquality_sessions_DataCollectionState CustomStringConvertible extension"
}
}
}
extension firebase_appquality_sessions_OsName: CustomStringConvertible {
public var description: String {
switch self {
case firebase_appquality_sessions_OsName_IOS:
return "IOS"
case firebase_appquality_sessions_OsName_IPADOS:
return "IPADOS"
case firebase_appquality_sessions_OsName_TVOS:
return "TVOS"
case firebase_appquality_sessions_OsName_IOS_ON_MAC:
return "IOS_ON_MAC"
case firebase_appquality_sessions_OsName_MACOS:
return "MACOS"
case firebase_appquality_sessions_OsName_MACCATALYST:
return "MACCATALYST"
case firebase_appquality_sessions_OsName_WATCHOS:
return "WATCHOS"
case firebase_appquality_sessions_OsName_UNKNOWN_OSNAME:
return "UNKNOWN_OSNAME"
case firebase_appquality_sessions_OsName_UNSPECIFIED:
return "UNSPECIFIED"
default:
return "Unrecognized OsName. Please update the firebase_appquality_sessions_OsName CustomStringConvertible extension"
}
}
}
extension firebase_appquality_sessions_LogEnvironment: CustomStringConvertible {
public var description: String {
switch self {
case firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD:
return "PROD"
case firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_STAGING:
return "STAGING"
case firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_AUTOPUSH:
return "AUTOPUSH"
case firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_UNKNOWN:
return "UNKNOWN"
default:
return "Unrecognized LogEnvironment. Please update the firebase_appquality_sessions_LogEnvironment CustomStringConvertible extension"
}
}
}
// This is written like this for Swift backwards-compatibility.
// Once we upgrade to Xcode 14, this can be written as
// UnsafeMutablePointer<pb_bytes_array_t>
extension UnsafeMutablePointer: CustomStringConvertible where Pointee == pb_bytes_array_t {
public var description: String {
let decoded = FIRSESDecodeString(self)
if decoded.count == 0 {
return "<EMPTY>"
}
return decoded
}
}
// For an optional field
// This is written like this for Swift backwards-compatibility.
// Once we upgrade to Xcode 14, this can be written as
// UnsafeMutablePointer<pb_bytes_array_t>?
extension Optional: CustomStringConvertible
where Wrapped == UnsafeMutablePointer<pb_bytes_array_t> {
public var description: String {
guard let this = self else {
return "<NULL>"
}
return this.description
}
}

View File

@ -0,0 +1,54 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import GoogleDataTransport
protocol EventGDTLoggerProtocol {
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void)
}
///
/// EventGDTLogger is responsible for
/// 1) Creating GDT Events and logging them to the GoogleDataTransport SDK
/// 2) Handling debugging situations (eg. running in Simulator or printing the event to console)
///
class EventGDTLogger: EventGDTLoggerProtocol {
let googleDataTransport: GoogleDataTransportProtocol
let devEventConsoleLogger: EventGDTLoggerProtocol
init(googleDataTransport: GoogleDataTransportProtocol,
devEventConsoleLogger: EventGDTLoggerProtocol = DevEventConsoleLogger()) {
self.googleDataTransport = googleDataTransport
self.devEventConsoleLogger = devEventConsoleLogger
}
/// Logs the event to FireLog, taking into account debugging cases such as running
/// in simulator.
func logEvent(event: SessionStartEvent, completion: @escaping (Result<Void, Error>) -> Void) {
let gdtEvent = googleDataTransport.eventForTransport()
gdtEvent.dataObject = event
gdtEvent.qosTier = GDTCOREventQoS.qosDefault
#if targetEnvironment(simulator)
Logger.logDebug("Logging events using fast QOS due to running on a simulator")
gdtEvent.qosTier = GDTCOREventQoS.qoSFast
#endif // targetEnvironment(simulator)
devEventConsoleLogger.logEvent(event: event) { _ in }
googleDataTransport.logGDTEvent(event: gdtEvent, completion: completion)
}
}

View File

@ -0,0 +1,283 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// Avoids exposing internal FirebaseCore APIs to Swift users.
@_implementationOnly import FirebaseCoreExtension
@_implementationOnly import FirebaseInstallations
@_implementationOnly import GoogleDataTransport
#if swift(>=6.0)
internal import Promises
#elseif swift(>=5.10)
import Promises
#else
@_implementationOnly import Promises
#endif
private enum GoogleDataTransportConfig {
static let sessionsLogSource = "1974"
static let sessionsTarget = GDTCORTarget.FLL
}
@objc(FIRSessions) final class Sessions: NSObject, Library, SessionsProvider {
// MARK: - Private Variables
/// The Firebase App ID associated with Sessions.
private let appID: String
/// Top-level Classes in the Sessions SDK
private let coordinator: SessionCoordinatorProtocol
private let initiator: SessionInitiator
private let sessionGenerator: SessionGenerator
private let appInfo: ApplicationInfoProtocol
private let settings: SettingsProtocol
/// Subscribers
/// `subscribers` are used to determine the Data Collection state of the Sessions SDK.
/// If any Subscribers has Data Collection enabled, the Sessions SDK will send events
private var subscribers: [SessionsSubscriber] = []
/// `subscriberPromises` are used to wait until all Subscribers have registered
/// themselves. Subscribers must have Data Collection state available upon registering.
private var subscriberPromises: [SessionsSubscriberName: Promise<Void>] = [:]
/// Notifications
static let SessionIDChangedNotificationName = Notification
.Name("SessionIDChangedNotificationName")
let notificationCenter = NotificationCenter()
// MARK: - Initializers
// Initializes the SDK and top-level classes
required convenience init(appID: String, installations: InstallationsProtocol) {
let googleDataTransport = GDTCORTransport(
mappingID: GoogleDataTransportConfig.sessionsLogSource,
transformers: nil,
target: GoogleDataTransportConfig.sessionsTarget
)
let fireLogger = EventGDTLogger(googleDataTransport: googleDataTransport!)
let appInfo = ApplicationInfo(appID: appID)
let settings = SessionsSettings(
appInfo: appInfo,
installations: installations
)
let sessionGenerator = SessionGenerator(collectEvents: Sessions
.shouldCollectEvents(settings: settings))
let coordinator = SessionCoordinator(
installations: installations,
fireLogger: fireLogger
)
let initiator = SessionInitiator(settings: settings)
self.init(appID: appID,
sessionGenerator: sessionGenerator,
coordinator: coordinator,
initiator: initiator,
appInfo: appInfo,
settings: settings) { result in
switch result {
case .success(()):
Logger.logInfo("Successfully logged Session Start event")
case let .failure(sessionsError):
switch sessionsError {
case let .SessionInstallationsError(error):
Logger.logError(
"Error getting Firebase Installation ID: \(error). Skipping this Session Event"
)
case let .DataTransportError(error):
Logger
.logError(
"Error logging Session Start event to GoogleDataTransport: \(error)."
)
case .NoDependenciesError:
Logger
.logError(
"Sessions SDK did not have any dependent SDKs register as dependencies. Events will not be sent."
)
case .SessionSamplingError:
Logger
.logDebug(
"Sessions SDK has sampled this session"
)
case .DisabledViaSettingsError:
Logger
.logDebug(
"Sessions SDK is disabled via Settings"
)
case .DataCollectionError:
Logger
.logDebug(
"Data Collection is disabled for all subscribers. Skipping this Session Event"
)
case .SessionInstallationsTimeOutError:
Logger.logError(
"Error getting Firebase Installation ID due to timeout. Skipping this Session Event"
)
}
}
}
}
// Initializes the SDK and begins the process of listening for lifecycle events and logging
// events
init(appID: String, sessionGenerator: SessionGenerator, coordinator: SessionCoordinatorProtocol,
initiator: SessionInitiator, appInfo: ApplicationInfoProtocol, settings: SettingsProtocol,
loggedEventCallback: @escaping (Result<Void, FirebaseSessionsError>) -> Void) {
self.appID = appID
self.sessionGenerator = sessionGenerator
self.coordinator = coordinator
self.initiator = initiator
self.appInfo = appInfo
self.settings = settings
super.init()
for subscriberName in SessionsDependencies.dependencies {
subscriberPromises[subscriberName] = Promise<Void>.pending()
}
Logger
.logDebug(
"Version \(FirebaseVersion()). Expecting subscriptions from: \(SessionsDependencies.dependencies)"
)
self.initiator.beginListening {
// Generating a Session ID early is important as Subscriber
// SDKs will need to read it immediately upon registration.
let sessionInfo = self.sessionGenerator.generateNewSession()
// Post a notification so subscriber SDKs can get an updated Session ID
self.notificationCenter.post(name: Sessions.SessionIDChangedNotificationName,
object: nil)
let event = SessionStartEvent(sessionInfo: sessionInfo, appInfo: self.appInfo)
// If there are no Dependencies, then the Sessions SDK can't acknowledge
// any products data collection state, so the Sessions SDK won't send events.
guard !self.subscriberPromises.isEmpty else {
loggedEventCallback(.failure(.NoDependenciesError))
return
}
// Wait until all subscriber promises have been fulfilled before
// doing any data collection.
all(self.subscriberPromises.values).then(on: .global(qos: .background)) { _ in
guard self.isAnyDataCollectionEnabled else {
loggedEventCallback(.failure(.DataCollectionError))
return
}
Logger.logDebug("Data Collection is enabled for at least one Subscriber")
// Fetch settings if they have expired. This must happen after the check for
// data collection because it uses the network, but it must happen before the
// check for sessionsEnabled from Settings because otherwise we would permanently
// turn off the Sessions SDK when we disabled it.
self.settings.updateSettings()
self.addSubscriberFields(event: event)
event.setSamplingRate(samplingRate: self.settings.samplingRate)
guard sessionInfo.shouldDispatchEvents else {
loggedEventCallback(.failure(.SessionSamplingError))
return
}
guard self.settings.sessionsEnabled else {
loggedEventCallback(.failure(.DisabledViaSettingsError))
return
}
self.coordinator.attemptLoggingSessionStart(event: event) { result in
loggedEventCallback(result)
}
}
}
}
// MARK: - Sampling
static func shouldCollectEvents(settings: SettingsProtocol) -> Bool {
// Calculate whether we should sample events using settings data
// Sampling rate of 1 means we do not sample.
let randomValue = Double.random(in: 0 ... 1)
return randomValue <= settings.samplingRate
}
// MARK: - Data Collection
var isAnyDataCollectionEnabled: Bool {
for subscriber in subscribers {
if subscriber.isDataCollectionEnabled {
return true
}
}
return false
}
func addSubscriberFields(event: SessionStartEvent) {
for subscriber in subscribers {
event.set(subscriber: subscriber.sessionsSubscriberName,
isDataCollectionEnabled: subscriber.isDataCollectionEnabled,
appInfo: appInfo)
}
}
// MARK: - SessionsProvider
var currentSessionDetails: SessionDetails {
return SessionDetails(sessionId: sessionGenerator.currentSession?.sessionId)
}
func register(subscriber: SessionsSubscriber) {
Logger
.logDebug(
"Registering Sessions SDK subscriber with name: \(subscriber.sessionsSubscriberName), data collection enabled: \(subscriber.isDataCollectionEnabled)"
)
notificationCenter.addObserver(
forName: Sessions.SessionIDChangedNotificationName,
object: nil,
queue: nil
) { notification in
subscriber.onSessionChanged(self.currentSessionDetails)
}
// Immediately call the callback because the Sessions SDK starts
// before subscribers, so subscribers will miss the first Notification
subscriber.onSessionChanged(currentSessionDetails)
// Fulfil this subscriber's promise
subscribers.append(subscriber)
subscriberPromises[subscriber.sessionsSubscriberName]?.fulfill(())
}
// MARK: - Library conformance
static func componentsToRegister() -> [Component] {
return [Component(SessionsProvider.self,
instantiationTiming: .alwaysEager) { container, isCacheable in
// Sessions SDK only works for the default app
guard let app = container.app, app.isDefaultApp else { return nil }
isCacheable.pointee = true
let installations = Installations.installations(app: app)
return self.init(appID: app.options.googleAppID, installations: installations)
}]
}
}

View File

@ -0,0 +1,35 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Contains the list of errors that are localized for Firebase Sessions Library
enum FirebaseSessionsError: Error {
/// Event sampling related error
case SessionSamplingError
/// Firebase Installation ID related error
case SessionInstallationsError(Error)
/// Firebase Installation ID related timeout error
case SessionInstallationsTimeOutError
/// Error from the GoogleDataTransport SDK
case DataTransportError(Error)
/// Sessions SDK is disabled via settings error
case DisabledViaSettingsError
/// Sessions SDK is disabled because all Subscribers have their
/// data collection disabled
case DataCollectionError
/// Sessions SDK didn't have any Subscribers depend
/// on it via addDependency in SessionDependencies
case NoDependenciesError
}

View File

@ -0,0 +1,41 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import GoogleDataTransport
enum GoogleDataTransportProtocolErrors: Error {
case writeFailure
}
protocol GoogleDataTransportProtocol {
func logGDTEvent(event: GDTCOREvent, completion: @escaping (Result<Void, Error>) -> Void)
func eventForTransport() -> GDTCOREvent
}
extension GDTCORTransport: GoogleDataTransportProtocol {
func logGDTEvent(event: GDTCOREvent, completion: @escaping (Result<Void, Error>) -> Void) {
sendDataEvent(event) { wasWritten, error in
if let error {
completion(.failure(error))
} else if !wasWritten {
completion(.failure(GoogleDataTransportProtocolErrors.writeFailure))
} else {
completion(.success(()))
}
}
}
}

View File

@ -0,0 +1,79 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import FirebaseInstallations
protocol InstallationsProtocol {
var installationsWaitTimeInSecond: Int { get }
/// Override Installation function for testing
func authToken(completion: @escaping (InstallationsAuthTokenResult?, Error?) -> Void)
/// Override Installation function for testing
func installationID(completion: @escaping (String?, Error?) -> Void)
/// Return a tuple: (installationID, authenticationToken) for success result
func installationID(completion: @escaping (Result<(String, String), Error>) -> Void)
}
extension InstallationsProtocol {
var installationsWaitTimeInSecond: Int {
return 10
}
func installationID(completion: @escaping (Result<(String, String), Error>) -> Void) {
var authTokenComplete = ""
var intallationComplete: String?
var errorComplete: Error?
let workingGroup = DispatchGroup()
workingGroup.enter()
authToken { (authTokenResult: InstallationsAuthTokenResult?, error: Error?) in
authTokenComplete = authTokenResult?.authToken ?? ""
workingGroup.leave()
}
workingGroup.enter()
installationID { (installationID: String?, error: Error?) in
if let installationID {
intallationComplete = installationID
} else if let error = error {
errorComplete = error
}
workingGroup.leave()
}
// adding timeout for 10 seconds
let result = workingGroup
.wait(timeout: .now() + DispatchTimeInterval.seconds(installationsWaitTimeInSecond))
switch result {
case .timedOut:
completion(.failure(FirebaseSessionsError.SessionInstallationsTimeOutError))
return
default:
if let intallationComplete {
completion(.success((intallationComplete, authTokenComplete)))
} else if let errorComplete {
completion(.failure(errorComplete))
}
}
}
}
extension Installations: InstallationsProtocol {}

View File

@ -0,0 +1,62 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import FirebaseCoreExtension
///
/// Logger is responsible for printing console logs
///
enum Logger {
private static let logServiceTag = "[FirebaseSessions]"
private static let logCode = "I-SES000000"
static func logInfo(_ message: String) {
FirebaseLogger.log(
level: .info,
service: logServiceTag,
code: logCode,
message: message
)
}
static func logDebug(_ message: String) {
FirebaseLogger.log(
level: .debug,
service: logServiceTag,
code: logCode,
message: message
)
}
static func logWarning(_ message: String) {
FirebaseLogger.log(
level: .warning,
service: logServiceTag,
code: logCode,
message: message
)
}
static func logError(_ message: String) {
FirebaseLogger.log(
level: .error,
service: logServiceTag,
code: logCode,
message: message
)
}
}

View File

@ -0,0 +1,42 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if SWIFT_PACKAGE
import FirebaseSessionsObjC
#endif // SWIFT_PACKAGE
#if SWIFT_PACKAGE
@_implementationOnly import GoogleUtilities_Environment
#else
@_implementationOnly import GoogleUtilities
#endif // SWIFT_PACKAGE
protocol NetworkInfoProtocol {
var networkType: GULNetworkType { get }
var mobileSubtype: String { get }
}
class NetworkInfo: NetworkInfoProtocol {
var networkType: GULNetworkType {
return GULNetworkInfo.getNetworkType()
}
var mobileSubtype: String {
return GULNetworkInfo.getNetworkRadioType()
}
}

View File

@ -0,0 +1,32 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// Sessions Dependencies determines when a dependent SDK is
// installed in the app. The Sessions SDK uses this to figure
// out which dependencies to wait for to getting the data
// collection state.
//
// This is important because the Sessions SDK starts up before
// dependent SDKs
@objc(FIRSessionsDependencies)
public class SessionsDependencies: NSObject {
static var dependencies: Set<SessionsSubscriberName> = .init()
@objc public static func addDependency(name: SessionsSubscriberName) {
SessionsDependencies.dependencies.insert(name)
}
}

View File

@ -0,0 +1,23 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
// Sessions Provider is the Session SDK's internal
// interface for other 1P SDKs to talk to.
@objc(FIRSessionsProvider)
public protocol SessionsProvider {
@objc func register(subscriber: SessionsSubscriber)
}

View File

@ -0,0 +1,56 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Sessions Subscriber is an interface that dependent SDKs
/// must implement.
@objc(FIRSessionsSubscriber)
public protocol SessionsSubscriber {
func onSessionChanged(_ session: SessionDetails)
var isDataCollectionEnabled: Bool { get }
var sessionsSubscriberName: SessionsSubscriberName { get }
}
/// Session Payload is a container for Session Data passed to Subscribers
/// whenever the Session changes
@objc(FIRSessionDetails)
public class SessionDetails: NSObject {
@objc public var sessionId: String?
public init(sessionId: String?) {
self.sessionId = sessionId
super.init()
}
}
/// Session Subscriber Names are used for identifying subscribers
@objc(FIRSessionsSubscriberName)
public enum SessionsSubscriberName: Int, CustomStringConvertible {
case Unknown
case Crashlytics
case Performance
public var description: String {
switch self {
case .Crashlytics:
return "Crashlytics"
case .Performance:
return "Performance"
default:
return "Unknown"
}
}
}

View File

@ -0,0 +1,81 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
protocol SessionCoordinatorProtocol {
func attemptLoggingSessionStart(event: SessionStartEvent,
callback: @escaping (Result<Void, FirebaseSessionsError>) -> Void)
}
///
/// SessionCoordinator is responsible for coordinating the systems in this SDK
/// involved with sending a Session Start event.
///
class SessionCoordinator: SessionCoordinatorProtocol {
let installations: InstallationsProtocol
let fireLogger: EventGDTLoggerProtocol
init(installations: InstallationsProtocol,
fireLogger: EventGDTLoggerProtocol) {
self.installations = installations
self.fireLogger = fireLogger
}
/// Begins the process of logging a SessionStartEvent to FireLog after
/// it has been approved for sending
func attemptLoggingSessionStart(event: SessionStartEvent,
callback: @escaping (Result<Void, FirebaseSessionsError>)
-> Void) {
/// Order of execution
/// 1. Fetch the installations Id. Regardless of success, move to step 2
/// 2. Log the event. If successful, all is good. Else, log the message with error.
/// 3. If there was a FireLog error, expose it to the callback. Otherwise expose the FIID
/// error if it exists. Otherwise, success.
fillInFIID(event: event) { fiidResult in
self.fireLogger.logEvent(event: event) { logResult in
switch logResult {
case .success():
Logger.logDebug("Successfully logged Session Start event to GoogleDataTransport")
switch fiidResult {
case .success(()):
callback(.success(()))
case let .failure(error):
callback(.failure(error))
}
case let .failure(error):
callback(.failure(FirebaseSessionsError.DataTransportError(error)))
}
}
}
}
private func fillInFIID(event: SessionStartEvent,
callback: @escaping (Result<Void, FirebaseSessionsError>)
-> Void) {
installations.installationID { result in
switch result {
case let .success(installationsInfo):
event.setInstallationID(installationId: installationsInfo.0)
event.setAuthenticationToken(authenticationToken: installationsInfo.1)
callback(.success(()))
case let .failure(error):
event.setInstallationID(installationId: "")
event.setAuthenticationToken(authenticationToken: "")
callback(.failure(FirebaseSessionsError.SessionInstallationsError(error)))
}
}
}
}

View File

@ -0,0 +1,76 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import FirebaseInstallations
struct SessionInfo {
let sessionId: String
let firstSessionId: String
let shouldDispatchEvents: Bool
let sessionIndex: Int32
init(sessionId: String, firstSessionId: String, dispatchEvents: Bool, sessionIndex: Int32) {
self.sessionId = sessionId
self.firstSessionId = firstSessionId
shouldDispatchEvents = dispatchEvents
self.sessionIndex = sessionIndex
}
}
///
/// Generator is responsible for:
/// 1) Generating the Session ID
/// 2) Persisting and reading the Session ID from the last session
/// (Maybe) 3) Persisting, reading, and incrementing an increasing index
///
class SessionGenerator {
private var thisSession: SessionInfo?
private var firstSessionId = ""
private var sessionIndex: Int32
private var collectEvents: Bool
init(collectEvents: Bool) {
// This will be incremented to 0 on the first generation
sessionIndex = -1
self.collectEvents = collectEvents
}
// Generates a new Session ID. If there was already a generated Session ID
// from the last session during the app's lifecycle, it will also set the last Session ID
func generateNewSession() -> SessionInfo {
let newSessionId = UUID().uuidString.replacingOccurrences(of: "-", with: "").lowercased()
// If firstSessionId is set, use it. Otherwise set it to the
// first generated Session ID
firstSessionId = firstSessionId.isEmpty ? newSessionId : firstSessionId
sessionIndex += 1
let newSession = SessionInfo(sessionId: newSessionId,
firstSessionId: firstSessionId,
dispatchEvents: collectEvents,
sessionIndex: sessionIndex)
thisSession = newSession
return newSession
}
var currentSession: SessionInfo? {
return thisSession
}
}

View File

@ -0,0 +1,136 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if os(iOS) || os(tvOS)
import UIKit
#elseif os(macOS)
import AppKit
import Cocoa
#elseif os(watchOS)
import WatchKit
#endif // os(iOS) || os(tvOS)
// swift(>=5.9) implies Xcode 15+
// Need to have this Swift version check to use os(visionOS) macro, VisionOS support.
// TODO: Remove this check and add `os(visionOS)` to the `os(iOS) || os(tvOS)` conditional above
// when Xcode 15 is the minimum supported by Firebase.
#if swift(>=5.9)
#if os(visionOS)
import UIKit
#endif // os(visionOS)
#endif // swift(>=5.9)
///
/// The SessionInitiator is responsible for:
/// 1) Running the initiate callback whenever a Session Start Event should
/// begin sending. This can happen at a cold start of the app, and when it
/// been in the background for a period of time (originally set at 30 mins)
/// and comes to the foreground.
///
class SessionInitiator {
let currentTime: () -> Date
var settings: SettingsProtocol
var backgroundTime = Date.distantFuture
var initiateSessionStart: () -> Void = {}
init(settings: SettingsProtocol, currentTimeProvider: @escaping () -> Date = Date.init) {
currentTime = currentTimeProvider
self.settings = settings
}
func beginListening(initiateSessionStart: @escaping () -> Void) {
self.initiateSessionStart = initiateSessionStart
self.initiateSessionStart()
let notificationCenter = NotificationCenter.default
#if os(iOS) || os(tvOS)
notificationCenter.addObserver(
self,
selector: #selector(appBackgrounded),
name: UIApplication.didEnterBackgroundNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(appForegrounded),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
#elseif os(macOS)
notificationCenter.addObserver(
self,
selector: #selector(appBackgrounded),
name: NSApplication.didResignActiveNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(appForegrounded),
name: NSApplication.didBecomeActiveNotification,
object: nil
)
#elseif os(watchOS)
// Versions below WatchOS 7 do not support lifecycle events
if #available(watchOSApplicationExtension 7.0, *) {
notificationCenter.addObserver(
self,
selector: #selector(appBackgrounded),
name: WKExtension.applicationDidEnterBackgroundNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(appForegrounded),
name: WKExtension.applicationDidBecomeActiveNotification,
object: nil
)
}
#endif // os(iOS) || os(tvOS)
// swift(>=5.9) implies Xcode 15+
// Need to have this Swift version check to use os(visionOS) macro, VisionOS support.
// TODO: Remove this check and add `os(visionOS)` to the `os(iOS) || os(tvOS)` conditional above
// when Xcode 15 is the minimum supported by Firebase.
#if swift(>=5.9)
#if os(visionOS)
notificationCenter.addObserver(
self,
selector: #selector(appBackgrounded),
name: UIApplication.didEnterBackgroundNotification,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(appForegrounded),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
#endif // os(visionOS)
#endif // swift(>=5.9)
}
@objc private func appBackgrounded() {
backgroundTime = currentTime()
}
@objc private func appForegrounded() {
let interval = currentTime().timeIntervalSince(backgroundTime)
// If the interval is greater the the session timeout duration, generate a new session.
if interval > settings.sessionTimeout {
initiateSessionStart()
}
}
}

View File

@ -0,0 +1,300 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
@_implementationOnly import GoogleDataTransport
#if SWIFT_PACKAGE
import FirebaseSessionsObjC
#endif // SWIFT_PACKAGE
#if SWIFT_PACKAGE
@_implementationOnly import GoogleUtilities_Environment
#else
@_implementationOnly import GoogleUtilities
#endif // SWIFT_PACKAGE
///
/// SessionStartEvent is responsible for:
/// 1) Writing fields to the Session proto
/// 2) Synthesizing itself for persisting to disk and logging to GoogleDataTransport
///
class SessionStartEvent: NSObject, GDTCOREventDataObject {
var proto: firebase_appquality_sessions_SessionEvent
init(sessionInfo: SessionInfo, appInfo: ApplicationInfoProtocol,
time: TimeProvider = Time()) {
proto = firebase_appquality_sessions_SessionEvent()
super.init()
// Note: If you add a proto string field here, remember to free it in the deinit.
proto.event_type = firebase_appquality_sessions_EventType_SESSION_START
proto.session_data.session_id = makeProtoString(sessionInfo.sessionId)
proto.session_data.first_session_id = makeProtoString(sessionInfo.firstSessionId)
proto.session_data.session_index = sessionInfo.sessionIndex
proto.session_data.event_timestamp_us = time.timestampUS
proto.application_info.app_id = makeProtoString(appInfo.appID)
proto.application_info.session_sdk_version = makeProtoString(appInfo.sdkVersion)
proto.application_info.os_version = makeProtoString(appInfo.osDisplayVersion)
proto.application_info.log_environment = convertLogEnvironment(environment: appInfo.environment)
proto.application_info.device_model = makeProtoString(appInfo.deviceModel)
// proto.application_info.development_platform_name;
// proto.application_info.development_platform_version;
// `which_platform_info` tells nanopb which oneof we're choosing to fill in for our proto
proto.application_info.which_platform_info = FIRSESGetAppleApplicationInfoTag()
proto.application_info.apple_app_info
.bundle_short_version = makeProtoString(appInfo.appDisplayVersion)
proto.application_info.apple_app_info
.app_build_version = makeProtoString(appInfo.appBuildVersion)
proto.application_info.apple_app_info.os_name = convertOSName(osName: appInfo.osName)
// Set network info to base values but don't fill them in with the real
// value because these are only tracked when Performance is installed
proto.application_info.apple_app_info.mcc_mnc = makeProtoString("")
proto.application_info.apple_app_info.network_connection_info
.network_type = convertNetworkType(networkType: .none)
proto.application_info.apple_app_info.network_connection_info
.mobile_subtype = convertMobileSubtype(mobileSubtype: "")
proto.session_data.data_collection_status
.crashlytics = firebase_appquality_sessions_DataCollectionState_COLLECTION_SDK_NOT_INSTALLED
proto.session_data.data_collection_status
.performance = firebase_appquality_sessions_DataCollectionState_COLLECTION_SDK_NOT_INSTALLED
}
deinit {
let garbage: [UnsafeMutablePointer<pb_bytes_array_t>?] = [
proto.application_info.app_id,
proto.application_info.apple_app_info.app_build_version,
proto.application_info.apple_app_info.bundle_short_version,
proto.application_info.apple_app_info.mcc_mnc,
proto.application_info.development_platform_name,
proto.application_info.development_platform_version,
proto.application_info.device_model,
proto.application_info.os_version,
proto.application_info.session_sdk_version,
proto.session_data.session_id,
proto.session_data.firebase_installation_id,
proto.session_data.firebase_authentication_token,
proto.session_data.first_session_id,
]
for pointer in garbage {
nanopb_free(pointer)
}
}
func setInstallationID(installationId: String) {
let oldID = proto.session_data.firebase_installation_id
proto.session_data.firebase_installation_id = makeProtoString(installationId)
nanopb_free(oldID)
}
func setAuthenticationToken(authenticationToken: String) {
let oldToken = proto.session_data.firebase_authentication_token
proto.session_data.firebase_authentication_token = makeProtoString(authenticationToken)
nanopb_free(oldToken)
}
func setSamplingRate(samplingRate: Double) {
proto.session_data.data_collection_status.session_sampling_rate = samplingRate
}
func set(subscriber: SessionsSubscriberName, isDataCollectionEnabled: Bool,
appInfo: ApplicationInfoProtocol) {
let dataCollectionState = makeDataCollectionProto(isDataCollectionEnabled)
switch subscriber {
case .Crashlytics:
proto.session_data.data_collection_status.crashlytics = dataCollectionState
case .Performance:
proto.session_data.data_collection_status.performance = dataCollectionState
default:
Logger
.logWarning("Attempted to set Data Collection status for unknown Subscriber: \(subscriber)")
}
// Only set restricted fields if Data Collection is enabled. If it's disabled,
// we're treating that as if the product isn't installed.
if isDataCollectionEnabled {
setRestrictedFields(subscriber: subscriber,
appInfo: appInfo)
}
}
/// This method should be called for every subscribed Subscriber. This is for cases where
/// fields should only be collected if a specific SDK is installed.
private func setRestrictedFields(subscriber: SessionsSubscriberName,
appInfo: ApplicationInfoProtocol) {
switch subscriber {
case .Performance:
let oldString = proto.application_info.apple_app_info.mcc_mnc
proto.application_info.apple_app_info.mcc_mnc = makeProtoString("")
nanopb_free(oldString)
proto.application_info.apple_app_info.network_connection_info
.network_type = convertNetworkType(networkType: appInfo.networkInfo.networkType)
proto.application_info.apple_app_info.network_connection_info
.mobile_subtype = convertMobileSubtype(mobileSubtype: appInfo.networkInfo.mobileSubtype)
default:
break
}
}
// MARK: - GDTCOREventDataObject
func transportBytes() -> Data {
return FIRSESTransportBytes(&proto)
}
// MARK: - Data Conversion
func makeDataCollectionProto(_ isDataCollectionEnabled: Bool)
-> firebase_appquality_sessions_DataCollectionState {
if isDataCollectionEnabled {
return firebase_appquality_sessions_DataCollectionState_COLLECTION_ENABLED
} else {
return firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED
}
}
private func makeProtoStringOrNil(_ string: String?) -> UnsafeMutablePointer<pb_bytes_array_t>? {
guard let string = string else {
return nil
}
return FIRSESEncodeString(string)
}
private func makeProtoString(_ string: String) -> UnsafeMutablePointer<pb_bytes_array_t>? {
return FIRSESEncodeString(string)
}
private func convertOSName(osName: String) -> firebase_appquality_sessions_OsName {
switch osName.lowercased() {
case "macos":
return firebase_appquality_sessions_OsName_MACOS
case "maccatalyst":
return firebase_appquality_sessions_OsName_MACCATALYST
case "ios_on_mac":
return firebase_appquality_sessions_OsName_IOS_ON_MAC
case "ios":
return firebase_appquality_sessions_OsName_IOS
case "tvos":
return firebase_appquality_sessions_OsName_TVOS
case "watchos":
return firebase_appquality_sessions_OsName_WATCHOS
case "ipados":
return firebase_appquality_sessions_OsName_IPADOS
default:
Logger.logWarning("Found unknown OSName: \"\(osName)\" while converting.")
return firebase_appquality_sessions_OsName_UNKNOWN_OSNAME
}
}
/// Encodes the proto in this SessionStartEvent to Data, and then decodes the Data back into
/// the proto object and returns the decoded proto. This is used for validating encoding works
/// and should not be used in production code.
func encodeDecodeEvent() -> firebase_appquality_sessions_SessionEvent {
let transportBytes = self.transportBytes()
var proto = firebase_appquality_sessions_SessionEvent()
var fields = firebase_appquality_sessions_SessionEvent_fields
let bytes = (transportBytes as NSData).bytes
var istream: pb_istream_t = pb_istream_from_buffer(bytes, transportBytes.count)
if !pb_decode(&istream, &fields.0, &proto) {
let errorMessage = FIRSESPBGetError(istream)
if errorMessage.count > 0 {
Logger.logError("Session Event failed to decode transportBytes: \(errorMessage)")
}
}
return proto
}
/// Converts the provided log environment to its Proto format.
private func convertLogEnvironment(environment: DevEnvironment)
-> firebase_appquality_sessions_LogEnvironment {
switch environment {
case .prod:
return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD
case .staging:
return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_STAGING
case .autopush:
return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_AUTOPUSH
}
}
private func convertNetworkType(networkType: GULNetworkType)
-> firebase_appquality_sessions_NetworkConnectionInfo_NetworkType {
switch networkType {
case .WIFI:
return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_WIFI
case .mobile:
return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE
case .none:
return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_DUMMY
@unknown default:
return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_DUMMY
}
}
private func convertMobileSubtype(mobileSubtype: String)
-> firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype {
var subtype: firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype
#if os(iOS) && !targetEnvironment(macCatalyst)
switch mobileSubtype {
case CTRadioAccessTechnologyGPRS:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_GPRS
case CTRadioAccessTechnologyEdge:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EDGE
case CTRadioAccessTechnologyWCDMA:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_CDMA
case CTRadioAccessTechnologyCDMA1x:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_CDMA
case CTRadioAccessTechnologyHSDPA:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSDPA
case CTRadioAccessTechnologyHSUPA:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSUPA
case CTRadioAccessTechnologyCDMAEVDORev0:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_0
case CTRadioAccessTechnologyCDMAEVDORevA:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_A
case CTRadioAccessTechnologyCDMAEVDORevB:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_B
case CTRadioAccessTechnologyeHRPD:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EHRPD
case CTRadioAccessTechnologyLTE:
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_LTE
default:
subtype =
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE
}
if #available(iOS 14.1, *) {
if mobileSubtype == CTRadioAccessTechnologyNRNSA || mobileSubtype ==
CTRadioAccessTechnologyNR {
subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_NR
}
}
#else // os(iOS) && !targetEnvironment(macCatalyst)
subtype =
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE
#endif // os(iOS) && !targetEnvironment(macCatalyst)
return subtype
}
}

View File

@ -0,0 +1,73 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Class that manages the local overrides configs related to the library.
class LocalOverrideSettings: SettingsProvider {
// This will disable Sessions SDK Events, but not Settings requests.
// If any apps use this flag to disable the Firebase Sessions SDK,
// keep in mind this may break metrics future features with products like
// FirePerf and Crashlytics. As a result, we would recommend apps
// use another way to disable data collection (like disabling it via
// the product public data collection APIs themselves).
// This flag is internal and may break in the future.
static let PlistKey_sessions_enabled = "FirebaseSessionsEnabled"
static let PlistKey_sessions_timeout = "FirebaseSessionsTimeout"
static let PlistKey_sessions_samplingRate = "FirebaseSessionsSampingRate"
var sessionsEnabled: Bool? {
let session_enabled = plistValueForConfig(configName: LocalOverrideSettings
.PlistKey_sessions_enabled) as? Bool
if session_enabled != nil {
return Bool(session_enabled!)
}
return nil
}
var sessionTimeout: TimeInterval? {
let timeout = plistValueForConfig(configName: LocalOverrideSettings
.PlistKey_sessions_timeout) as? Double
if timeout != nil {
return Double(timeout!)
}
return nil
}
var samplingRate: Double? {
let rate = plistValueForConfig(configName: LocalOverrideSettings
.PlistKey_sessions_samplingRate) as? Double
if rate != nil {
return Double(rate!)
}
return nil
}
private func plistValueForConfig(configName: String) -> Any? {
return Bundle.main.infoDictionary?[configName]
}
}
typealias LocalOverrideSettingsProvider = LocalOverrideSettings
extension LocalOverrideSettingsProvider {
func updateSettings() {
// Nothing to be done since there is nothing to be updated.
}
func isSettingsStale() -> Bool {
// Settings are never stale since all of these are local settings from Plist
return false
}
}

View File

@ -0,0 +1,134 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Extends ApplicationInfoProtocol to string-format a combined appDisplayVersion and
/// appBuildVersion
extension ApplicationInfoProtocol {
var synthesizedVersion: String { return "\(appDisplayVersion) (\(appBuildVersion))" }
}
class RemoteSettings: SettingsProvider {
private static let cacheDurationSecondsDefault: TimeInterval = 60 * 60
private static let flagSessionsEnabled = "sessions_enabled"
private static let flagSamplingRate = "sampling_rate"
private static let flagSessionTimeout = "session_timeout_seconds"
private static let flagCacheDuration = "cache_duration"
private static let flagSessionsCache = "app_quality"
private let appInfo: ApplicationInfoProtocol
private let downloader: SettingsDownloadClient
private var cache: SettingsCacheClient
private var cacheDurationSeconds: TimeInterval {
guard let duration = cache.cacheContent[RemoteSettings.flagCacheDuration] as? Double else {
return RemoteSettings.cacheDurationSecondsDefault
}
return duration
}
private var sessionsCache: [String: Any] {
return cache.cacheContent[RemoteSettings.flagSessionsCache] as? [String: Any] ?? [:]
}
init(appInfo: ApplicationInfoProtocol,
downloader: SettingsDownloadClient,
cache: SettingsCacheClient = SettingsCache()) {
self.appInfo = appInfo
self.cache = cache
self.downloader = downloader
}
private func fetchAndCacheSettings(currentTime: Date) {
// Only fetch if cache is expired, otherwise do nothing
guard isCacheExpired(time: currentTime) else {
Logger.logDebug("[Settings] Cache is not expired, no fetch will be made.")
return
}
downloader.fetch { result in
switch result {
case let .success(dictionary):
// Saves all newly fetched Settings to cache
self.cache.cacheContent = dictionary
// Saves a "cache-key" which carries TTL metadata about current cache
self.cache.cacheKey = CacheKey(
createdAt: currentTime,
googleAppID: self.appInfo.appID,
appVersion: self.appInfo.synthesizedVersion
)
case let .failure(error):
Logger.logError("[Settings] Fetching newest settings failed with error: \(error)")
}
}
}
}
typealias RemoteSettingsConfigurations = RemoteSettings
extension RemoteSettingsConfigurations {
var sessionsEnabled: Bool? {
return sessionsCache[RemoteSettings.flagSessionsEnabled] as? Bool
}
var samplingRate: Double? {
return sessionsCache[RemoteSettings.flagSamplingRate] as? Double
}
var sessionTimeout: TimeInterval? {
return sessionsCache[RemoteSettings.flagSessionTimeout] as? Double
}
}
typealias RemoteSettingsProvider = RemoteSettings
extension RemoteSettingsConfigurations {
func updateSettings(currentTime: Date) {
fetchAndCacheSettings(currentTime: currentTime)
}
func updateSettings() {
updateSettings(currentTime: Date())
}
func isSettingsStale() -> Bool {
return isCacheExpired(time: Date())
}
private func isCacheExpired(time: Date) -> Bool {
guard !cache.cacheContent.isEmpty else {
cache.removeCache()
return true
}
guard let cacheKey = cache.cacheKey else {
Logger.logError("[Settings] Could not load settings cache key")
cache.removeCache()
return true
}
guard cacheKey.googleAppID == appInfo.appID else {
Logger
.logDebug("[Settings] Cache expired because Google App ID changed")
cache.removeCache()
return true
}
if time.timeIntervalSince(cacheKey.createdAt) > cacheDurationSeconds {
Logger.logDebug("[Settings] Cache TTL expired")
return true
}
if appInfo.synthesizedVersion != cacheKey.appVersion {
Logger.logDebug("[Settings] Cache expired because app version changed")
return true
}
return false
}
}

View File

@ -0,0 +1,46 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Class that manages the local overrides configs related to the library.
class SDKDefaultSettings: SettingsProvider {
var sessionsEnabled: Bool? {
// Default is sessions enabled
return true
}
var sessionTimeout: TimeInterval? {
// Default is 30 minutes
return 30 * 60
}
var samplingRate: Double? {
// Default is all events are dispatched
return 1.0
}
}
typealias SDKDefaultSettingsProvider = SDKDefaultSettings
extension SDKDefaultSettingsProvider {
func updateSettings() {
// Nothing to be done since there is nothing to be updated.
}
func isSettingsStale() -> Bool {
// Settings are never stale since all of these are local settings from Plist
return false
}
}

View File

@ -0,0 +1,84 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Class that manages the configs related to the settings library
class SessionsSettings: SettingsProtocol {
private let appInfo: ApplicationInfoProtocol
private let installations: InstallationsProtocol
private let sdkDefaults: SDKDefaultSettings
private let localOverrides: LocalOverrideSettings
private let remoteSettings: RemoteSettings
convenience init(appInfo: ApplicationInfoProtocol, installations: InstallationsProtocol) {
self.init(appInfo: appInfo,
installations: installations,
sdkDefaults: SDKDefaultSettings(),
localOverrides: LocalOverrideSettings(),
remoteSettings: RemoteSettings(appInfo: appInfo,
downloader: SettingsDownloader(appInfo: appInfo,
installations: installations)))
}
init(appInfo: ApplicationInfoProtocol,
installations: InstallationsProtocol,
sdkDefaults: SDKDefaultSettings,
localOverrides: LocalOverrideSettings,
remoteSettings: RemoteSettings) {
self.appInfo = appInfo
self.installations = installations
self.sdkDefaults = sdkDefaults
self.localOverrides = localOverrides
self.remoteSettings = remoteSettings
}
var sessionsEnabled: Bool {
// Order of precedence LocalOverrides > Remote Settings > SDK Defaults
if let sessionEnabled = localOverrides.sessionsEnabled {
return sessionEnabled
} else if let sessionEnabled = remoteSettings.sessionsEnabled {
return sessionEnabled
}
return sdkDefaults.sessionsEnabled!
}
var sessionTimeout: TimeInterval {
// Order of precedence LocalOverrides > Remote Settings > SDK Defaults
if let sessionTimeout = localOverrides.sessionTimeout {
return sessionTimeout
} else if let sessionTimeout = remoteSettings.sessionTimeout {
return sessionTimeout
}
return sdkDefaults.sessionTimeout!
}
var samplingRate: Double {
// Order of precedence LocalOverrides > Remote Settings > SDK Defaults
if let samplingRate = localOverrides.samplingRate {
return samplingRate
} else if let samplingRate = remoteSettings.samplingRate {
return samplingRate
}
return sdkDefaults.samplingRate!
}
func updateSettings() {
// Update the settings for all the settings providers
sdkDefaults.updateSettings()
remoteSettings.updateSettings()
localOverrides.updateSettings()
}
}

View File

@ -0,0 +1,95 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if SWIFT_PACKAGE
@_implementationOnly import GoogleUtilities_UserDefaults
#else
@_implementationOnly import GoogleUtilities
#endif // SWIFT_PACKAGE
/// CacheKey is like a "key" to a "safe". It provides necessary metadata about the current cache to
/// know if it should be expired.
struct CacheKey: Codable {
var createdAt: Date
var googleAppID: String
var appVersion: String
}
/// SettingsCacheClient is responsible for accessing the cache that Settings are stored in.
protocol SettingsCacheClient {
/// Returns in-memory cache content in O(1) time. Returns empty dictionary if it does not exist in
/// cache.
var cacheContent: [String: Any] { get set }
/// Returns in-memory cache-key, no performance guarantee because type-casting depends on size of
/// CacheKey
var cacheKey: CacheKey? { get set }
/// Removes all cache content and cache-key
func removeCache()
}
/// SettingsCache uses UserDefaults to store Settings on-disk, but also directly query UserDefaults
/// when accessing Settings values during run-time. This is because UserDefaults encapsulates both
/// in-memory and persisted-on-disk storage, allowing fast synchronous access in-app while hiding
/// away the complexity of managing persistence asynchronously.
class SettingsCache: SettingsCacheClient {
private static let settingsVersion: Int = 1
private enum UserDefaultsKeys {
static let forContent = "firebase-sessions-settings"
static let forCacheKey = "firebase-sessions-cache-key"
}
/// UserDefaults holds values in memory, making access O(1) and synchronous within the app, while
/// abstracting away async disk IO.
private let cache: GULUserDefaults = .standard()
/// Converting to dictionary is O(1) because object conversion is O(1)
var cacheContent: [String: Any] {
get {
return (cache.object(forKey: UserDefaultsKeys.forContent) as? [String: Any]) ?? [:]
}
set {
cache.setObject(newValue, forKey: UserDefaultsKeys.forContent)
}
}
/// Casting to Codable from Data is O(n)
var cacheKey: CacheKey? {
get {
if let data = cache.object(forKey: UserDefaultsKeys.forCacheKey) as? Data {
do {
return try JSONDecoder().decode(CacheKey.self, from: data)
} catch {
Logger.logError("[Settings] Decoding CacheKey failed with error: \(error)")
}
}
return nil
}
set {
do {
try cache.setObject(JSONEncoder().encode(newValue), forKey: UserDefaultsKeys.forCacheKey)
} catch {
Logger.logError("[Settings] Encoding CacheKey failed with error: \(error)")
}
}
}
/// Removes stored cache
func removeCache() {
cache.setObject(nil, forKey: UserDefaultsKeys.forContent)
cache.setObject(nil, forKey: UserDefaultsKeys.forCacheKey)
}
}

View File

@ -0,0 +1,107 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
#if SWIFT_PACKAGE
@_implementationOnly import GoogleUtilities_Environment
#else
@_implementationOnly import GoogleUtilities
#endif // SWIFT_PACKAGE
protocol SettingsDownloadClient {
func fetch(completion: @escaping (Result<[String: Any], SettingsDownloaderError>) -> Void)
}
enum SettingsDownloaderError: Error {
/// Error constructing the URL
case URLError(String)
/// Error from the URLSession task
case URLSessionError(String)
/// Error parsing the JSON response from Settings
case JSONParseError(String)
/// Error getting the Installation ID
case InstallationIDError(String)
}
class SettingsDownloader: SettingsDownloadClient {
private let appInfo: ApplicationInfoProtocol
private let installations: InstallationsProtocol
init(appInfo: ApplicationInfoProtocol, installations: InstallationsProtocol) {
self.appInfo = appInfo
self.installations = installations
}
func fetch(completion: @escaping (Result<[String: Any], SettingsDownloaderError>) -> Void) {
guard let validURL = url else {
completion(.failure(.URLError("Invalid URL")))
return
}
installations.installationID { result in
switch result {
case let .success(installationsInfo):
let request = self.buildRequest(url: validURL, fiid: installationsInfo.0)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let data {
if let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
completion(.success(dict))
} else {
completion(.failure(
.JSONParseError("Failed to parse JSON to dictionary")
))
}
} else if let error {
completion(.failure(.URLSessionError(error.localizedDescription)))
}
}
// Start the task that sends the network request
task.resume()
case let .failure(error):
completion(.failure(.InstallationIDError(error.localizedDescription)))
}
}
}
private var url: URL? {
var components = URLComponents()
components.scheme = "https"
components.host = "firebase-settings.crashlytics.com"
components.path = "/spi/v2/platforms/\(appInfo.osName)/gmp/\(appInfo.appID)/settings"
components.queryItems = [
URLQueryItem(name: "build_version", value: appInfo.appBuildVersion),
URLQueryItem(name: "display_version", value: appInfo.appDisplayVersion),
]
return components.url
}
private func buildRequest(url: URL, fiid: String) -> URLRequest {
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(fiid, forHTTPHeaderField: "X-Crashlytics-Installation-ID")
request.setValue(appInfo.deviceModel, forHTTPHeaderField: "X-Crashlytics-Device-Model")
request.setValue(
appInfo.osBuildVersion,
forHTTPHeaderField: "X-Crashlytics-OS-Build-Version"
)
request.setValue(
appInfo.osDisplayVersion,
forHTTPHeaderField: "X-Crashlytics-OS-Display-Version"
)
request.setValue(appInfo.sdkVersion, forHTTPHeaderField: "X-Crashlytics-API-Client-Version")
return request
}
}

View File

@ -0,0 +1,31 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// Provides the APIs to access Settings and their configuration values
protocol SettingsProtocol {
// Update the settings for all the settings providers
func updateSettings()
// Config to show if sessions is enabled
var sessionsEnabled: Bool { get }
// Config showing the sampling rate for sessions
var samplingRate: Double { get }
// Background timeout config value before which a new session is generated
var sessionTimeout: TimeInterval { get }
}

View File

@ -0,0 +1,35 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// APIs that needs to be implemented by any settings provider
protocol SettingsProvider {
// API to update the settings
func updateSettings()
// API to check if the settings are stale
func isSettingsStale() -> Bool
// Config to show if sessions is enabled
var sessionsEnabled: Bool? { get }
// Config showing the sampling rate for sessions
var samplingRate: Double? { get }
// Background timeout config value before which a new session is generated
var sessionTimeout: TimeInterval? { get }
}

View File

@ -0,0 +1,31 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
protocol TimeProvider {
var timestampUS: Int64 { get }
}
///
/// Time is provides timestamp values in different formats to classes in the Sessions SDK. It mainly
/// exists for testing purposes.
///
class Time: TimeProvider {
// Returns the current time as a timestamp in microseconds
var timestampUS: Int64 {
return Int64(UInt64(Date().timeIntervalSince1970) * USEC_PER_SEC)
}
}

View File

@ -0,0 +1,99 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FIRSESNanoPBHelpers_h
#define FIRSESNanoPBHelpers_h
#import <Foundation/Foundation.h>
#import <TargetConditionals.h>
#if __has_include("CoreTelephony/CTTelephonyNetworkInfo.h") && !TARGET_OS_MACCATALYST && \
!TARGET_OS_OSX && !TARGET_OS_TV
#define TARGET_HAS_MOBILE_CONNECTIVITY
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#endif
#import <nanopb/pb.h>
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>
NS_ASSUME_NONNULL_BEGIN
/// Deinitializes a nanopb struct. Rewritten here to expose to Swift, since `pb_free` is a macro.
void nanopb_free(void* _Nullable);
/// Returns an error associated with the istream. Written in Objective-C because Swift does not
/// support C language macros
NSString* FIRSESPBGetError(pb_istream_t istream);
// It seems impossible to specify the nullability of the `fields` parameter below,
// yet the compiler complains that it's missing a nullability specifier. Google
// yields no results at this time.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullability-completeness"
NSData* _Nullable FIRSESEncodeProto(const pb_field_t fields[],
const void* _Nonnull proto,
NSError** error);
#pragma clang diagnostic pop
/// Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
/// @note Memory needs to be freed manually, through pb_free or pb_release.
/// @param data The data to copy into the new bytes array.
pb_bytes_array_t* _Nullable FIRSESEncodeData(NSData* _Nullable data);
/// Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
/// @note Memory needs to be freed manually, through pb_free or pb_release.
/// @param string The string to encode as pb_bytes.
pb_bytes_array_t* _Nullable FIRSESEncodeString(NSString* _Nullable string);
/// Decodes an array of nanopb bytes into an NSData object
/// @param pbData nanopb data
NSData* FIRSESDecodeData(pb_bytes_array_t* pbData);
/// Decodes an array of nanopb bytes into an NSString object
/// @param pbData nanopb data
NSString* FIRSESDecodeString(pb_bytes_array_t* pbData);
/// Checks if 2 nanopb arrays are equal
/// @param array array to check
/// @param expected expected value of the array
BOOL FIRSESIsPBArrayEqual(pb_bytes_array_t* _Nullable array, pb_bytes_array_t* _Nullable expected);
/// Checks if a nanopb string is equal to an NSString
/// @param pbString nanopb string to check
/// @param str NSString that's expected
BOOL FIRSESIsPBStringEqual(pb_bytes_array_t* _Nullable pbString, NSString* _Nullable str);
/// Checks if a nanopb array is equal to NSData
/// @param pbArray nanopb array to check
/// @param data NSData that's expected
BOOL FIRSESIsPBDataEqual(pb_bytes_array_t* _Nullable pbArray, NSData* _Nullable data);
/// Returns the protobuf tag number. Use this to specify which oneof message type we are
/// using for the platform\_info field. This function is required to be in Objective-C because
/// Swift does not support c-style macros.
pb_size_t FIRSESGetAppleApplicationInfoTag(void);
/// Returns sysctl entry, useful for obtaining OS build version from the kernel. Copied from a
/// private method in GULAppEnvironmentUtil.
NSString* _Nullable FIRSESGetSysctlEntry(const char* sysctlKey);
/// C function to bridge from Swift to do nanopb bytes transfer.
NSData* FIRSESTransportBytes(const void* _Nonnull proto);
NS_ASSUME_NONNULL_END
#endif /* FIRSESNanoPBHelpers_h */

View File

@ -0,0 +1,205 @@
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import <GoogleUtilities/GULNetworkInfo.h>
#import "FirebaseSessions/SourcesObjC/NanoPB/FIRSESNanoPBHelpers.h"
#import "FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.h"
@import FirebaseCoreExtension;
#import <nanopb/pb.h>
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>
#import <sys/sysctl.h>
NS_ASSUME_NONNULL_BEGIN
void nanopb_free(void *_Nullable ptr) {
pb_free(ptr);
}
NSError *FIRSESMakeEncodeError(NSString *description) {
return [NSError errorWithDomain:@"FIRSESEncodeError"
code:-1
userInfo:@{@"NSLocalizedDescriptionKey" : description}];
}
NSString *FIRSESPBGetError(pb_istream_t istream) {
return [NSString stringWithCString:PB_GET_ERROR(&istream) encoding:NSASCIIStringEncoding];
}
// It seems impossible to specify the nullability of the `fields` parameter below,
// yet the compiler complains that it's missing a nullability specifier. Google
// yields no results at this time.
//
// Note 4/17/2023: The warning seems to be spurious (pb_field_t is a non-pointer
// type) and is not present on Xcode 14+. This pragma can be removed after the
// minimum supported Xcode version is above 14.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullability-completeness"
NSData *_Nullable FIRSESEncodeProto(const pb_field_t fields[],
const void *_Nonnull proto,
NSError **error) {
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
// Encode 1 time to determine the size.
if (!pb_encode(&sizestream, fields, proto)) {
NSString *errorString = [NSString
stringWithFormat:@"Error in nanopb encoding to get size: %s", PB_GET_ERROR(&sizestream)];
if (error != NULL) {
*error = FIRSESMakeEncodeError(errorString);
}
return nil;
}
// Encode a 2nd time to actually get the bytes from it.
size_t bufferSize = sizestream.bytes_written;
CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
CFDataSetLength(dataRef, bufferSize);
pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
if (!pb_encode(&ostream, fields, proto)) {
NSString *errorString =
[NSString stringWithFormat:@"Error in nanopb encoding: %s", PB_GET_ERROR(&sizestream)];
if (error != NULL) {
*error = FIRSESMakeEncodeError(errorString);
}
CFBridgingRelease(dataRef);
return nil;
}
return CFBridgingRelease(dataRef);
}
#pragma clang diagnostic pop
/** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
* @note Memory needs to be free manually, through pb_free or pb_release.
* @param data The data to copy into the new bytes array.
*/
pb_bytes_array_t *_Nullable FIRSESEncodeData(NSData *_Nullable data) {
pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
if (pbBytes == NULL) {
return NULL;
}
[data getBytes:pbBytes->bytes length:data.length];
pbBytes->size = (pb_size_t)data.length;
return pbBytes;
}
/** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
* @note Memory needs to be freed manually, through pb_free or pb_release.
* @param string The string to encode as pb_bytes.
*/
pb_bytes_array_t *_Nullable FIRSESEncodeString(NSString *_Nullable string) {
if ([string isMemberOfClass:[NSNull class]]) {
string = nil;
}
NSString *stringToEncode = string ? string : @"";
NSData *stringBytes = [stringToEncode dataUsingEncoding:NSUTF8StringEncoding];
return FIRSESEncodeData(stringBytes);
}
NSData *FIRSESDecodeData(pb_bytes_array_t *pbData) {
NSData *data = [NSData dataWithBytes:&(pbData->bytes) length:pbData->size];
return data;
}
NSString *FIRSESDecodeString(pb_bytes_array_t *pbData) {
if (pbData->size == 0) {
return @"";
}
NSData *data = FIRSESDecodeData(pbData);
// There was a bug where length 32 strings were sometimes null after encoding
// and decoding. We found that this was due to the null terminator sometimes not
// being included in the decoded code. Using stringWithCString assumes the string
// is null terminated, so we switched to initWithBytes because it takes a length.
return [[NSString alloc] initWithBytes:data.bytes
length:data.length
encoding:NSUTF8StringEncoding];
}
BOOL FIRSESIsPBArrayEqual(pb_bytes_array_t *_Nullable array, pb_bytes_array_t *_Nullable expected) {
// Treat the empty string as the same as a missing field
if (array == nil) {
return expected->size == 0;
}
if (array->size != expected->size) {
return false;
}
for (int i = 0; i < array->size; i++) {
if (expected->bytes[i] != array->bytes[i]) {
return false;
}
}
return true;
}
BOOL FIRSESIsPBStringEqual(pb_bytes_array_t *_Nullable pbString, NSString *_Nullable str) {
pb_bytes_array_t *expected = FIRSESEncodeString(str);
return FIRSESIsPBArrayEqual(pbString, expected);
}
BOOL FIRSESIsPBDataEqual(pb_bytes_array_t *_Nullable pbArray, NSData *_Nullable data) {
pb_bytes_array_t *expected = FIRSESEncodeData(data);
BOOL equal = FIRSESIsPBArrayEqual(pbArray, expected);
free(expected);
return equal;
}
pb_size_t FIRSESGetAppleApplicationInfoTag(void) {
return firebase_appquality_sessions_ApplicationInfo_apple_app_info_tag;
}
/// Copied from a private method in GULAppEnvironmentUtil.
NSString *_Nullable FIRSESGetSysctlEntry(const char *sysctlKey) {
static NSString *entryValue;
size_t size;
sysctlbyname(sysctlKey, NULL, &size, NULL, 0);
if (size > 0) {
char *entryValueCStr = malloc(size);
sysctlbyname(sysctlKey, entryValueCStr, &size, NULL, 0);
entryValue = [NSString stringWithCString:entryValueCStr encoding:NSUTF8StringEncoding];
free(entryValueCStr);
return entryValue;
} else {
return nil;
}
}
NSData *FIRSESTransportBytes(const void *_Nonnull proto) {
const pb_field_t *fields = firebase_appquality_sessions_SessionEvent_fields;
NSError *error;
NSData *data = FIRSESEncodeProto(fields, proto, &error);
if (error != nil) {
FIRLogError(
@"FirebaseSessions", @"I-SES000001", @"%@",
[NSString stringWithFormat:@"Session Event failed to encode as proto with error: %@",
error.debugDescription]);
}
if (data == nil) {
data = [NSData data];
FIRLogError(@"FirebaseSessions", @"I-SES000002",
@"Session Event generated nil transportBytes. Returning empty data.");
}
return data;
}
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,125 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.9.9 */
#include "FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.h"
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
const pb_field_t firebase_appquality_sessions_SessionEvent_fields[4] = {
PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, firebase_appquality_sessions_SessionEvent, event_type, event_type, 0),
PB_FIELD( 2, MESSAGE , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_SessionEvent, session_data, event_type, &firebase_appquality_sessions_SessionInfo_fields),
PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_SessionEvent, application_info, session_data, &firebase_appquality_sessions_ApplicationInfo_fields),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_NetworkConnectionInfo_fields[3] = {
PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, firebase_appquality_sessions_NetworkConnectionInfo, network_type, network_type, 0),
PB_FIELD( 2, UENUM , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_NetworkConnectionInfo, mobile_subtype, network_type, 0),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_SessionInfo_fields[8] = {
PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, firebase_appquality_sessions_SessionInfo, session_id, session_id, 0),
PB_FIELD( 3, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, firebase_installation_id, session_id, 0),
PB_FIELD( 4, INT64 , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_SessionInfo, event_timestamp_us, firebase_installation_id, 0),
PB_FIELD( 6, MESSAGE , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_SessionInfo, data_collection_status, event_timestamp_us, &firebase_appquality_sessions_DataCollectionStatus_fields),
PB_FIELD( 7, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, first_session_id, data_collection_status, 0),
PB_FIELD( 8, INT32 , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_SessionInfo, session_index, first_session_id, 0),
PB_FIELD( 9, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, firebase_authentication_token, session_index, 0),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_DataCollectionStatus_fields[4] = {
PB_FIELD( 1, UENUM , SINGULAR, STATIC , FIRST, firebase_appquality_sessions_DataCollectionStatus, performance, performance, 0),
PB_FIELD( 2, UENUM , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_DataCollectionStatus, crashlytics, performance, 0),
PB_FIELD( 3, DOUBLE , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_DataCollectionStatus, session_sampling_rate, crashlytics, 0),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_ApplicationInfo_fields[10] = {
PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, firebase_appquality_sessions_ApplicationInfo, app_id, app_id, 0),
PB_FIELD( 2, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_ApplicationInfo, device_model, app_id, 0),
PB_FIELD( 3, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_ApplicationInfo, development_platform_name, device_model, 0),
PB_FIELD( 4, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_ApplicationInfo, development_platform_version, development_platform_name, 0),
PB_ANONYMOUS_ONEOF_FIELD(platform_info, 5, MESSAGE , ONEOF, STATIC , OTHER, firebase_appquality_sessions_ApplicationInfo, android_app_info, development_platform_version, &firebase_appquality_sessions_AndroidApplicationInfo_fields),
PB_ANONYMOUS_ONEOF_FIELD(platform_info, 6, MESSAGE , ONEOF, STATIC , UNION, firebase_appquality_sessions_ApplicationInfo, apple_app_info, development_platform_version, &firebase_appquality_sessions_AppleApplicationInfo_fields),
PB_FIELD( 7, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_ApplicationInfo, session_sdk_version, apple_app_info, 0),
PB_FIELD( 8, UENUM , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_ApplicationInfo, log_environment, session_sdk_version, 0),
PB_FIELD( 9, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_ApplicationInfo, os_version, log_environment, 0),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_AndroidApplicationInfo_fields[3] = {
PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, firebase_appquality_sessions_AndroidApplicationInfo, package_name, package_name, 0),
PB_FIELD( 3, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_AndroidApplicationInfo, version_name, package_name, 0),
PB_LAST_FIELD
};
const pb_field_t firebase_appquality_sessions_AppleApplicationInfo_fields[6] = {
PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, firebase_appquality_sessions_AppleApplicationInfo, bundle_short_version, bundle_short_version, 0),
PB_FIELD( 3, MESSAGE , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_AppleApplicationInfo, network_connection_info, bundle_short_version, &firebase_appquality_sessions_NetworkConnectionInfo_fields),
PB_FIELD( 4, UENUM , SINGULAR, STATIC , OTHER, firebase_appquality_sessions_AppleApplicationInfo, os_name, network_connection_info, 0),
PB_FIELD( 5, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_AppleApplicationInfo, mcc_mnc, os_name, 0),
PB_FIELD( 6, BYTES , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_AppleApplicationInfo, app_build_version, mcc_mnc, 0),
PB_LAST_FIELD
};
/* Check that field information fits in pb_field_t */
#if !defined(PB_FIELD_32BIT)
/* If you get an error here, it means that you need to define PB_FIELD_32BIT
* compile-time option. You can do that in pb.h or on compiler command line.
*
* The reason you need to do this is that some of your messages contain tag
* numbers or field sizes that are larger than what can fit in 8 or 16 bit
* field descriptors.
*/
PB_STATIC_ASSERT((pb_membersize(firebase_appquality_sessions_SessionEvent, session_data) < 65536 && pb_membersize(firebase_appquality_sessions_SessionEvent, application_info) < 65536 && pb_membersize(firebase_appquality_sessions_SessionInfo, data_collection_status) < 65536 && pb_membersize(firebase_appquality_sessions_ApplicationInfo, android_app_info) < 65536 && pb_membersize(firebase_appquality_sessions_ApplicationInfo, apple_app_info) < 65536 && pb_membersize(firebase_appquality_sessions_AppleApplicationInfo, network_connection_info) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_firebase_appquality_sessions_SessionEvent_firebase_appquality_sessions_NetworkConnectionInfo_firebase_appquality_sessions_SessionInfo_firebase_appquality_sessions_DataCollectionStatus_firebase_appquality_sessions_ApplicationInfo_firebase_appquality_sessions_AndroidApplicationInfo_firebase_appquality_sessions_AppleApplicationInfo)
#endif
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
/* If you get an error here, it means that you need to define PB_FIELD_16BIT
* compile-time option. You can do that in pb.h or on compiler command line.
*
* The reason you need to do this is that some of your messages contain tag
* numbers or field sizes that are larger than what can fit in the default
* 8 bit descriptors.
*/
PB_STATIC_ASSERT((pb_membersize(firebase_appquality_sessions_SessionEvent, session_data) < 256 && pb_membersize(firebase_appquality_sessions_SessionEvent, application_info) < 256 && pb_membersize(firebase_appquality_sessions_SessionInfo, data_collection_status) < 256 && pb_membersize(firebase_appquality_sessions_ApplicationInfo, android_app_info) < 256 && pb_membersize(firebase_appquality_sessions_ApplicationInfo, apple_app_info) < 256 && pb_membersize(firebase_appquality_sessions_AppleApplicationInfo, network_connection_info) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_firebase_appquality_sessions_SessionEvent_firebase_appquality_sessions_NetworkConnectionInfo_firebase_appquality_sessions_SessionInfo_firebase_appquality_sessions_DataCollectionStatus_firebase_appquality_sessions_ApplicationInfo_firebase_appquality_sessions_AndroidApplicationInfo_firebase_appquality_sessions_AppleApplicationInfo)
#endif
/* On some platforms (such as AVR), double is really float.
* These are not directly supported by nanopb, but see example_avr_double.
* To get rid of this error, remove any double fields from your .proto.
*/
PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)
/* @@protoc_insertion_point(eof) */

View File

@ -0,0 +1,270 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.9 */
#ifndef PB_FIREBASE_APPQUALITY_SESSIONS_SESSIONS_NANOPB_H_INCLUDED
#define PB_FIREBASE_APPQUALITY_SESSIONS_SESSIONS_NANOPB_H_INCLUDED
#include <nanopb/pb.h>
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
typedef enum _firebase_appquality_sessions_EventType {
firebase_appquality_sessions_EventType_EVENT_TYPE_UNKNOWN = 0,
firebase_appquality_sessions_EventType_SESSION_START = 1
} firebase_appquality_sessions_EventType;
#define _firebase_appquality_sessions_EventType_MIN firebase_appquality_sessions_EventType_EVENT_TYPE_UNKNOWN
#define _firebase_appquality_sessions_EventType_MAX firebase_appquality_sessions_EventType_SESSION_START
#define _firebase_appquality_sessions_EventType_ARRAYSIZE ((firebase_appquality_sessions_EventType)(firebase_appquality_sessions_EventType_SESSION_START+1))
typedef enum _firebase_appquality_sessions_DataCollectionState {
firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN = 0,
firebase_appquality_sessions_DataCollectionState_COLLECTION_SDK_NOT_INSTALLED = 1,
firebase_appquality_sessions_DataCollectionState_COLLECTION_ENABLED = 2,
firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED = 3,
firebase_appquality_sessions_DataCollectionState_COLLECTION_DISABLED_REMOTE = 4,
firebase_appquality_sessions_DataCollectionState_COLLECTION_SAMPLED = 5
} firebase_appquality_sessions_DataCollectionState;
#define _firebase_appquality_sessions_DataCollectionState_MIN firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN
#define _firebase_appquality_sessions_DataCollectionState_MAX firebase_appquality_sessions_DataCollectionState_COLLECTION_SAMPLED
#define _firebase_appquality_sessions_DataCollectionState_ARRAYSIZE ((firebase_appquality_sessions_DataCollectionState)(firebase_appquality_sessions_DataCollectionState_COLLECTION_SAMPLED+1))
typedef enum _firebase_appquality_sessions_OsName {
firebase_appquality_sessions_OsName_UNKNOWN_OSNAME = 0,
firebase_appquality_sessions_OsName_MACOS = 1,
firebase_appquality_sessions_OsName_MACCATALYST = 2,
firebase_appquality_sessions_OsName_IOS_ON_MAC = 3,
firebase_appquality_sessions_OsName_IOS = 4,
firebase_appquality_sessions_OsName_TVOS = 5,
firebase_appquality_sessions_OsName_WATCHOS = 6,
firebase_appquality_sessions_OsName_IPADOS = 7,
firebase_appquality_sessions_OsName_UNSPECIFIED = 8
} firebase_appquality_sessions_OsName;
#define _firebase_appquality_sessions_OsName_MIN firebase_appquality_sessions_OsName_UNKNOWN_OSNAME
#define _firebase_appquality_sessions_OsName_MAX firebase_appquality_sessions_OsName_UNSPECIFIED
#define _firebase_appquality_sessions_OsName_ARRAYSIZE ((firebase_appquality_sessions_OsName)(firebase_appquality_sessions_OsName_UNSPECIFIED+1))
typedef enum _firebase_appquality_sessions_LogEnvironment {
firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_UNKNOWN = 0,
firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_AUTOPUSH = 1,
firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_STAGING = 2,
firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD = 3
} firebase_appquality_sessions_LogEnvironment;
#define _firebase_appquality_sessions_LogEnvironment_MIN firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_UNKNOWN
#define _firebase_appquality_sessions_LogEnvironment_MAX firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD
#define _firebase_appquality_sessions_LogEnvironment_ARRAYSIZE ((firebase_appquality_sessions_LogEnvironment)(firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD+1))
typedef enum _firebase_appquality_sessions_NetworkConnectionInfo_NetworkType {
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE = 0,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_WIFI = 1,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_MMS = 2,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_SUPL = 3,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_DUN = 4,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_HIPRI = 5,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_WIMAX = 6,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_BLUETOOTH = 7,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_DUMMY = 8,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_ETHERNET = 9,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_FOTA = 10,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_IMS = 11,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_CBS = 12,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_WIFI_P2P = 13,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_IA = 14,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE_EMERGENCY = 15,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_PROXY = 16,
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_VPN = 17
} firebase_appquality_sessions_NetworkConnectionInfo_NetworkType;
#define _firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MIN firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE
#define _firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MAX firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_VPN
#define _firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_ARRAYSIZE ((firebase_appquality_sessions_NetworkConnectionInfo_NetworkType)(firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_VPN+1))
typedef enum _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype {
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE = 0,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_GPRS = 1,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EDGE = 2,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UMTS = 3,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_CDMA = 4,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_0 = 5,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_A = 6,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_RTT = 7,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSDPA = 8,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSUPA = 9,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSPA = 10,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_IDEN = 11,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_B = 12,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_LTE = 13,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EHRPD = 14,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSPAP = 15,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_GSM = 16,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_TD_SCDMA = 17,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_IWLAN = 18,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_LTE_CA = 19,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_NR = 20,
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_COMBINED = 100
} firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype;
#define _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_MIN firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE
#define _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_MAX firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_COMBINED
#define _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_ARRAYSIZE ((firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype)(firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_COMBINED+1))
/* Struct definitions */
typedef struct _firebase_appquality_sessions_AndroidApplicationInfo {
pb_bytes_array_t *package_name;
pb_bytes_array_t *version_name;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_AndroidApplicationInfo) */
} firebase_appquality_sessions_AndroidApplicationInfo;
typedef struct _firebase_appquality_sessions_DataCollectionStatus {
firebase_appquality_sessions_DataCollectionState performance;
firebase_appquality_sessions_DataCollectionState crashlytics;
double session_sampling_rate;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_DataCollectionStatus) */
} firebase_appquality_sessions_DataCollectionStatus;
typedef struct _firebase_appquality_sessions_NetworkConnectionInfo {
firebase_appquality_sessions_NetworkConnectionInfo_NetworkType network_type;
firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype mobile_subtype;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_NetworkConnectionInfo) */
} firebase_appquality_sessions_NetworkConnectionInfo;
typedef struct _firebase_appquality_sessions_AppleApplicationInfo {
pb_bytes_array_t *bundle_short_version;
firebase_appquality_sessions_NetworkConnectionInfo network_connection_info;
firebase_appquality_sessions_OsName os_name;
pb_bytes_array_t *mcc_mnc;
pb_bytes_array_t *app_build_version;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_AppleApplicationInfo) */
} firebase_appquality_sessions_AppleApplicationInfo;
typedef struct _firebase_appquality_sessions_SessionInfo {
pb_bytes_array_t *session_id;
pb_bytes_array_t *firebase_installation_id;
int64_t event_timestamp_us;
firebase_appquality_sessions_DataCollectionStatus data_collection_status;
pb_bytes_array_t *first_session_id;
int32_t session_index;
pb_bytes_array_t *firebase_authentication_token;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_SessionInfo) */
} firebase_appquality_sessions_SessionInfo;
typedef struct _firebase_appquality_sessions_ApplicationInfo {
pb_bytes_array_t *app_id;
pb_bytes_array_t *device_model;
pb_bytes_array_t *development_platform_name;
pb_bytes_array_t *development_platform_version;
pb_size_t which_platform_info;
union {
firebase_appquality_sessions_AndroidApplicationInfo android_app_info;
firebase_appquality_sessions_AppleApplicationInfo apple_app_info;
};
pb_bytes_array_t *session_sdk_version;
firebase_appquality_sessions_LogEnvironment log_environment;
pb_bytes_array_t *os_version;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_ApplicationInfo) */
} firebase_appquality_sessions_ApplicationInfo;
typedef struct _firebase_appquality_sessions_SessionEvent {
firebase_appquality_sessions_EventType event_type;
firebase_appquality_sessions_SessionInfo session_data;
firebase_appquality_sessions_ApplicationInfo application_info;
/* @@protoc_insertion_point(struct:firebase_appquality_sessions_SessionEvent) */
} firebase_appquality_sessions_SessionEvent;
/* Default values for struct fields */
/* Initializer values for message structs */
#define firebase_appquality_sessions_SessionEvent_init_default {_firebase_appquality_sessions_EventType_MIN, firebase_appquality_sessions_SessionInfo_init_default, firebase_appquality_sessions_ApplicationInfo_init_default}
#define firebase_appquality_sessions_NetworkConnectionInfo_init_default {_firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MIN, _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_MIN}
#define firebase_appquality_sessions_SessionInfo_init_default {NULL, NULL, 0, firebase_appquality_sessions_DataCollectionStatus_init_default, NULL, 0}
#define firebase_appquality_sessions_DataCollectionStatus_init_default {_firebase_appquality_sessions_DataCollectionState_MIN, _firebase_appquality_sessions_DataCollectionState_MIN, 0}
#define firebase_appquality_sessions_ApplicationInfo_init_default {NULL, NULL, NULL, NULL, 0, {firebase_appquality_sessions_AndroidApplicationInfo_init_default}, NULL, _firebase_appquality_sessions_LogEnvironment_MIN, NULL}
#define firebase_appquality_sessions_AndroidApplicationInfo_init_default {NULL, NULL}
#define firebase_appquality_sessions_AppleApplicationInfo_init_default {NULL, firebase_appquality_sessions_NetworkConnectionInfo_init_default, _firebase_appquality_sessions_OsName_MIN, NULL, NULL}
#define firebase_appquality_sessions_SessionEvent_init_zero {_firebase_appquality_sessions_EventType_MIN, firebase_appquality_sessions_SessionInfo_init_zero, firebase_appquality_sessions_ApplicationInfo_init_zero}
#define firebase_appquality_sessions_NetworkConnectionInfo_init_zero {_firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MIN, _firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_MIN}
#define firebase_appquality_sessions_SessionInfo_init_zero {NULL, NULL, 0, firebase_appquality_sessions_DataCollectionStatus_init_zero, NULL, 0}
#define firebase_appquality_sessions_DataCollectionStatus_init_zero {_firebase_appquality_sessions_DataCollectionState_MIN, _firebase_appquality_sessions_DataCollectionState_MIN, 0}
#define firebase_appquality_sessions_ApplicationInfo_init_zero {NULL, NULL, NULL, NULL, 0, {firebase_appquality_sessions_AndroidApplicationInfo_init_zero}, NULL, _firebase_appquality_sessions_LogEnvironment_MIN, NULL}
#define firebase_appquality_sessions_AndroidApplicationInfo_init_zero {NULL, NULL}
#define firebase_appquality_sessions_AppleApplicationInfo_init_zero {NULL, firebase_appquality_sessions_NetworkConnectionInfo_init_zero, _firebase_appquality_sessions_OsName_MIN, NULL, NULL}
/* Field tags (for use in manual encoding/decoding) */
#define firebase_appquality_sessions_AndroidApplicationInfo_package_name_tag 1
#define firebase_appquality_sessions_AndroidApplicationInfo_version_name_tag 3
#define firebase_appquality_sessions_DataCollectionStatus_performance_tag 1
#define firebase_appquality_sessions_DataCollectionStatus_crashlytics_tag 2
#define firebase_appquality_sessions_DataCollectionStatus_session_sampling_rate_tag 3
#define firebase_appquality_sessions_NetworkConnectionInfo_network_type_tag 1
#define firebase_appquality_sessions_NetworkConnectionInfo_mobile_subtype_tag 2
#define firebase_appquality_sessions_AppleApplicationInfo_bundle_short_version_tag 1
#define firebase_appquality_sessions_AppleApplicationInfo_app_build_version_tag 6
#define firebase_appquality_sessions_AppleApplicationInfo_network_connection_info_tag 3
#define firebase_appquality_sessions_AppleApplicationInfo_os_name_tag 4
#define firebase_appquality_sessions_AppleApplicationInfo_mcc_mnc_tag 5
#define firebase_appquality_sessions_SessionInfo_session_id_tag 1
#define firebase_appquality_sessions_SessionInfo_first_session_id_tag 7
#define firebase_appquality_sessions_SessionInfo_session_index_tag 8
#define firebase_appquality_sessions_SessionInfo_firebase_installation_id_tag 3
#define firebase_appquality_sessions_SessionInfo_event_timestamp_us_tag 4
#define firebase_appquality_sessions_SessionInfo_data_collection_status_tag 6
#define firebase_appquality_sessions_SessionInfo_firebase_authentication_token_tag 9
#define firebase_appquality_sessions_ApplicationInfo_android_app_info_tag 5
#define firebase_appquality_sessions_ApplicationInfo_apple_app_info_tag 6
#define firebase_appquality_sessions_ApplicationInfo_app_id_tag 1
#define firebase_appquality_sessions_ApplicationInfo_device_model_tag 2
#define firebase_appquality_sessions_ApplicationInfo_development_platform_name_tag 3
#define firebase_appquality_sessions_ApplicationInfo_development_platform_version_tag 4
#define firebase_appquality_sessions_ApplicationInfo_session_sdk_version_tag 7
#define firebase_appquality_sessions_ApplicationInfo_os_version_tag 9
#define firebase_appquality_sessions_ApplicationInfo_log_environment_tag 8
#define firebase_appquality_sessions_SessionEvent_event_type_tag 1
#define firebase_appquality_sessions_SessionEvent_session_data_tag 2
#define firebase_appquality_sessions_SessionEvent_application_info_tag 3
/* Struct field encoding specification for nanopb */
extern const pb_field_t firebase_appquality_sessions_SessionEvent_fields[4];
extern const pb_field_t firebase_appquality_sessions_NetworkConnectionInfo_fields[3];
extern const pb_field_t firebase_appquality_sessions_SessionInfo_fields[8];
extern const pb_field_t firebase_appquality_sessions_DataCollectionStatus_fields[4];
extern const pb_field_t firebase_appquality_sessions_ApplicationInfo_fields[10];
extern const pb_field_t firebase_appquality_sessions_AndroidApplicationInfo_fields[3];
extern const pb_field_t firebase_appquality_sessions_AppleApplicationInfo_fields[6];
/* Maximum encoded size of messages (where known) */
/* firebase_appquality_sessions_SessionEvent_size depends on runtime parameters */
#define firebase_appquality_sessions_NetworkConnectionInfo_size 4
/* firebase_appquality_sessions_SessionInfo_size depends on runtime parameters */
#define firebase_appquality_sessions_DataCollectionStatus_size 13
/* firebase_appquality_sessions_ApplicationInfo_size depends on runtime parameters */
/* firebase_appquality_sessions_AndroidApplicationInfo_size depends on runtime parameters */
/* firebase_appquality_sessions_AppleApplicationInfo_size depends on runtime parameters */
/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
#define SESSIONS_MESSAGES \
#endif
/* @@protoc_insertion_point(eof) */
#endif

202
Pods/FirebaseSessions/LICENSE generated Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

302
Pods/FirebaseSessions/README.md generated Normal file
View File

@ -0,0 +1,302 @@
<p align="center">
<a href="https://cocoapods.org/pods/Firebase">
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=CocoaPods"/>
</a>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/github/v/release/Firebase/firebase-ios-sdk?style=flat&label=Swift%20Package%20Index&color=red"/>
</a>
<a href="https://cocoapods.org/pods/Firebase">
<img src="https://img.shields.io/github/license/Firebase/firebase-ios-sdk?style=flat"/>
</a><br/>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dplatforms"/>
</a>
<a href="https://swiftpackageindex.com/firebase/firebase-ios-sdk">
<img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Ffirebase%2Ffirebase-ios-sdk%2Fbadge%3Ftype%3Dswift-versions"/>
</a>
</p>
# Firebase Apple Open Source Development
This repository contains the source code for all Apple platform Firebase SDKs except FirebaseAnalytics.
Firebase is an app development platform with tools to help you build, grow, and
monetize your app. More information about Firebase can be found on the
[official Firebase website](https://firebase.google.com).
## Installation
See the subsections below for details about the different installation methods. Where
available, it's recommended to install any libraries with a `Swift` suffix to get the
best experience when writing your app in Swift.
1. [Standard pod install](#standard-pod-install)
2. [Swift Package Manager](#swift-package-manager)
3. [Installing from the GitHub repo](#installing-from-github)
4. [Experimental Carthage](#carthage-ios-only)
### Standard pod install
For instructions on the standard pod install, visit:
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Swift Package Manager
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
found in the [SwiftPackageManager.md](SwiftPackageManager.md) Markdown file.
### Installing from GitHub
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See [the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```ruby
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'main'
```
To access FirebaseMessaging via a checked-out version of the firebase-ios-sdk repo:
```ruby
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution can be found at
[Carthage.md](Carthage.md).
### Using Firebase from a Framework or a library
For details on using Firebase from a Framework or a library, refer to [firebase_in_libraries.md](docs/firebase_in_libraries.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 15.2 (or later)
CocoaPods is still the canonical way to develop, but much of the repo now supports
development with Swift Package Manager.
### CocoaPods
Install the following:
* CocoaPods 1.12.0 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
For the pod that you want to develop:
```ruby
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
```
Note: If the CocoaPods cache is out of date, you may need to run
`pod repo update` before the `pod gen` command.
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
those platforms. Since 10.2, Xcode does not properly handle multi-platform
CocoaPods workspaces.
Firestore has a self-contained Xcode project. See
[Firestore/README](Firestore/README.md) Markdown file.
#### Development for Catalyst
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
* Check the Mac box in the App-iOS Build Settings
* Sign the App in the Settings Signing & Capabilities tab
* Click Pods in the Project Manager
* Add Signing to the iOS host app and unit test targets
* Select the Unit-unit scheme
* Run it to build and test
Alternatively, disable signing in each target:
* Go to Build Settings tab
* Click `+`
* Select `Add User-Defined Setting`
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
### Swift Package Manager
* To enable test schemes: `./scripts/setup_spm_tests.sh`
* `open Package.swift` or double click `Package.swift` in Finder.
* Xcode will open the project
* Choose a scheme for a library to build or test suite to run
* Choose a target platform by selecting the run destination along with the scheme
### Adding a New Firebase Pod
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
### Managing Headers and Imports
For information about managing headers and imports, see [HeadersImports](HeadersImports.md) Markdown file.
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
before creating a pull request (PR).
GitHub Actions will verify that any code changes are done in a style-compliant
way. Install `clang-format` and `mint`:
```console
brew install clang-format@18
brew install mint
```
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
### Running Sample Apps
To run the sample apps and integration tests, you'll need a valid
`GoogleService-Info.plist
` file. The Firebase Xcode project contains dummy plist
files without real values, but they can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g., `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
### Coverage Report Generation
For coverage report generation instructions, see [scripts/code_coverage_report/README](scripts/code_coverage_report/README.md) Markdown file.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
For specific Firebase Auth development, refer to the [Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
The Firebase Database Integration tests can be run against a locally running Database Emulator
or against a production instance.
To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
running the integration test.
To run against a production instance, provide a valid `GoogleServices-Info.plist` and copy it to
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
running.
### Firebase Dynamic Links
Firebase Dynamic Links is **deprecated** and should not be used in new projects. The service will shut down on August 25, 2025.
Please see our [Dynamic Links Deprecation FAQ documentation](https://firebase.google.com/support/dynamic-links-faq) for more guidance.
### Firebase Performance Monitoring
For specific Firebase Performance Monitoring development, see
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
integrating Performance with the dev test App.
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[StorageIntegration.swift](FirebaseStorage/Tests/Integration/StorageIntegration.swift).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the
Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications and will not receive push notifications.
To receive push notifications, follow the steps above and run the app on a physical device.
### Vertex AI for Firebase
See the [Vertex AI for Firebase README](FirebaseVertexAI#development) for
instructions about building and testing the SDK.
## Building with Firebase on Apple platforms
Firebase provides official beta support for macOS, Catalyst, and tvOS. visionOS and watchOS
are community supported. Thanks to community contributions for many of the multi-platform PRs.
At this time, most of Firebase's products are available across Apple platforms. There are still
a few gaps, especially on visionOS and watchOS. For details about the current support matrix, see
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
in Firebase's documentation.
### visionOS
Where supported, visionOS works as expected with the exception of Firestore via Swift Package
Manager where it is required to use the source distribution.
To enable the Firestore source distribution, quit Xcode and open the desired
project from the command line with the `FIREBASE_SOURCE_FIRESTORE` environment
variable: `open --env FIREBASE_SOURCE_FIRESTORE /path/to/project.xcodeproj`.
To go back to using the binary distribution of Firestore, quit Xcode and open
Xcode like normal, without the environment variable.
### watchOS
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
work on watchOS. See the [Independent Watch App Sample](Example/watchOSSample).
Keep in mind that watchOS is not officially supported by Firebase. While we can catch basic unit
test issues with GitHub Actions, there may be some changes where the SDK no longer works as expected
on watchOS. If you encounter this, please
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
During app setup in the console, you may get to a step that mentions something like "Checking if the
app has communicated with our servers". This relies on Analytics and will not work on watchOS.
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
#### Additional Crashlytics Notes
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
## Combine
Thanks to contributions from the community, _FirebaseCombineSwift_ contains support for Apple's Combine
framework. This module is currently under development and not yet supported for use in production
environments. For more details, please refer to the [docs](FirebaseCombineSwift/README.md).
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase Apple SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
Apple SDK.
## License
The contents of this repository are licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).