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.
120 lines
4.3 KiB
Swift
120 lines
4.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
|
|
|
|
/// File downloader response.
|
|
struct FileDownloaderResponse {
|
|
var urlResponse: HTTPURLResponse
|
|
var fileURL: URL
|
|
}
|
|
|
|
/// Possible file downloader errors.
|
|
enum FileDownloaderError: Error {
|
|
case unexpectedResponseType
|
|
case networkError(Error)
|
|
}
|
|
|
|
/// Protocol to download a file from server.
|
|
protocol FileDownloader {
|
|
typealias CompletionHandler = (Result<FileDownloaderResponse, Error>) -> Void
|
|
typealias ProgressHandler = (_ bytesWritten: Int64, _ bytesExpectedToWrite: Int64) -> Void
|
|
|
|
func downloadFile(with url: URL,
|
|
progressHandler: @escaping ProgressHandler,
|
|
completion: @escaping CompletionHandler)
|
|
}
|
|
|
|
/// Downloader to get model files from server.
|
|
class ModelFileDownloader: NSObject, FileDownloader {
|
|
/// Model conditions for download.
|
|
private let conditions: ModelDownloadConditions
|
|
|
|
/// URL session configuration.
|
|
private let configuration: URLSessionConfiguration
|
|
|
|
/// Task to handle model file download.
|
|
private var downloadTask: URLSessionDownloadTask?
|
|
|
|
/// URLSession to handle model downloads.
|
|
private lazy var downloadSession: URLSession = URLSession(
|
|
configuration: configuration,
|
|
delegate: self,
|
|
delegateQueue: nil
|
|
)
|
|
/// Successful download completion handler.
|
|
private var completion: FileDownloader.CompletionHandler?
|
|
|
|
/// Download progress handler.
|
|
private var progressHandler: FileDownloader.ProgressHandler?
|
|
|
|
init(conditions: ModelDownloadConditions) {
|
|
self.conditions = conditions
|
|
configuration = URLSessionConfiguration.ephemeral
|
|
/// Wait for network connectivity.
|
|
if #available(iOS 11.0, macOS 10.13, macCatalyst 13.0, tvOS 11.0, watchOS 4.0, *) {
|
|
self.configuration.waitsForConnectivity = true
|
|
/// Wait for 10 minutes.
|
|
self.configuration.timeoutIntervalForResource = 600
|
|
}
|
|
configuration.allowsCellularAccess = conditions.allowsCellularAccess
|
|
}
|
|
|
|
func downloadFile(with url: URL,
|
|
progressHandler: @escaping (Int64, Int64) -> Void,
|
|
completion: @escaping (Result<FileDownloaderResponse, Error>) -> Void) {
|
|
// TODO: Fail if download already in progress.
|
|
self.completion = completion
|
|
self.progressHandler = progressHandler
|
|
let downloadTask = downloadSession.downloadTask(with: url)
|
|
// Begin or resume model download.
|
|
downloadTask.resume()
|
|
self.downloadTask = downloadTask
|
|
}
|
|
}
|
|
|
|
/// Extension to handle delegate methods.
|
|
extension ModelFileDownloader: URLSessionDownloadDelegate {
|
|
// Handle client-side errors.
|
|
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
|
guard let error = error else { return }
|
|
session.finishTasksAndInvalidate()
|
|
// Unable to resolve hostname or connect to host.
|
|
completion?(.failure(FileDownloaderError.networkError(error)))
|
|
}
|
|
|
|
/// Download completion.
|
|
func urlSession(_ session: URLSession,
|
|
downloadTask: URLSessionDownloadTask,
|
|
didFinishDownloadingTo location: URL) {
|
|
guard let response = downloadTask.response,
|
|
let urlResponse = response as? HTTPURLResponse else {
|
|
completion?(.failure(FileDownloaderError.unexpectedResponseType))
|
|
return
|
|
}
|
|
let downloaderResponse = FileDownloaderResponse(urlResponse: urlResponse, fileURL: location)
|
|
session.finishTasksAndInvalidate()
|
|
completion?(.success(downloaderResponse))
|
|
}
|
|
|
|
/// Download progress.
|
|
func urlSession(_ session: URLSession,
|
|
downloadTask: URLSessionDownloadTask,
|
|
didWriteData bytesWritten: Int64,
|
|
totalBytesWritten: Int64,
|
|
totalBytesExpectedToWrite: Int64) {
|
|
progressHandler?(totalBytesWritten, totalBytesExpectedToWrite)
|
|
}
|
|
}
|