Files
firebase-ios-sdk/FirebaseCore/Internal/Sources/HeartbeatLogging/Storage.swift
T

146 lines
4.5 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
/// A type that reads from and writes to an underlying storage container.
protocol Storage {
/// Reads and returns the data stored by this storage type.
/// - Returns: The data read from storage.
/// - Throws: An error if the read failed.
func read() throws -> Data
/// Writes the given data to this storage type.
/// - Throws: An error if the write failed.
func write(_ data: Data?) throws
}
/// Error types for `Storage` operations.
enum StorageError: Error {
case readError
case writeError
}
// MARK: - FileStorage
/// A object that provides API for reading and writing to a file system resource.
final class FileStorage: Storage {
/// A file system URL to the underlying file resource.
private let url: URL
/// The file manager used to perform file system operations.
private let fileManager: FileManager
/// Designated initializer.
/// - Parameters:
/// - url: A file system URL for the underlying file resource.
/// - fileManager: A file manager. Defaults to `default` manager.
init(url: URL, fileManager: FileManager = .default) {
self.url = url
self.fileManager = fileManager
}
/// Reads and returns the data from this object's associated file resource.
///
/// - Returns: The data stored on disk.
/// - Throws: An error if reading the contents of the file resource fails (i.e. file doesn't
/// exist).
func read() throws -> Data {
do {
return try Data(contentsOf: url)
} catch {
throw StorageError.readError
}
}
/// Writes the given data to this object's associated file resource.
///
/// When the given `data` is `nil`, this object's associated file resource is emptied.
///
/// - Parameter data: The `Data?` to write to this object's associated file resource.
func write(_ data: Data?) throws {
do {
try createDirectories(in: url.deletingLastPathComponent())
if let data {
try data.write(to: url, options: .atomic)
} else {
let emptyData = Data()
try emptyData.write(to: url, options: .atomic)
}
} catch {
throw StorageError.writeError
}
}
/// Creates all directories in the given file system URL.
///
/// If the directory for the given URL already exists, the error is ignored because the directory
/// has already been created.
///
/// - Parameter url: The URL to create directories in.
private func createDirectories(in url: URL) throws {
do {
try fileManager.createDirectory(
at: url,
withIntermediateDirectories: true
)
} catch CocoaError.fileWriteFileExists {
// Directory already exists.
} catch { throw error }
}
}
// MARK: - UserDefaultsStorage
/// A object that provides API for reading and writing to a user defaults resource.
final class UserDefaultsStorage: Storage {
/// The underlying defaults container.
private let defaults: UserDefaults
/// The key mapping to the object's associated resource in `defaults`.
private let key: String
/// Designated initializer.
/// - Parameters:
/// - defaults: The defaults container.
/// - key: The key mapping to the value stored in the defaults container.
init(defaults: UserDefaults, key: String) {
self.defaults = defaults
self.key = key
}
/// Reads and returns the data from this object's associated defaults resource.
///
/// - Returns: The data stored on disk.
/// - Throws: An error if no data has been stored to the defaults container.
func read() throws -> Data {
if let data = defaults.data(forKey: key) {
return data
} else {
throw StorageError.readError
}
}
/// Writes the given data to this object's associated defaults.
///
/// When the given `data` is `nil`, the associated default is removed.
///
/// - Parameter data: The `Data?` to write to this object's associated defaults.
func write(_ data: Data?) throws {
if let data {
defaults.set(data, forKey: key)
} else {
defaults.removeObject(forKey: key)
}
}
}