Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add protocols for dependencies #114

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Sources/AppState/Application/Types/Helper/FileManaging.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// A protocol that provides methods for reading, writing, and deleting files in a type-safe, sendable manner.
public protocol FileManaging: Sendable {

/// Reads a file from the given path and decodes its contents into the specified type.
/// - Parameters:
/// - path: The directory path where the file is located. Defaults to the current directory `"."`.
/// - filename: The name of the file to read.
/// - Returns: The decoded value of the file's content as the specified type.
/// - Throws: An error if the file cannot be found or decoded.
func `in`<Value: Decodable>(path: String, filename: String) throws -> Value

/// Encodes and writes the given value to a file at the specified path.
/// - Parameters:
/// - value: The value to encode and write to the file. It must conform to `Encodable`.
/// - path: The directory path where the file will be written. Defaults to the current directory `"."`.
/// - filename: The name of the file to write.
/// - base64Encoded: Whether to encode the content as Base64. Defaults to `true`.
/// - Throws: An error if the file cannot be written.
func `out`<Value: Encodable>(_ value: Value, path: String, filename: String, base64Encoded: Bool) throws

/// Deletes a file at the specified path.
/// - Parameters:
/// - path: The directory path where the file is located. Defaults to the current directory `"."`.
/// - filename: The name of the file to delete.
/// - Throws: An error if the file cannot be deleted.
func `delete`(path: String, filename: String) throws

/// Removes a file or directory at the specified path.
/// - Parameter path: The full path of the file or directory to remove.
/// - Throws: An error if the item cannot be removed.
func removeItem(atPath path: String) throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#if !os(Linux) && !os(Windows)
import Foundation

/// A protocol that provides a thread-safe interface for interacting with `NSUbiquitousKeyValueStore`,
/// which synchronizes key-value data across the user's iCloud-enabled devices.
public protocol UbiquitousKeyValueStoreManaging: Sendable {

/// Retrieves data stored in iCloud for the specified key.
/// - Parameter key: The key used to retrieve the associated data from the `NSUbiquitousKeyValueStore`.
/// - Returns: The `Data` object associated with the key, or `nil` if no data is found.
func data(forKey key: String) -> Data?

/// Sets a `Data` object for the specified key in iCloud's key-value store.
/// - Parameters:
/// - value: The `Data` object to store. Pass `nil` to remove the data associated with the key.
/// - key: The key with which to associate the data.
func set(_ value: Data?, forKey key: String)

/// Removes the value associated with the specified key from iCloud's key-value store.
/// - Parameter key: The key whose associated value should be removed.
func removeObject(forKey key: String)
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// A protocol that provides a thread-safe interface for interacting with `UserDefaults`,
/// allowing the storage, retrieval, and removal of user preferences and data.
public protocol UserDefaultsManaging: Sendable {

/// Retrieves an object from `UserDefaults` for the given key.
/// - Parameter key: The key used to retrieve the associated value from `UserDefaults`.
/// - Returns: The value stored in `UserDefaults` for the given key, or `nil` if no value is associated with the key.
func object(forKey key: String) -> Any?

/// Removes the value associated with the specified key from `UserDefaults`.
/// - Parameter key: The key whose associated value should be removed.
func removeObject(forKey key: String)

/// Sets the value for the specified key in `UserDefaults`.
/// - Parameters:
/// - value: The value to store in `UserDefaults`. Can be `nil` to remove the value associated with the key.
/// - key: The key with which to associate the value.
func set(_ value: Any?, forKey key: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,14 @@ import Foundation

extension Application {
/// A struct that provides methods for reading, writing, and deleting files in a type-safe, sendable manner.
/// This struct is marked as `Sendable`, allowing its methods to be used in concurrent code safely.
public struct SendableFileManager: Sendable {

/// Reads a file from the given path and decodes its contents into the specified type.
/// - Parameters:
/// - path: The directory path where the file is located. Defaults to the current directory `"."`.
/// - filename: The name of the file to read.
/// - Returns: The decoded value of the file's content as the specified type.
/// - Throws: An error if the file cannot be found or decoded.
public struct SendableFileManager: FileManaging {
public func `in`<Value: Decodable>(
path: String = ".",
filename: String
) throws -> Value {
try FileManager.default.in(path: path, filename: filename)
}

/// Encodes and writes the given value to a file at the specified path.
/// - Parameters:
/// - value: The value to encode and write to the file. It must conform to `Encodable`.
/// - path: The directory path where the file will be written. Defaults to the current directory `"."`.
/// - filename: The name of the file to write.
/// - base64Encoded: Whether to encode the content as Base64. Defaults to `true`.
/// - Throws: An error if the file cannot be written.
public func `out`<Value: Encodable>(
_ value: Value,
path: String = ".",
Expand All @@ -39,33 +24,25 @@ extension Application {
)
}

/// Deletes a file at the specified path.
/// - Parameters:
/// - path: The directory path where the file is located. Defaults to the current directory `"."`.
/// - filename: The name of the file to delete.
/// - Throws: An error if the file cannot be deleted.
public func `delete`(path: String = ".", filename: String) throws {
try FileManager.default.delete(path: path, filename: filename)
}

/// Removes a file or directory at the specified path.
/// - Parameter path: The full path of the file or directory to remove.
/// - Throws: An error if the item cannot be removed.
public func removeItem(atPath path: String) throws {
try FileManager.default.removeItem(atPath: path)
}
}

/// The shared `FileManager` instance.
public var fileManager: Dependency<SendableFileManager> {
public var fileManager: Dependency<FileManaging> {
dependency(SendableFileManager())
}

/// `FileState` encapsulates the value within the application's scope and allows any changes to be propagated throughout the scoped area. State is stored using `FileManager`.
public struct FileState<Value: Codable & Sendable>: MutableApplicationState {
public static var emoji: Character { "🗄️" }

@AppDependency(\.fileManager) private var fileManager: SendableFileManager
@AppDependency(\.fileManager) private var fileManager: FileManaging

/// The initial value of the state.
private var initial: () -> Value
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
import Foundation

extension Application {
/// A struct that provides a thread-safe interface for interacting with `UserDefaults`, allowing the storage,
/// retrieval, and removal of user preferences and data. This struct is marked as `Sendable`, enabling safe
/// use in concurrent environments.
public struct SendableUserDefaults: Sendable {

/// Retrieves an object from `UserDefaults` for the given key.
/// - Parameter key: The key used to retrieve the associated value from `UserDefaults`.
/// - Returns: The value stored in `UserDefaults` for the given key, or `nil` if no value is associated with the key.
/// A struct that provides a thread-safe interface for interacting with `UserDefaults`, allowing the storage, retrieval, and removal of user preferences and data.
public struct SendableUserDefaults: UserDefaultsManaging {
public func object(forKey key: String) -> Any? {
UserDefaults.standard.object(forKey: key)
}

/// Removes the value associated with the specified key from `UserDefaults`.
/// - Parameter key: The key whose associated value should be removed.
public func removeObject(forKey key: String) {
UserDefaults.standard.removeObject(forKey: key)
}

/// Sets the value for the specified key in `UserDefaults`.
/// - Parameters:
/// - value: The value to store in `UserDefaults`. Can be `nil` to remove the value associated with the key.
/// - key: The key with which to associate the value.
public func set(_ value: Any?, forKey key: String) {
UserDefaults.standard.set(value, forKey: key)
}
}

/// The shared `UserDefaults` instance.
public var userDefaults: Dependency<SendableUserDefaults> {
public var userDefaults: Dependency<UserDefaultsManaging> {
dependency(SendableUserDefaults())
}

/// `StoredState` encapsulates the value within the application's scope and allows any changes to be propagated throughout the scoped area. State is stored using `UserDefaults`.
public struct StoredState<Value: Codable & Sendable>: MutableApplicationState {
public static var emoji: Character { "💾" }

@AppDependency(\.userDefaults) private var userDefaults: SendableUserDefaults
@AppDependency(\.userDefaults) private var userDefaults: UserDefaultsManaging

/// The initial value of the state.
private var initial: () -> Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,20 @@ import Foundation
extension Application {
/// A struct that provides a thread-safe interface for interacting with `NSUbiquitousKeyValueStore`,
/// which synchronizes small amounts of key-value data across the user's iCloud-enabled devices.
/// This struct is marked as `Sendable`, allowing for safe usage in concurrent environments.
public struct SendableNSUbiquitousKeyValueStore: Sendable {

/// Retrieves data stored in iCloud for the specified key.
/// - Parameter key: The key used to retrieve the associated data from the `NSUbiquitousKeyValueStore`.
/// - Returns: The `Data` object associated with the key, or `nil` if no data is found.
public struct SendableNSUbiquitousKeyValueStore: UbiquitousKeyValueStoreManaging {
public func data(forKey key: String) -> Data? {
NSUbiquitousKeyValueStore.default.data(forKey: key)
}

/// Sets a `Data` object for the specified key in iCloud's key-value store.
/// - Parameters:
/// - value: The `Data` object to store. Pass `nil` to remove the data associated with the key.
/// - key: The key with which to associate the data.
public func set(_ value: Data?, forKey key: String) {
NSUbiquitousKeyValueStore.default.set(value, forKey: key)
}

/// Removes the value associated with the specified key from iCloud's key-value store.
/// - Parameter key: The key whose associated value should be removed.
public func removeObject(forKey key: String) {
NSUbiquitousKeyValueStore.default.removeObject(forKey: key)
}
}

/// The default `NSUbiquitousKeyValueStore` instance.
public var icloudStore: Dependency<SendableNSUbiquitousKeyValueStore> {
public var icloudStore: Dependency<UbiquitousKeyValueStoreManaging> {
dependency {
NotificationCenter.default.addObserver(
self,
Expand Down Expand Up @@ -63,7 +50,7 @@ extension Application {
public struct SyncState<Value: Codable & Sendable>: MutableApplicationState {
public static var emoji: Character { "☁️" }

@AppDependency(\.icloudStore) private var icloudStore: SendableNSUbiquitousKeyValueStore
@AppDependency(\.icloudStore) private var icloudStore: UbiquitousKeyValueStoreManaging

/// The initial value of the state.
private var initial: () -> Value
Expand Down
Loading