mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
* Restores ML Pods after M77. * Fix Package.swift * Re-add catalyst to GHA workflow. * Add tests for list models. * Failable emptying of model directory in tests. * Remove path from local model info and user defaults. * Clean up user defaults on delete + add interpreter to test app. * Replace .absoluteString with .path. This makes model.path work directly with TFLite interpreter. * Fix paths in tests. * Unit test for successful get model. * Rename modelPath to modelURL. * Use defaults.integer instead of casting to Int. * Remove force casting. * Mostly minor fixes that came out of the bugbash (#7462) * Use appropriate errors for network offline and api disabled. * Error + test for resourceExhausted. * Surface backend error message in logs whenever available. * Add disclaimer to empty directories for testing only. * Add Firelog events for model info retrieval. * Add documentation + other minor fixes. (#7465) * Add docs + clean up ModelDownloader.swift. * Add docs + clean up ModelDownloadTask.swift. * Add docs + clean up FileDownloader.swift. * Add docs + clean up LocalModelInfo.swift. Handle TODO for userdefaults init failure. * Add docs + clean up RemoteModelInfo.swift, DownloaderUserDefaults.swift. * Add docs + clean up ModelInfoRetriever.swift. * Add docs + clean up ModelDownloadConditions.swift. * Add docs + clean up DeviceLogger.swift. * Add docs + clean up TelemetryLogger.swift. * Add docs + clean up ModelFileManager.swift. * Remove resolved TODOs + other minor fixes. * Update model download conditions init. * Fix comment formatting. * Minor newline formatting. * Minor comment formatting. * Add docs for ModelDownloadConditions. * Basic fix for user defaults race condition. * Minor updates to tests. * Minor updates to tests. * Final fixes (#7471) * Refactor error parsing in model info response. * Add Firelog for delete event + minor fixes. * Temporary - test to check telemetry for delete event. * Disable backup of model files; handle inability to locate model storage dir. * Minor fixes. * Add tflite path extension to model file.
252 lines
8.3 KiB
Swift
252 lines
8.3 KiB
Swift
// Copyright 2021 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
|
|
import FirebaseCore
|
|
import GoogleDataTransport
|
|
|
|
/// Extension to set Firebase app info.
|
|
extension SystemInfo {
|
|
mutating func setAppInfo(apiKey: String?, projectID: String?) {
|
|
appID = Bundle.main.bundleIdentifier ?? "unknownBundleID"
|
|
let appVersionKey = "CFBundleShortVersionString"
|
|
appVersion = Bundle.main.infoDictionary?[appVersionKey] as? String ?? "unknownAppVersion"
|
|
mlSdkVersion = FirebaseVersion()
|
|
self.apiKey = apiKey ?? "unknownAPIKey"
|
|
firebaseProjectID = projectID ?? "unknownProjectID"
|
|
}
|
|
}
|
|
|
|
/// Extension to set model options.
|
|
extension ModelOptions {
|
|
mutating func setModelOptions(model: CustomModel, isModelUpdateEnabled: Bool? = nil) {
|
|
if let updateEnabled = isModelUpdateEnabled {
|
|
self.isModelUpdateEnabled = updateEnabled
|
|
}
|
|
modelInfo.name = model.name
|
|
modelInfo.hash = model.hash
|
|
modelInfo.modelType = .custom
|
|
}
|
|
}
|
|
|
|
/// Extension to build model delete log event.
|
|
extension DeleteModelLogEvent {
|
|
mutating func setEvent(modelType: ModelInfo.ModelType = .custom, isSuccessful: Bool) {
|
|
self.modelType = modelType
|
|
self.isSuccessful = isSuccessful
|
|
}
|
|
}
|
|
|
|
/// Extension to build model download log event.
|
|
extension ModelDownloadLogEvent {
|
|
mutating func setEvent(status: DownloadStatus, errorCode: ErrorCode,
|
|
roughDownloadDuration: UInt64? = nil, exactDownloadDuration: UInt64? = nil,
|
|
downloadFailureStatus: Int64? = nil, modelOptions: ModelOptions) {
|
|
downloadStatus = status
|
|
self.errorCode = errorCode
|
|
if let roughDuration = roughDownloadDuration {
|
|
roughDownloadDurationMs = roughDuration
|
|
}
|
|
if let exactDuration = exactDownloadDuration {
|
|
exactDownloadDurationMs = exactDuration
|
|
}
|
|
if let failureStatus = downloadFailureStatus {
|
|
self.downloadFailureStatus = failureStatus
|
|
}
|
|
options = modelOptions
|
|
}
|
|
}
|
|
|
|
/// Extension to build log event.
|
|
extension FirebaseMlLogEvent {
|
|
mutating func setEvent(eventName: EventName, systemInfo: SystemInfo,
|
|
modelDownloadLogEvent: ModelDownloadLogEvent) {
|
|
self.eventName = eventName
|
|
self.systemInfo = systemInfo
|
|
self.modelDownloadLogEvent = modelDownloadLogEvent
|
|
}
|
|
|
|
mutating func setEvent(eventName: EventName, systemInfo: SystemInfo,
|
|
deleteModelLogEvent: DeleteModelLogEvent) {
|
|
self.eventName = eventName
|
|
self.systemInfo = systemInfo
|
|
self.deleteModelLogEvent = deleteModelLogEvent
|
|
}
|
|
}
|
|
|
|
/// Data object for Firelog event.
|
|
class FBMLDataObject: NSObject, GDTCOREventDataObject {
|
|
private let event: FirebaseMlLogEvent
|
|
|
|
init(event: FirebaseMlLogEvent) {
|
|
self.event = event
|
|
}
|
|
|
|
/// Encode Firelog event for transport.
|
|
func transportBytes() -> Data {
|
|
do {
|
|
let data = try event.serializedData()
|
|
return data
|
|
} catch {
|
|
DeviceLogger.logEvent(level: .debug,
|
|
message: TelemetryLogger.ErrorDescription.encodeEvent,
|
|
messageCode: .analyticsEventEncodeError)
|
|
return Data()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Firelog logger.
|
|
class TelemetryLogger {
|
|
/// Mapping ID for the log source.
|
|
private let mappingID = "1326"
|
|
|
|
/// Current Firebase app.
|
|
private let app: FirebaseApp
|
|
|
|
/// Transport for Firelog events.
|
|
private let fllTransport: GDTCORTransport
|
|
|
|
/// Init logger, could be nil if unable to get event transport.
|
|
init?(app: FirebaseApp) {
|
|
self.app = app
|
|
guard let fllTransport = GDTCORTransport(
|
|
mappingID: mappingID,
|
|
transformers: nil,
|
|
target: GDTCORTarget.FLL
|
|
) else {
|
|
DeviceLogger.logEvent(level: .debug,
|
|
message: TelemetryLogger.ErrorDescription.initTelemetryLogger,
|
|
messageCode: .telemetryInitError)
|
|
return nil
|
|
}
|
|
self.fllTransport = fllTransport
|
|
}
|
|
|
|
/// Log events to Firelog.
|
|
private func logModelEvent(event: FirebaseMlLogEvent) {
|
|
let eventForTransport: GDTCOREvent = fllTransport.eventForTransport()
|
|
eventForTransport.dataObject = FBMLDataObject(event: event)
|
|
fllTransport.sendTelemetryEvent(eventForTransport)
|
|
}
|
|
|
|
/// Log model deleted event to Firelog.
|
|
func logModelDeletedEvent(eventName: EventName, isSuccessful: Bool) {
|
|
guard app.isDataCollectionDefaultEnabled else { return }
|
|
var systemInfo = SystemInfo()
|
|
let apiKey = app.options.apiKey
|
|
let projectID = app.options.projectID
|
|
systemInfo.setAppInfo(apiKey: apiKey, projectID: projectID)
|
|
var deleteModelLogEvent = DeleteModelLogEvent()
|
|
deleteModelLogEvent.setEvent(isSuccessful: isSuccessful)
|
|
var fbmlEvent = FirebaseMlLogEvent()
|
|
fbmlEvent.setEvent(
|
|
eventName: eventName,
|
|
systemInfo: systemInfo,
|
|
deleteModelLogEvent: deleteModelLogEvent
|
|
)
|
|
logModelEvent(event: fbmlEvent)
|
|
}
|
|
|
|
/// Log model info retrieval event to Firelog.
|
|
func logModelInfoRetrievalEvent(eventName: EventName,
|
|
status: ModelDownloadLogEvent.DownloadStatus,
|
|
modelInfoErrorCode: ModelInfoErrorCode) {
|
|
guard app.isDataCollectionDefaultEnabled else { return }
|
|
var systemInfo = SystemInfo()
|
|
let apiKey = app.options.apiKey
|
|
let projectID = app.options.projectID
|
|
systemInfo.setAppInfo(apiKey: apiKey, projectID: projectID)
|
|
var errorCode = ErrorCode()
|
|
switch modelInfoErrorCode {
|
|
case .noError:
|
|
errorCode = .noError
|
|
case .noHash:
|
|
errorCode = .modelInfoDownloadNoHash
|
|
case .connectionFailed:
|
|
errorCode = .modelInfoDownloadConnectionFailed
|
|
case .hashMismatch:
|
|
errorCode = .modelHashMismatch
|
|
case let .httpError(code):
|
|
errorCode = ErrorCode(rawValue: code) ?? .modelInfoDownloadUnsuccessfulHTTPStatus
|
|
}
|
|
let modelOptions = ModelOptions()
|
|
var modelDownloadLogEvent = ModelDownloadLogEvent()
|
|
modelDownloadLogEvent.setEvent(
|
|
status: status,
|
|
errorCode: errorCode,
|
|
modelOptions: modelOptions
|
|
)
|
|
var fbmlEvent = FirebaseMlLogEvent()
|
|
fbmlEvent.setEvent(
|
|
eventName: eventName,
|
|
systemInfo: systemInfo,
|
|
modelDownloadLogEvent: modelDownloadLogEvent
|
|
)
|
|
logModelEvent(event: fbmlEvent)
|
|
}
|
|
|
|
/// Log model download event to Firelog.
|
|
func logModelDownloadEvent(eventName: EventName, status: ModelDownloadLogEvent.DownloadStatus,
|
|
model: CustomModel? = nil, downloadErrorCode: ModelDownloadErrorCode) {
|
|
guard app.isDataCollectionDefaultEnabled else { return }
|
|
var modelOptions = ModelOptions()
|
|
if let model = model {
|
|
modelOptions.setModelOptions(model: model)
|
|
}
|
|
var systemInfo = SystemInfo()
|
|
let apiKey = app.options.apiKey
|
|
let projectID = app.options.projectID
|
|
systemInfo.setAppInfo(apiKey: apiKey, projectID: projectID)
|
|
|
|
var errorCode = ErrorCode()
|
|
switch downloadErrorCode {
|
|
case .noError:
|
|
errorCode = .noError
|
|
case .urlExpired:
|
|
errorCode = .uriExpired
|
|
case .noConnection:
|
|
errorCode = .noNetworkConnection
|
|
case .downloadFailed:
|
|
errorCode = .downloadFailed
|
|
case let .httpError(code):
|
|
errorCode = ErrorCode(rawValue: code) ?? .unknownError
|
|
}
|
|
|
|
var modelDownloadLogEvent = ModelDownloadLogEvent()
|
|
modelDownloadLogEvent.setEvent(
|
|
status: status,
|
|
errorCode: errorCode,
|
|
modelOptions: modelOptions
|
|
)
|
|
|
|
var fbmlEvent = FirebaseMlLogEvent()
|
|
fbmlEvent.setEvent(
|
|
eventName: eventName,
|
|
systemInfo: systemInfo,
|
|
modelDownloadLogEvent: modelDownloadLogEvent
|
|
)
|
|
logModelEvent(event: fbmlEvent)
|
|
}
|
|
}
|
|
|
|
/// Possible error messages while logging telemetry.
|
|
extension TelemetryLogger {
|
|
/// Error descriptions.
|
|
fileprivate enum ErrorDescription {
|
|
static let encodeEvent = "Unable to encode event for Firelog."
|
|
static let initTelemetryLogger = "Unable to create telemetry logger."
|
|
}
|
|
}
|