Skip to content
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
15 changes: 12 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ let package = Package(
dependencies: [
.package(
url: "https://github.com/EasyPackages/EasyMock.git",
from: "1.0.0"
branch: "main"
),
.package(path: "../EasyTesting")
.package(
url: "https://github.com/EasyPackages/EasyTesting.git",
branch: "main"
Comment on lines +20 to +24
Copy link

Copilot AI May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinning dependencies to the main branch can introduce instability. Consider using a version-based requirement to ensure reproducible builds.

Copilot uses AI. Check for mistakes.
)
],
targets: [
.target(name: "EasyPrefence"),
Expand Down
29 changes: 29 additions & 0 deletions Sources/EasyPrefence/Preference.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
import Foundation

///
/// `Preference` is a composite type that combines the capabilities of both reading and writing preference values.
///
/// This typealias combines two protocols:
/// - `PreferenceGetter`: Provides methods for retrieving values from a preference store
/// - `PreferenceSetter`: Provides methods for saving values to a preference store
///
/// By combining these protocols, `Preference` represents a complete interface for interacting with
/// preference data, allowing both reading and writing operations through a single type.
///
/// ## Usage Example:
/// ```swift
/// // Create a preference object that conforms to both getter and setter protocols
/// let preferences: Preference = UserDefaultPreferenceAdapter()
///
/// // Store a value
/// preferences.setInt("user.age", value: 25)
///
/// // Retrieve a value
/// if let age = preferences.getInt("user.age") {
/// print("User age is \(age)")
/// }
/// ```
///
/// The actual implementation of these methods depends on the concrete type that conforms to `Preference`.
/// For example, `UserDefaultPreferenceAdapter` implements these methods using UserDefaults.
///
public typealias Preference = (
PreferenceGetter &
PreferenceSetter
Expand Down
77 changes: 77 additions & 0 deletions Sources/EasyPrefence/PreferenceGetter.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,85 @@
import Foundation

///
/// `PreferenceGetter` is a protocol that defines an interface for retrieving various types of values from a preference store.
///
/// This protocol provides a standardized way to access stored preferences regardless of the underlying storage mechanism.
/// Each method follows a consistent pattern of accepting a string key and returning an optional value of the specified type.
/// The optional return type reflects the possibility that the requested key might not exist in the preference store.
///
/// ## Supported Value Types
/// - `Bool`: Boolean values
/// - `Int`: Integer values
/// - `Double`: Double-precision floating point values
/// - `Data`: Binary data
/// - `[String: Any]`: Dictionary values (commonly used for storing complex objects)
///
/// ## Usage Example
/// ```swift
/// let preferences: PreferenceGetter = UserDefaultPreferenceGetterAdapter()
///
/// // Retrieve a boolean preference
/// if let isEnabled = preferences.getBool("feature.enabled") {
/// print("Feature is enabled: \(isEnabled)")
/// } else {
/// print("Feature enabled setting not found")
/// }
///
/// // Retrieve user settings as a dictionary
/// if let settings = preferences.getDict("user.settings") {
/// print("User has \(settings.count) settings configured")
/// }
/// ```
///
/// ## Implementation Notes
/// When implementing this protocol, consider the following:
/// - Methods should return `nil` when a key doesn't exist
/// - Type conversion should be handled safely (return `nil` rather than crashing on type mismatch)
/// - Consider thread safety for concurrent access scenarios
///
public protocol PreferenceGetter {
///
/// Retrieves a Boolean value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Boolean value associated with the specified key, or `nil` if the key
/// doesn't exist or the value is not a Boolean.
///
func getBool(_ key: String) -> Bool?

///
/// Retrieves an integer value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The integer value associated with the specified key, or `nil` if the key
/// doesn't exist or the value is not an integer.
///
func getInt(_ key: String) -> Int?

///
/// Retrieves a double-precision floating-point value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Double value associated with the specified key, or `nil` if the key
/// doesn't exist or the value is not a Double.
///
func getDouble(_ key: String) -> Double?

///
/// Retrieves binary data associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Data value associated with the specified key, or `nil` if the key
/// doesn't exist or the value is not Data.
///
func getData(_ key: String) -> Data?

///
/// Retrieves a dictionary associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The dictionary associated with the specified key, or `nil` if the key
/// doesn't exist or the value is not a dictionary.
///
func getDict(_ key: String) -> [String: Any]?
}
76 changes: 76 additions & 0 deletions Sources/EasyPrefence/PreferenceSetter.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,84 @@
import Foundation

///
/// `PreferenceSetter` is a protocol that defines an interface for storing various types of values in a preference store.
///
/// This protocol provides a standardized way to save preferences regardless of the underlying storage mechanism.
/// Each method follows a consistent pattern of accepting a string key and a value of the specified type.
/// The setter methods handle the serialization and storage of the values in the backing store.
///
/// ## Supported Value Types
/// - `Bool`: Boolean values
/// - `Int`: Integer values
/// - `Double`: Double-precision floating point values
/// - `Data`: Binary data
/// - `[String: Any]`: Dictionary values (commonly used for storing complex objects)
///
/// ## Usage Example
/// ```swift
/// let preferences: PreferenceSetter = UserDefaultPreferenceSetterAdapter()
///
/// // Store a boolean preference
/// preferences.setBool("feature.enabled", value: true)
///
/// // Store user settings as a dictionary
/// let settings: [String: Any] = ["theme": "dark", "fontSize": 14, "notifications": true]
/// preferences.setDict("user.settings", value: settings)
/// ```
///
/// ## Implementation Notes
/// When implementing this protocol, consider the following:
/// - Ensure values are properly persisted to the underlying storage
/// - Handle type serialization appropriately for the storage medium
/// - Consider thread safety for concurrent write operations
/// - Implement proper error handling within the implementation
/// - Consider implementing synchronization behavior if needed (e.g., immediate write to disk)
///
public protocol PreferenceSetter {
///
/// Stores a Boolean value in the preference store for the specified key.
///
/// - Parameters:
/// - key: The key under which to store the value.
/// - value: The Boolean value to store.
///
func setBool(_ key: String, value: Bool)

///
/// Stores an integer value in the preference store for the specified key.
///
/// - Parameters:
/// - key: The key under which to store the value.
/// - value: The integer value to store.
///
func setInt(_ key: String, value: Int)

///
/// Stores a double-precision floating-point value in the preference store for the specified key.
///
/// - Parameters:
/// - key: The key under which to store the value.
/// - value: The Double value to store.
///
func setDouble(_ key: String, value: Double)

///
/// Stores binary data in the preference store for the specified key.
///
/// - Parameters:
/// - key: The key under which to store the value.
/// - value: The Data object to store.
///
func setData(_ key: String, value: Data)

///
/// Stores a dictionary in the preference store for the specified key.
///
/// - Parameters:
/// - key: The key under which to store the value.
/// - value: The dictionary to store.
/// - Note: The dictionary must contain only property list compatible types
/// if using implementations like UserDefaults.
///
func setDict(_ key: String, value: [String: Any])
}
111 changes: 111 additions & 0 deletions Sources/EasyUserDefaultPreference/PreferenceGetterProvider.swift
Original file line number Diff line number Diff line change
@@ -1,29 +1,140 @@
import Foundation

/// `PreferenceGetterProvider` is a protocol that defines an abstraction for retrieving raw values from a preference store.
///
/// This protocol is designed to decouple the retrieval logic from the underlying storage mechanism, enabling flexibility
/// and testability. It serves as the foundation for adapters like `UserDefaultPreferenceGetterAdapter` that implement
/// higher-level interfaces such as `PreferenceGetter`.
///
/// ## Purpose
/// The primary purpose of `PreferenceGetterProvider` is to provide a low-level interface for fetching values
/// from a storage system (e.g., `UserDefaults`) without imposing type constraints. The retrieved values are
/// typically cast to the desired type by higher-level abstractions.
///
/// ## Usage Example
/// ```swift
/// // Example of a custom provider
/// struct CustomPreferenceGetterProvider: PreferenceGetterProvider {
/// private var storage: [String: Any] = [:]
///
/// func value(forKey key: String) -> Any? {
/// return storage[key]
/// }
/// }
///
/// let provider = CustomPreferenceGetterProvider()
/// let value = provider.value(forKey: "example.key")
/// ```
///
/// ## Implementation Notes
/// - The `value(forKey:)` method should return `nil` if the key does not exist in the storage.
/// - The returned value is expected to be cast to the appropriate type by the caller.
/// - Implementations should ensure thread safety if the underlying storage is accessed concurrently.
public protocol PreferenceGetterProvider {
/// Retrieves a raw value associated with the specified key from the preference store.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The raw value associated with the key, or `nil` if the key does not exist.
func value(forKey key: String) -> Any?
}

///
/// `UserDefaultPreferenceGetterAdapter` is a concrete implementation of the `PreferenceGetter` protocol
/// that retrieves values from the `UserDefaults` system.
///
/// This adapter uses a `PreferenceGetterProvider` to abstract the underlying storage mechanism,
/// allowing for flexibility and testability. By default, it uses `UserDefaults.standard` as the provider.
///
/// ## Purpose
/// The adapter provides a seamless way to access stored preferences in `UserDefaults` while adhering to the
/// `PreferenceGetter` protocol. It ensures type safety and handles type casting for various data types.
///
/// ## Supported Value Types
/// - `Bool`: Boolean values
/// - `Int`: Integer values
/// - `Double`: Double-precision floating point values
/// - `Data`: Binary data
/// - `[String: Any]`: Dictionary values
///
/// ## Usage Example
/// ```swift
/// let getter = UserDefaultPreferenceGetterAdapter()
///
/// // Retrieve a boolean value
/// if let isEnabled = getter.getBool("feature.enabled") {
/// print("Feature is enabled: \(isEnabled)")
/// }
///
/// // Retrieve a dictionary value
/// if let settings = getter.getDict("user.settings") {
/// print("User settings: \(settings)")
/// }
/// ```
///
/// ## Implementation Notes
/// - The `provider` is responsible for fetching raw values from the underlying storage.
/// - Type casting is performed to ensure the returned value matches the expected type.
/// - If the key does not exist or the value cannot be cast to the expected type, the methods return `nil`.
///
public struct UserDefaultPreferenceGetterAdapter: PreferenceGetter {
private let provider: PreferenceGetterProvider

///
/// Initializes the adapter with a `PreferenceGetterProvider`.
///
/// - Parameter provider: The provider responsible for fetching raw values. Defaults to `UserDefaults.standard`.
///
public init(provider: PreferenceGetterProvider = UserDefaults.standard) {
self.provider = provider
}

///
/// Retrieves a Boolean value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Boolean value associated with the key, or `nil` if the key does not exist or the value is not a Boolean.
///
public func getBool(_ key: String) -> Bool? {
provider.value(forKey: key) as? Bool
}

///
/// Retrieves an integer value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The integer value associated with the key, or `nil` if the key does not exist or the value is not an integer.
///
public func getInt(_ key: String) -> Int? {
provider.value(forKey: key) as? Int
}

///
/// Retrieves a double-precision floating-point value associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Double value associated with the key, or `nil` if the key does not exist or the value is not a Double.
///
public func getDouble(_ key: String) -> Double? {
provider.value(forKey: key) as? Double
}

///
/// Retrieves binary data associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The Data value associated with the key, or `nil` if the key does not exist or the value is not Data.
///
public func getData(_ key: String) -> Data? {
provider.value(forKey: key) as? Data
}

///
/// Retrieves a dictionary associated with the specified key.
///
/// - Parameter key: The key to look up in the preference store.
/// - Returns: The dictionary associated with the key, or `nil` if the key does not exist or the value is not a dictionary.
///
public func getDict(_ key: String) -> [String : Any]? {
provider.value(forKey: key) as? [String: Any]
}
}
Loading