Skip to content

Commit

Permalink
Add protocols for dependencies (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xLeif authored Oct 8, 2024
1 parent 39f88b8 commit 57ea077
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 58 deletions.
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

0 comments on commit 57ea077

Please sign in to comment.