修改pods
This commit is contained in:
@@ -15,25 +15,34 @@
|
||||
import Foundation
|
||||
|
||||
/// An object that provides API to log and flush heartbeats from a synchronized storage container.
|
||||
public final class HeartbeatController {
|
||||
public final class HeartbeatController: Sendable {
|
||||
/// Used for standardizing dates for calendar-day comparison.
|
||||
private enum DateStandardizer {
|
||||
private static let calendar: Calendar = {
|
||||
var calendar = Calendar(identifier: .iso8601)
|
||||
calendar.locale = Locale(identifier: "en_US_POSIX")
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
return calendar
|
||||
}()
|
||||
|
||||
static func standardize(_ date: Date) -> (Date) {
|
||||
return calendar.startOfDay(for: date)
|
||||
}
|
||||
}
|
||||
|
||||
/// The thread-safe storage object to log and flush heartbeats from.
|
||||
private let storage: HeartbeatStorageProtocol
|
||||
private let storage: any HeartbeatStorageProtocol
|
||||
/// The max capacity of heartbeats to store in storage.
|
||||
private let heartbeatsStorageCapacity: Int = 30
|
||||
private static let heartbeatsStorageCapacity: Int = 30
|
||||
/// Current date provider. It is used for testability.
|
||||
private let dateProvider: () -> Date
|
||||
/// Used for standardizing dates for calendar-day comparision.
|
||||
static let dateStandardizer: (Date) -> (Date) = {
|
||||
var calendar = Calendar(identifier: .iso8601)
|
||||
calendar.locale = Locale(identifier: "en_US_POSIX")
|
||||
calendar.timeZone = TimeZone(secondsFromGMT: 0)!
|
||||
return calendar.startOfDay(for:)
|
||||
}()
|
||||
private let dateProvider: @Sendable () -> Date
|
||||
/// Used for standardizing dates for calendar-day comparison.
|
||||
private static let dateStandardizer = DateStandardizer.self
|
||||
|
||||
/// Public initializer.
|
||||
/// - Parameter id: The `id` to associate this controller's heartbeat storage with.
|
||||
public convenience init(id: String) {
|
||||
self.init(id: id, dateProvider: Date.init)
|
||||
self.init(id: id, dateProvider: { Date() })
|
||||
}
|
||||
|
||||
/// Convenience initializer. Mirrors the semantics of the public initializer with the added
|
||||
@@ -42,7 +51,7 @@ public final class HeartbeatController {
|
||||
/// - Parameters:
|
||||
/// - id: The id to associate this controller's heartbeat storage with.
|
||||
/// - dateProvider: A date provider.
|
||||
convenience init(id: String, dateProvider: @escaping () -> Date) {
|
||||
convenience init(id: String, dateProvider: @escaping @Sendable () -> Date) {
|
||||
let storage = HeartbeatStorage.getInstance(id: id)
|
||||
self.init(storage: storage, dateProvider: dateProvider)
|
||||
}
|
||||
@@ -52,9 +61,9 @@ public final class HeartbeatController {
|
||||
/// - storage: A heartbeat storage container.
|
||||
/// - dateProvider: A date provider. Defaults to providing the current date.
|
||||
init(storage: HeartbeatStorageProtocol,
|
||||
dateProvider: @escaping () -> Date = Date.init) {
|
||||
dateProvider: @escaping @Sendable () -> Date = { Date() }) {
|
||||
self.storage = storage
|
||||
self.dateProvider = { Self.dateStandardizer(dateProvider()) }
|
||||
self.dateProvider = { Self.dateStandardizer.standardize(dateProvider()) }
|
||||
}
|
||||
|
||||
/// Asynchronously logs a new heartbeat, if needed.
|
||||
@@ -67,7 +76,7 @@ public final class HeartbeatController {
|
||||
|
||||
storage.readAndWriteAsync { heartbeatsBundle in
|
||||
var heartbeatsBundle = heartbeatsBundle ??
|
||||
HeartbeatsBundle(capacity: self.heartbeatsStorageCapacity)
|
||||
HeartbeatsBundle(capacity: Self.heartbeatsStorageCapacity)
|
||||
|
||||
// Filter for the time periods where the last heartbeat to be logged for
|
||||
// that time period was logged more than one time period (i.e. day) ago.
|
||||
@@ -100,7 +109,7 @@ public final class HeartbeatController {
|
||||
// The new value that's stored will use the old's cache to prevent the
|
||||
// logging of duplicates after flushing.
|
||||
return HeartbeatsBundle(
|
||||
capacity: self.heartbeatsStorageCapacity,
|
||||
capacity: Self.heartbeatsStorageCapacity,
|
||||
cache: oldHeartbeatsBundle.lastAddedHeartbeatDates
|
||||
)
|
||||
}
|
||||
@@ -117,6 +126,34 @@ public final class HeartbeatController {
|
||||
}
|
||||
}
|
||||
|
||||
public func flushAsync(completionHandler: @escaping @Sendable (HeartbeatsPayload) -> Void) {
|
||||
let resetTransform = { @Sendable (heartbeatsBundle: HeartbeatsBundle?) -> HeartbeatsBundle? in
|
||||
guard let oldHeartbeatsBundle = heartbeatsBundle else {
|
||||
return nil // Storage was empty.
|
||||
}
|
||||
// The new value that's stored will use the old's cache to prevent the
|
||||
// logging of duplicates after flushing.
|
||||
return HeartbeatsBundle(
|
||||
capacity: Self.heartbeatsStorageCapacity,
|
||||
cache: oldHeartbeatsBundle.lastAddedHeartbeatDates
|
||||
)
|
||||
}
|
||||
|
||||
// Asynchronously gets and returns the stored heartbeats, resetting storage
|
||||
// using the given transform.
|
||||
storage.getAndSetAsync(using: resetTransform) { result in
|
||||
switch result {
|
||||
case let .success(heartbeatsBundle):
|
||||
// If no heartbeats bundle was stored, return an empty payload.
|
||||
completionHandler(heartbeatsBundle?.makeHeartbeatsPayload() ?? HeartbeatsPayload
|
||||
.emptyPayload)
|
||||
case .failure:
|
||||
// If the operation throws, assume no heartbeat(s) were retrieved or set.
|
||||
completionHandler(HeartbeatsPayload.emptyPayload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronously flushes the heartbeat for today.
|
||||
///
|
||||
/// If no heartbeat was logged today, the returned payload is empty.
|
||||
|
||||
@@ -15,19 +15,22 @@
|
||||
import Foundation
|
||||
|
||||
/// A type that can perform atomic operations using block-based transformations.
|
||||
protocol HeartbeatStorageProtocol {
|
||||
protocol HeartbeatStorageProtocol: Sendable {
|
||||
func readAndWriteSync(using transform: (HeartbeatsBundle?) -> HeartbeatsBundle?)
|
||||
func readAndWriteAsync(using transform: @escaping (HeartbeatsBundle?) -> HeartbeatsBundle?)
|
||||
func readAndWriteAsync(using transform: @escaping @Sendable (HeartbeatsBundle?)
|
||||
-> HeartbeatsBundle?)
|
||||
func getAndSet(using transform: (HeartbeatsBundle?) -> HeartbeatsBundle?) throws
|
||||
-> HeartbeatsBundle?
|
||||
func getAndSetAsync(using transform: @escaping @Sendable (HeartbeatsBundle?) -> HeartbeatsBundle?,
|
||||
completion: @escaping @Sendable (Result<HeartbeatsBundle?, Error>) -> Void)
|
||||
}
|
||||
|
||||
/// Thread-safe storage object designed for transforming heartbeat data that is persisted to disk.
|
||||
final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
final class HeartbeatStorage: Sendable, HeartbeatStorageProtocol {
|
||||
/// The identifier used to differentiate instances.
|
||||
private let id: String
|
||||
/// The underlying storage container to read from and write to.
|
||||
private let storage: Storage
|
||||
private let storage: any Storage
|
||||
/// The encoder used for encoding heartbeat data.
|
||||
private let encoder: JSONEncoder = .init()
|
||||
/// The decoder used for decoding heartbeat data.
|
||||
@@ -37,7 +40,7 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
|
||||
/// Designated initializer.
|
||||
/// - Parameters:
|
||||
/// - id: A string identifer.
|
||||
/// - id: A string identifier.
|
||||
/// - storage: The underlying storage container where heartbeat data is stored.
|
||||
init(id: String,
|
||||
storage: Storage) {
|
||||
@@ -49,7 +52,9 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
// MARK: - Instance Management
|
||||
|
||||
/// Statically allocated cache of `HeartbeatStorage` instances keyed by string IDs.
|
||||
private static var cachedInstances: [String: WeakContainer<HeartbeatStorage>] = [:]
|
||||
private static let cachedInstances: FIRAllocatedUnfairLock<
|
||||
[String: WeakContainer<HeartbeatStorage>]
|
||||
> = FIRAllocatedUnfairLock(initialState: [:])
|
||||
|
||||
/// Gets an existing `HeartbeatStorage` instance with the given `id` if one exists. Otherwise,
|
||||
/// makes a new instance with the given `id`.
|
||||
@@ -57,12 +62,14 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
/// - Parameter id: A string identifier.
|
||||
/// - Returns: A `HeartbeatStorage` instance.
|
||||
static func getInstance(id: String) -> HeartbeatStorage {
|
||||
if let cachedInstance = cachedInstances[id]?.object {
|
||||
return cachedInstance
|
||||
} else {
|
||||
let newInstance = HeartbeatStorage.makeHeartbeatStorage(id: id)
|
||||
cachedInstances[id] = WeakContainer(object: newInstance)
|
||||
return newInstance
|
||||
cachedInstances.withLock { cachedInstances in
|
||||
if let cachedInstance = cachedInstances[id]?.object {
|
||||
return cachedInstance
|
||||
} else {
|
||||
let newInstance = HeartbeatStorage.makeHeartbeatStorage(id: id)
|
||||
cachedInstances[id] = WeakContainer(object: newInstance)
|
||||
return newInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +93,9 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
|
||||
deinit {
|
||||
// Removes the instance if it was cached.
|
||||
Self.cachedInstances.removeValue(forKey: id)
|
||||
Self.cachedInstances.withLock { value in
|
||||
value.removeValue(forKey: id)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HeartbeatStorageProtocol
|
||||
@@ -105,7 +114,8 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
/// Asynchronously reads from and writes to storage using the given transform block.
|
||||
/// - Parameter transform: A block to transform the currently stored heartbeats bundle to a new
|
||||
/// heartbeats bundle value.
|
||||
func readAndWriteAsync(using transform: @escaping (HeartbeatsBundle?) -> HeartbeatsBundle?) {
|
||||
func readAndWriteAsync(using transform: @escaping @Sendable (HeartbeatsBundle?)
|
||||
-> HeartbeatsBundle?) {
|
||||
queue.async { [self] in
|
||||
let oldHeartbeatsBundle = try? load(from: storage)
|
||||
let newHeartbeatsBundle = transform(oldHeartbeatsBundle)
|
||||
@@ -134,6 +144,27 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
return heartbeatsBundle
|
||||
}
|
||||
|
||||
/// Asynchronously gets the current heartbeat data from storage and resets the storage using the
|
||||
/// given transform block.
|
||||
/// - Parameters:
|
||||
/// - transform: An escaping block used to reset the currently stored heartbeat.
|
||||
/// - completion: An escaping block used to process the heartbeat data that
|
||||
/// was stored (before the `transform` was applied); otherwise, the error
|
||||
/// that occurred.
|
||||
func getAndSetAsync(using transform: @escaping @Sendable (HeartbeatsBundle?) -> HeartbeatsBundle?,
|
||||
completion: @escaping @Sendable (Result<HeartbeatsBundle?, Error>) -> Void) {
|
||||
queue.async {
|
||||
do {
|
||||
let oldHeartbeatsBundle = try? self.load(from: self.storage)
|
||||
let newHeartbeatsBundle = transform(oldHeartbeatsBundle)
|
||||
try self.save(newHeartbeatsBundle, to: self.storage)
|
||||
completion(.success(oldHeartbeatsBundle))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads and decodes the stored heartbeats bundle from a given storage object.
|
||||
/// - Parameter storage: The storage container to read from.
|
||||
/// - Returns: The decoded `HeartbeatsBundle` loaded from storage; `nil` if storage is empty.
|
||||
@@ -153,7 +184,7 @@ final class HeartbeatStorage: HeartbeatStorageProtocol {
|
||||
/// - heartbeatsBundle: The heartbeats bundle to encode and save.
|
||||
/// - storage: The storage container to write to.
|
||||
private func save(_ heartbeatsBundle: HeartbeatsBundle?, to storage: Storage) throws {
|
||||
if let heartbeatsBundle = heartbeatsBundle {
|
||||
if let heartbeatsBundle {
|
||||
let data = try heartbeatsBundle.encoded(using: encoder)
|
||||
try storage.write(data)
|
||||
} else {
|
||||
|
||||
@@ -45,7 +45,7 @@ struct HeartbeatsBundle: Codable, HeartbeatsPayloadConvertible {
|
||||
|
||||
/// Designated initializer.
|
||||
/// - Parameters:
|
||||
/// - capacity: The heartbeat capacity of the inititialized collection.
|
||||
/// - capacity: The heartbeat capacity of the initialized collection.
|
||||
/// - cache: A cache of time periods mapping to dates. Defaults to using static `cacheProvider`.
|
||||
init(capacity: Int,
|
||||
cache: [TimePeriod: Date] = cacheProvider()) {
|
||||
@@ -72,8 +72,8 @@ struct HeartbeatsBundle: Codable, HeartbeatsPayloadConvertible {
|
||||
}
|
||||
|
||||
// Update cache with the new heartbeat's date.
|
||||
heartbeat.timePeriods.forEach {
|
||||
lastAddedHeartbeatDates[$0] = heartbeat.date
|
||||
for timePeriod in heartbeat.timePeriods {
|
||||
lastAddedHeartbeatDates[timePeriod] = heartbeat.date
|
||||
}
|
||||
|
||||
} catch let error as RingBuffer<Heartbeat>.Error {
|
||||
@@ -98,8 +98,8 @@ struct HeartbeatsBundle: Codable, HeartbeatsPayloadConvertible {
|
||||
|
||||
if case .success = secondPushAttempt {
|
||||
// Update cache with the new heartbeat's date.
|
||||
diagnosticHeartbeat.timePeriods.forEach {
|
||||
lastAddedHeartbeatDates[$0] = diagnosticHeartbeat.date
|
||||
for timePeriod in diagnosticHeartbeat.timePeriods {
|
||||
lastAddedHeartbeatDates[timePeriod] = diagnosticHeartbeat.date
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -124,9 +124,9 @@ struct HeartbeatsBundle: Codable, HeartbeatsPayloadConvertible {
|
||||
poppedHeartbeats.append(poppedHeartbeat)
|
||||
}
|
||||
|
||||
poppedHeartbeats.reversed().forEach {
|
||||
for poppedHeartbeat in poppedHeartbeats.reversed() {
|
||||
do {
|
||||
try buffer.push($0)
|
||||
try buffer.push(poppedHeartbeat)
|
||||
} catch {
|
||||
// Ignore error.
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
import Foundation
|
||||
|
||||
#if SWIFT_PACKAGE
|
||||
import GoogleUtilities_NSData
|
||||
internal import GoogleUtilities_NSData
|
||||
#else
|
||||
import GoogleUtilities
|
||||
internal import GoogleUtilities
|
||||
#endif // SWIFT_PACKAGE
|
||||
|
||||
/// A type that provides a string representation for use in an HTTP header.
|
||||
@@ -44,7 +44,7 @@ public protocol HTTPHeaderRepresentable {
|
||||
/// ]
|
||||
/// }
|
||||
///
|
||||
public struct HeartbeatsPayload: Codable {
|
||||
public struct HeartbeatsPayload: Codable, Sendable {
|
||||
/// The version of the payload. See go/firebase-apple-heartbeats for details regarding current
|
||||
/// version.
|
||||
static let version: Int = 2
|
||||
@@ -93,12 +93,8 @@ extension HeartbeatsPayload: HTTPHeaderRepresentable {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.dateEncodingStrategy = .formatted(Self.dateFormatter)
|
||||
#if DEBUG
|
||||
// TODO: Remove the following #available check when FirebaseCore's minimum deployment target
|
||||
// is iOS 11+; all other supported platforms already meet the minimum for `.sortedKeys`.
|
||||
if #available(iOS 11, *) {
|
||||
// Sort keys in debug builds to simplify output comparisons in unit tests.
|
||||
encoder.outputFormatting = .sortedKeys
|
||||
}
|
||||
// Sort keys in debug builds to simplify output comparisons in unit tests.
|
||||
encoder.outputFormatting = .sortedKeys
|
||||
#endif // DEBUG
|
||||
|
||||
guard let data = try? encoder.encode(self) else {
|
||||
|
||||
@@ -16,13 +16,13 @@ import Foundation
|
||||
|
||||
/// A generic circular queue structure.
|
||||
struct RingBuffer<Element>: Sequence {
|
||||
/// An array of heartbeats treated as a circular queue and intialized with a fixed capacity.
|
||||
/// An array of heartbeats treated as a circular queue and initialized with a fixed capacity.
|
||||
private var circularQueue: [Element?]
|
||||
/// The current "tail" and insert point for the `circularQueue`.
|
||||
private var tailIndex: Array<Element?>.Index
|
||||
|
||||
/// Error types for `RingBuffer` operations.
|
||||
enum Error: LocalizedError {
|
||||
enum Error: Swift.Error {
|
||||
case outOfBoundsPush(pushIndex: Array<Element?>.Index, endIndex: Array<Element?>.Index)
|
||||
|
||||
var errorDescription: String {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import Foundation
|
||||
|
||||
/// A type that reads from and writes to an underlying storage container.
|
||||
protocol Storage {
|
||||
protocol Storage: Sendable {
|
||||
/// Reads and returns the data stored by this storage type.
|
||||
/// - Returns: The data read from storage.
|
||||
/// - Throws: An error if the read failed.
|
||||
@@ -38,16 +38,12 @@ enum StorageError: Error {
|
||||
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) {
|
||||
init(url: URL) {
|
||||
self.url = url
|
||||
self.fileManager = fileManager
|
||||
}
|
||||
|
||||
/// Reads and returns the data from this object's associated file resource.
|
||||
@@ -71,7 +67,7 @@ final class FileStorage: Storage {
|
||||
func write(_ data: Data?) throws {
|
||||
do {
|
||||
try createDirectories(in: url.deletingLastPathComponent())
|
||||
if let data = data {
|
||||
if let data {
|
||||
try data.write(to: url, options: .atomic)
|
||||
} else {
|
||||
let emptyData = Data()
|
||||
@@ -90,7 +86,7 @@ final class FileStorage: Storage {
|
||||
/// - Parameter url: The URL to create directories in.
|
||||
private func createDirectories(in url: URL) throws {
|
||||
do {
|
||||
try fileManager.createDirectory(
|
||||
try FileManager.default.createDirectory(
|
||||
at: url,
|
||||
withIntermediateDirectories: true
|
||||
)
|
||||
@@ -104,17 +100,26 @@ final class FileStorage: Storage {
|
||||
|
||||
/// 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 suite name for the underlying defaults container.
|
||||
private let suiteName: String
|
||||
|
||||
/// The key mapping to the object's associated resource in `defaults`.
|
||||
private let key: String
|
||||
|
||||
/// The underlying defaults container.
|
||||
private var defaults: UserDefaults {
|
||||
// It's safe to force unwrap the below defaults instance because the
|
||||
// initializer only returns `nil` when the bundle id or `globalDomain`
|
||||
// is passed in as the `suiteName`.
|
||||
UserDefaults(suiteName: suiteName)!
|
||||
}
|
||||
|
||||
/// Designated initializer.
|
||||
/// - Parameters:
|
||||
/// - defaults: The defaults container.
|
||||
/// - suiteName: The suite name for the defaults container.
|
||||
/// - key: The key mapping to the value stored in the defaults container.
|
||||
init(defaults: UserDefaults, key: String) {
|
||||
self.defaults = defaults
|
||||
init(suiteName: String, key: String) {
|
||||
self.suiteName = suiteName
|
||||
self.key = key
|
||||
}
|
||||
|
||||
@@ -136,7 +141,7 @@ final class UserDefaultsStorage: Storage {
|
||||
///
|
||||
/// - Parameter data: The `Data?` to write to this object's associated defaults.
|
||||
func write(_ data: Data?) throws {
|
||||
if let data = data {
|
||||
if let data {
|
||||
defaults.set(data, forKey: key)
|
||||
} else {
|
||||
defaults.removeObject(forKey: key)
|
||||
|
||||
@@ -56,11 +56,7 @@ extension FileManager {
|
||||
extension UserDefaultsStorage: StorageFactory {
|
||||
static func makeStorage(id: String) -> Storage {
|
||||
let suiteName = Constants.heartbeatUserDefaultsSuiteName
|
||||
// It's safe to force unwrap the below defaults instance because the
|
||||
// initializer only returns `nil` when the bundle id or `globalDomain`
|
||||
// is passed in as the `suiteName`.
|
||||
let defaults = UserDefaults(suiteName: suiteName)!
|
||||
let key = "heartbeats-\(id)"
|
||||
return UserDefaultsStorage(defaults: defaults, key: key)
|
||||
return UserDefaultsStorage(suiteName: suiteName, key: key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,18 @@ public class _ObjC_HeartbeatController: NSObject {
|
||||
return _ObjC_HeartbeatsPayload(heartbeatsPayload)
|
||||
}
|
||||
|
||||
/// Asynchronously flushes heartbeats from storage into a heartbeats payload.
|
||||
///
|
||||
/// - Note: This API is thread-safe.
|
||||
/// - Returns: A heartbeats payload for the flushed heartbeat(s).
|
||||
public func flushAsync(completionHandler: @escaping @Sendable (_ObjC_HeartbeatsPayload) -> Void) {
|
||||
// TODO: When minimum version moves to iOS 13.0, restore the async version
|
||||
// removed in #13952.
|
||||
heartbeatController.flushAsync { heartbeatsPayload in
|
||||
completionHandler(_ObjC_HeartbeatsPayload(heartbeatsPayload))
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronously flushes the heartbeat for today.
|
||||
///
|
||||
/// If no heartbeat was logged today, the returned payload is empty.
|
||||
|
||||
45
Pods/FirebaseCoreInternal/FirebaseCore/Internal/Sources/Utilities/AtomicBox.swift
generated
Normal file
45
Pods/FirebaseCoreInternal/FirebaseCore/Internal/Sources/Utilities/AtomicBox.swift
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2025 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
|
||||
|
||||
final class AtomicBox<T> {
|
||||
private var _value: T
|
||||
private let lock = NSLock()
|
||||
|
||||
public init(_ value: T) {
|
||||
_value = value
|
||||
}
|
||||
|
||||
public func value() -> T {
|
||||
lock.withLock {
|
||||
_value
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func withLock(_ mutatingBody: (_ value: inout T) -> Void) -> T {
|
||||
lock.withLock {
|
||||
mutatingBody(&_value)
|
||||
return _value
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func withLock<R>(_ mutatingBody: (_ value: inout T) throws -> R) rethrows -> R {
|
||||
try lock.withLock {
|
||||
try mutatingBody(&_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Pods/FirebaseCoreInternal/FirebaseCore/Internal/Sources/Utilities/FIRAllocatedUnfairLock.swift
generated
Normal file
72
Pods/FirebaseCoreInternal/FirebaseCore/Internal/Sources/Utilities/FIRAllocatedUnfairLock.swift
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2025 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 os.lock
|
||||
|
||||
/// A reference wrapper around `os_unfair_lock`. Replace this class with
|
||||
/// `OSAllocatedUnfairLock` once we support only iOS 16+. For an explanation
|
||||
/// on why this is necessary, see the docs:
|
||||
/// https://developer.apple.com/documentation/os/osallocatedunfairlock
|
||||
public final class FIRAllocatedUnfairLock<State>: @unchecked Sendable {
|
||||
private var lockPointer: UnsafeMutablePointer<os_unfair_lock>
|
||||
private var state: State
|
||||
|
||||
public init(initialState: sending State) {
|
||||
lockPointer = UnsafeMutablePointer<os_unfair_lock>
|
||||
.allocate(capacity: 1)
|
||||
lockPointer.initialize(to: os_unfair_lock())
|
||||
state = initialState
|
||||
}
|
||||
|
||||
public convenience init() where State == Void {
|
||||
self.init(initialState: ())
|
||||
}
|
||||
|
||||
public func lock() {
|
||||
os_unfair_lock_lock(lockPointer)
|
||||
}
|
||||
|
||||
public func unlock() {
|
||||
os_unfair_lock_unlock(lockPointer)
|
||||
}
|
||||
|
||||
public func value() -> State {
|
||||
lock()
|
||||
defer { unlock() }
|
||||
return state
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R {
|
||||
let value: R
|
||||
lock()
|
||||
defer { unlock() }
|
||||
value = try body(&state)
|
||||
return value
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func withLock<R>(_ body: () throws -> R) rethrows -> R {
|
||||
let value: R
|
||||
lock()
|
||||
defer { unlock() }
|
||||
value = try body()
|
||||
return value
|
||||
}
|
||||
|
||||
deinit {
|
||||
lockPointer.deallocate()
|
||||
}
|
||||
}
|
||||
11
Pods/FirebaseCoreInternal/README.md
generated
11
Pods/FirebaseCoreInternal/README.md
generated
@@ -86,7 +86,7 @@ For details on using Firebase from a Framework or a library, refer to [firebase_
|
||||
To develop Firebase software in this repository, ensure that you have at least
|
||||
the following software:
|
||||
|
||||
* Xcode 14.1 (or later)
|
||||
* Xcode 16.2 (or later)
|
||||
|
||||
CocoaPods is still the canonical way to develop, but much of the repo now supports
|
||||
development with Swift Package Manager.
|
||||
@@ -137,7 +137,7 @@ Alternatively, disable signing in each target:
|
||||
|
||||
### Adding a New Firebase Pod
|
||||
|
||||
Refer to [AddNewPod](AddNewPod.md) Markdown file for details.
|
||||
Refer to [AddNewPod](docs/AddNewPod.md) Markdown file for details.
|
||||
|
||||
### Managing Headers and Imports
|
||||
|
||||
@@ -153,7 +153,7 @@ 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 clang-format@20
|
||||
brew install mint
|
||||
```
|
||||
|
||||
@@ -235,6 +235,11 @@ at **Project Settings > Cloud Messaging > [Your Firebase App]**.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user