From 5b5ee9238a18ce8a3d828b48947fbb738475af6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Tue, 27 Feb 2024 15:35:25 +0300 Subject: [PATCH 1/6] Migrate to macro --- Package.resolved | 14 ++ Package.swift | 31 +++- README.md | 110 +++++++++++-- .../Decoder/Decoder.swift | 4 +- .../KeyedDecodingContainerProtocol.swift | 7 +- Sources/UserDefaultsProperty/Error.swift | 4 +- .../UserDefaultsProperty.swift | 96 +----------- .../UserDefaultsPropertyData.swift | 11 ++ .../UserDefaultsProvider.swift | 71 +++++++++ .../UserDefaultsStorage.swift | 48 ++++++ .../CompilerPlugin.swift | 9 ++ .../UserDefaultsMacro.swift | 103 +++++++++++++ .../ArrayTests.swift | 86 +++++++++-- .../CacheTests.swift | 28 ---- .../CodableTests.swift | 144 +++++++++++++----- .../IntKeyDictionaryTests.swift | 21 ++- .../OtherKeyDictionaryTests.swift | 21 ++- .../PrimitiveTests.swift | 84 ++++++++-- .../PrimitiveWithDefaultTests.swift | 132 ++++++++++++++++ .../StringKeyDictionaryTests.swift | 84 ++++++++-- 20 files changed, 866 insertions(+), 242 deletions(-) create mode 100644 Package.resolved create mode 100644 Sources/UserDefaultsProperty/UserDefaultsPropertyData.swift create mode 100644 Sources/UserDefaultsProperty/UserDefaultsProvider.swift create mode 100644 Sources/UserDefaultsProperty/UserDefaultsStorage.swift create mode 100644 Sources/UserDefaultsPropertyMacros/CompilerPlugin.swift create mode 100644 Sources/UserDefaultsPropertyMacros/UserDefaultsMacro.swift delete mode 100644 Tests/UserDefaultsPropertyTests/CacheTests.swift create mode 100644 Tests/UserDefaultsPropertyTests/PrimitiveWithDefaultTests.swift diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..6fd9cd0 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", + "version" : "509.0.2" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 7e01457..eed7058 100644 --- a/Package.swift +++ b/Package.swift @@ -1,24 +1,43 @@ -// swift-tools-version:5.2 -// The swift-tools-version declares the minimum version of Swift required to build this package. +// swift-tools-version:5.9 +import CompilerPluginSupport import PackageDescription let package = Package( name: "UserDefaultsProperty", + platforms: [ + .iOS(.v14), + .macOS(.v10_15), + ], products: [ .library( name: "UserDefaultsProperty", - targets: ["UserDefaultsProperty"]), + targets: ["UserDefaultsProperty"] + ), ], dependencies: [ - + .package(url: "https://github.com/apple/swift-syntax.git", exact: "509.0.2"), ], targets: [ .target( name: "UserDefaultsProperty", - dependencies: []), + dependencies: [ + "UserDefaultsPropertyMacros" + ] + ), .testTarget( name: "UserDefaultsPropertyTests", - dependencies: ["UserDefaultsProperty"]), + dependencies: [ + "UserDefaultsProperty", + "UserDefaultsPropertyMacros", + ] + ), + .macro( + name: "UserDefaultsPropertyMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + ] + ), ] ) diff --git a/README.md b/README.md index cd66e1d..500b617 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,112 @@ # UserDefaultsProperty +## Implementation + ```swift import Foundation import UserDefaultsProperty +// To access this without importing UserDefaultsProperty on every file +typealias UserDefaultsStorage = UserDefaultsProperty.UserDefaultsStorage + class MyUserDefaults: UserDefaultsProvider { - let userDefaults = UserDefaults(suiteName: "custom")! + static let shared: MyUserDefaults = .init() + + let userDefaults = UserDefaults.standard - @UserDefaultsProperty(key: "stringProperty") - var stringProperty: String? + // Use property name as key on UserDefaults + @UserDefaultsProperty + var note: String? - @UserDefaultsProperty(key: "dataProperty") + // Use custom key instead of property name + @UserDefaultsProperty(key: "data_property") var dataProperty: Data? + + // Non-optional property with default value. + // Returns the default value if the key is not present on UserDefaults. + // To remove custom value: MyUserDefaults.shared.userDefaults.removeObject(forKey: "userName") + @UserDefaultsProperty + var userName: String = "User" + + // Optional property with default value. + // Because UserDefault cannot store nil information, + // it is imposible to different intentionaly assigned nil value and nonassigned value. + // For example: + // MyUserDefaults.shared.role = nil + // MyUserDefaults.shared.role == "Normal" //true + @UserDefaultsProperty + var role: String? = "Normal" } ``` +## Usage + ```swift -import Foundation -import UserDefaultsProperty +func someFunction() { -class MyUserDefaults: UserDefaultsProvider, UserDefaultsCacheProvider { - let userDefaults = UserDefaults(suiteName: "custom")! - let cache = UserDefaultsPropertyCache() - - @UserDefaultsProperty(key: "stringProperty") - var stringProperty: String? - - @UserDefaultsProperty(key: "dataProperty") - var dataProperty: Data? + // Without fallback value + + print(MyUserDefaults.shared.note) // nil + print(MyUserDefaults.shared.isSet(.\$note)) // false + + MyUserDefaults.shared.note = "some note" + + print(MyUserDefaults.shared.userName) // Optional("some note") + print(MyUserDefaults.shared.isSet(.\$note)) // true + + MyUserDefaults.shared.note = nil + + print(MyUserDefaults.shared.note) // nil + print(MyUserDefaults.shared.isSet(.\$note)) // false + + + // Non optional with fallback + + print(MyUserDefaults.shared.userName) // "User" + print(MyUserDefaults.shared.isSet(.\$userName)) // false + + MyUserDefaults.shared.userName = "User 2" + + print(MyUserDefaults.shared.userName) // "User 2" + print(MyUserDefaults.shared.isSet(.\$userName)) // true + + MyUserDefaults.shared.reset(.\$userName)// Cannot set to nil use helper funtion + + print(MyUserDefaults.shared.userName) // "User" + print(MyUserDefaults.shared.isSet(.\$userName)) // false + + // Optional with fallback (Not suggested, it does not feels OK) + + print(MyUserDefaults.shared.role) // "Normal" + print(MyUserDefaults.shared.isSet(.\$role)) // false + + MyUserDefaults.shared.role = "Admin" + + print(MyUserDefaults.shared.role) // "Admin" + print(MyUserDefaults.shared.isSet(.\$role)) // true + + MyUserDefaults.shared.role = nil // Setting nil removes the value, it will return default value + + print(MyUserDefaults.shared.role) // "Normal" // It is not nil. Returns defaults value + print(MyUserDefaults.shared.isSet(.\$role)) // false + +} + +var observer: AnyCancellable? // Keep strong reference + +func registerObserver() { + observer = MyUserDefaults.shared.observer(\.$userName) { newUserName in + print(newUserName) + } } + +struct SomeView: View { + @UserDefaultsStorage(AppDefaults.shared, \.$userName) private var userName + + var body: some View { + Text(userName) + TextEditor(text: $userName) + } +} + ``` diff --git a/Sources/UserDefaultsProperty/Decoder/Decoder.swift b/Sources/UserDefaultsProperty/Decoder/Decoder.swift index ba25d0e..7aa7b25 100644 --- a/Sources/UserDefaultsProperty/Decoder/Decoder.swift +++ b/Sources/UserDefaultsProperty/Decoder/Decoder.swift @@ -1,10 +1,10 @@ import Foundation -func cast(_ value: Any) throws -> T { +func cast(_ value: Any?) throws -> T { if let value = value as? T { return value } else { - throw "Cannot decode \(value.self) to \(T.self)" + throw UserDefaultsPropertyError.cast(value: value, type: T.self) } } diff --git a/Sources/UserDefaultsProperty/Decoder/KeyedDecodingContainerProtocol.swift b/Sources/UserDefaultsProperty/Decoder/KeyedDecodingContainerProtocol.swift index 95dbe97..1934408 100644 --- a/Sources/UserDefaultsProperty/Decoder/KeyedDecodingContainerProtocol.swift +++ b/Sources/UserDefaultsProperty/Decoder/KeyedDecodingContainerProtocol.swift @@ -15,11 +15,8 @@ struct _KeyedDecodingContainerProtocol: KeyedDecodingContainerPr } func getData(forKey key: String) throws -> T { - if let data = data.object(forKey: key) as? T { - return data - } else { - throw "Cannot decode \(data.self) to \(T.self)" - } + let value = data.object(forKey: key) + return try cast(value) } func decode(_ type: T.Type, forKey key: Key) throws -> T where T: Decodable { diff --git a/Sources/UserDefaultsProperty/Error.swift b/Sources/UserDefaultsProperty/Error.swift index a590a1e..6025712 100644 --- a/Sources/UserDefaultsProperty/Error.swift +++ b/Sources/UserDefaultsProperty/Error.swift @@ -1,5 +1,5 @@ import Foundation -extension String: LocalizedError { - public var errorDescription: String? { return self } +enum UserDefaultsPropertyError: Error { + case cast(value: Any?, type: Any) } diff --git a/Sources/UserDefaultsProperty/UserDefaultsProperty.swift b/Sources/UserDefaultsProperty/UserDefaultsProperty.swift index 95d0f4e..8ad942c 100644 --- a/Sources/UserDefaultsProperty/UserDefaultsProperty.swift +++ b/Sources/UserDefaultsProperty/UserDefaultsProperty.swift @@ -1,92 +1,4 @@ -import Foundation - -public protocol UserDefaultsProvider { - var userDefaults: UserDefaults { get } -} - -public protocol UserDefaultsCacheProvider { - var cache: UserDefaultsPropertyCache { get } -} - -public class UserDefaultsPropertyCache: NSObject { - private let cache: NSCache - - public init(cache: NSCache = .init()) { - self.cache = cache - } - - class Wrapper { - let value: T? - - init(_ value: T?) { - self.value = value - } - } - - fileprivate func set(value: T?, forKey key: String) { - self.cache.setObject(Wrapper(value), forKey: key as NSString) - } - - fileprivate func get(forKey key: String) -> Wrapper? { - return self.cache.object(forKey: key as NSString) as? Wrapper - } -} - -@propertyWrapper -public struct UserDefaultsProperty { - let key: String - - public init(key: String) { - self.key = key - } - - public static subscript( - _enclosingInstance instance: E, - wrapped wrappedKeyPath: ReferenceWritableKeyPath, - storage storageKeyPath: ReferenceWritableKeyPath - ) -> T? { - get { - let wrapper = instance[keyPath: storageKeyPath] - - if let wrapper: UserDefaultsPropertyCache.Wrapper = (instance as? UserDefaultsCacheProvider)?.cache.get(forKey: wrapper.key) { - return wrapper.value - } - - if let value = instance.userDefaults.value(forKey: wrapper.key) { - do { - let data = try UserDefaultsDecoder.decode(T.self, from: value) - (instance as? UserDefaultsCacheProvider)?.cache.set(value: data, forKey: wrapper.key) - return data - } catch { - print("error \(error)") - } - } - return nil - } - set { - let wrapper = instance[keyPath: storageKeyPath] - guard let newValue = newValue else { - instance.userDefaults.removeObject(forKey: wrapper.key) - (instance as? UserDefaultsCacheProvider)?.cache.set(value: nil as T?, forKey: wrapper.key) - return - } - do { - let value = try UserDefaultsEncoder.encode(newValue) - instance.userDefaults.set(value, forKey: wrapper.key) - (instance as? UserDefaultsCacheProvider)?.cache.set(value: value, forKey: wrapper.key) - } catch { - print("error \(error)") - } - } - } - - @available(*, unavailable, message: "This property wrapper can only be applied to classes") - public var wrappedValue: T? { - get { - fatalError() - } - set { - fatalError() - } - } -} +@attached(accessor) +@attached(peer, names: prefixed(`$`)) +public macro UserDefaultsProperty(key: String? = nil) += #externalMacro(module: "UserDefaultsPropertyMacros", type: "UserDefaultsMacro") diff --git a/Sources/UserDefaultsProperty/UserDefaultsPropertyData.swift b/Sources/UserDefaultsProperty/UserDefaultsPropertyData.swift new file mode 100644 index 0000000..c34e8e0 --- /dev/null +++ b/Sources/UserDefaultsProperty/UserDefaultsPropertyData.swift @@ -0,0 +1,11 @@ +import Foundation + +public struct UserDefaultsPropertyData { + public let key: String + public let fallback: T + + public init(key: String, fallback: T) { + self.key = key + self.fallback = fallback + } +} diff --git a/Sources/UserDefaultsProperty/UserDefaultsProvider.swift b/Sources/UserDefaultsProperty/UserDefaultsProvider.swift new file mode 100644 index 0000000..3998350 --- /dev/null +++ b/Sources/UserDefaultsProperty/UserDefaultsProvider.swift @@ -0,0 +1,71 @@ +import Foundation +import Combine + +public protocol UserDefaultsProvider { + var userDefaults: UserDefaults { get } +} + +private var changedSubjects: [UserDefaults:PassthroughSubject] = [:] + +public extension UserDefaultsProvider { + internal var changedSubject: PassthroughSubject { + get { + if let c = changedSubjects[userDefaults] { + return c + } else { + let new = PassthroughSubject() + changedSubjects[userDefaults] = new + return new + } + } + } + + func observe(_ keyPath: KeyPath>, callback: @escaping (_ value: T) -> Void) -> AnyCancellable { + let data = self[keyPath: keyPath] + return changedSubject.filter({$0 == data.key}).sink { _ in + let value = _uf_get(forKey: data.key, withFallback: data.fallback) + callback(value) + } + } + + func reset(_ keyPath: KeyPath>) { + let data = self[keyPath: keyPath] + userDefaults.removeObject(forKey: data.key) + changedSubject.send(data.key) + } + + func isSet(_ keyPath: KeyPath>) -> Bool { + let data = self[keyPath: keyPath] + return userDefaults.object(forKey: data.key) != nil + } + + func _uf_set(value: T, forKey key: String) { + do { + if value as AnyObject is NSNull { + userDefaults.removeObject(forKey: key) + } else { + let _value = try UserDefaultsEncoder.encode(value) + userDefaults.set(_value, forKey: key) + } + changedSubject.send(key) + } catch { + print("cannot set \(value) to key: \(key)", error) + } + } + + func _uf_get(forKey key: String) -> T? { + guard let object = userDefaults.object(forKey: key) else { + return nil + } + do { + return try UserDefaultsDecoder.decode(T.self, from: object) + } catch { + print("cannot get for key: \(key)", error) + return nil + } + } + + func _uf_get(forKey key: String, withFallback fallback: T) -> T { + return _uf_get(forKey: key) ?? fallback + } +} diff --git a/Sources/UserDefaultsProperty/UserDefaultsStorage.swift b/Sources/UserDefaultsProperty/UserDefaultsStorage.swift new file mode 100644 index 0000000..c6fc703 --- /dev/null +++ b/Sources/UserDefaultsProperty/UserDefaultsStorage.swift @@ -0,0 +1,48 @@ +import Foundation +import SwiftUI +import Combine + +@propertyWrapper +public struct UserDefaultsStorage: DynamicProperty { + public var wrappedValue: T { + get { + let data = userDefaultsProvider[keyPath: keyPath] + return userDefaultsProvider._uf_get(forKey: data.key, withFallback: data.fallback) + } + + nonmutating set { + let data = userDefaultsProvider[keyPath: keyPath] + userDefaultsProvider._uf_set(value: newValue, forKey: data.key) + } + } + + public var projectedValue: Binding { + Binding( + get: { wrappedValue }, + set: { wrappedValue = $0 } + ) + } + + let userDefaultsProvider: U + let keyPath: KeyPath> + + @ObservedObject private var observer: PublisherObservableObject + + public init(_ userDefaultsProvider: U, _ keyPath: KeyPath>) { + self.userDefaultsProvider = userDefaultsProvider + self.keyPath = keyPath + let data = userDefaultsProvider[keyPath: keyPath] + self.observer = .init(publisher: userDefaultsProvider.changedSubject.filter({$0 == data.key}).map({_ in ()}).eraseToAnyPublisher()) + } +} + +final class PublisherObservableObject: ObservableObject { + + var subscriber: AnyCancellable? + + init(publisher: AnyPublisher) { + subscriber = publisher.sink(receiveValue: { [weak self] _ in + self?.objectWillChange.send() + }) + } +} diff --git a/Sources/UserDefaultsPropertyMacros/CompilerPlugin.swift b/Sources/UserDefaultsPropertyMacros/CompilerPlugin.swift new file mode 100644 index 0000000..2ca9f7f --- /dev/null +++ b/Sources/UserDefaultsPropertyMacros/CompilerPlugin.swift @@ -0,0 +1,9 @@ +import SwiftCompilerPlugin +import SwiftSyntaxMacros + +@main +struct _CompilerPlugin: CompilerPlugin { + let providingMacros: [Macro.Type] = [ + UserDefaultsMacro.self, + ] +} diff --git a/Sources/UserDefaultsPropertyMacros/UserDefaultsMacro.swift b/Sources/UserDefaultsPropertyMacros/UserDefaultsMacro.swift new file mode 100644 index 0000000..0340dbd --- /dev/null +++ b/Sources/UserDefaultsPropertyMacros/UserDefaultsMacro.swift @@ -0,0 +1,103 @@ +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct UserDefaultsMacro: AccessorMacro, PeerMacro { + public static func expansion(of node: SwiftSyntax.AttributeSyntax, providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, in context: some SwiftSyntaxMacros.MacroExpansionContext) throws -> [SwiftSyntax.DeclSyntax] { + let name = try declaration.name() + let key = try node.attribute(withKey: "key") ?? "\"\(try declaration.name())\"" + let fallback = try declaration.initializer() ?? "nil" + + if let type = try declaration.type() { + return [ + """ + var $\(raw: name): UserDefaultsPropertyData<\(raw: type)> = + .init( + key: \(raw: key), + fallback: \(raw: fallback) + ) + """ + ] + } else { + return [ + """ + var $\(raw: name): UserDefaultsPropertyData = + .init( + key: \(raw: key), + fallback: \(raw: fallback) + ) + """ + ] + } + } + + public static func expansion( + of node: SwiftSyntax.AttributeSyntax, + providingAccessorsOf declaration: some SwiftSyntax.DeclSyntaxProtocol, + in _: some SwiftSyntaxMacros.MacroExpansionContext + ) throws -> [SwiftSyntax.AccessorDeclSyntax] { + let key = try node.attribute(withKey: "key") ?? "\"\(try declaration.name())\"" + let fallback = try declaration.initializer() ?? "nil" + + return [ + """ + get { + return _uf_get( + forKey: \(raw: key), + withFallback: \(raw: fallback) + ) + } + set { + _uf_set( + value: newValue, + forKey: \(raw: key) + ) + } + """, + ] + } +} + +extension SwiftSyntax.DeclSyntaxProtocol { + func name() throws -> String { + guard let variableDecl = self.as(VariableDeclSyntax.self) else { + throw UserDefaultsMacroMacroError.error("not variableDecl") + } + guard let identifier = variableDecl.bindings.first?.pattern.as(IdentifierPatternSyntax.self) else { + throw UserDefaultsMacroMacroError.error("not identifier") + } + let name = identifier.identifier.text + return name + } + + func type() throws -> String? { + guard let variableDecl = self.as(VariableDeclSyntax.self) else { + throw UserDefaultsMacroMacroError.error("not variableDecl") + } + guard let typeAnnotation = variableDecl.bindings.first?.typeAnnotation?.as(TypeAnnotationSyntax.self) else { + return nil + } + return typeAnnotation.type.description + } + + func initializer() throws -> String? { + guard let variableDecl = self.as(VariableDeclSyntax.self) else { + throw UserDefaultsMacroMacroError.error("not variableDecl") + } + guard let identifier = variableDecl.bindings.first?.initializer?.as(InitializerClauseSyntax.self) else { + return nil + } + return identifier.value.description.trimmingCharacters(in: .whitespaces) + } +} + +extension SwiftSyntax.AttributeSyntax { + func attribute(withKey key: String) -> String? { + let macroKey = arguments?.as(LabeledExprListSyntax.self)?.first(where: { $0.label?.text == key }) + return macroKey?.expression.formatted().description.trimmingCharacters(in: .whitespaces) + } +} + +enum UserDefaultsMacroMacroError: Error { + case error(String) +} diff --git a/Tests/UserDefaultsPropertyTests/ArrayTests.swift b/Tests/UserDefaultsPropertyTests/ArrayTests.swift index db4181d..89b7106 100644 --- a/Tests/UserDefaultsPropertyTests/ArrayTests.swift +++ b/Tests/UserDefaultsPropertyTests/ArrayTests.swift @@ -4,25 +4,40 @@ import XCTest final class ArrayTests: XCTestCase { func testStringArray() { UserDefaults.standard.removePersistentDomain(forName: "testStringArray") + class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testStringArray")! - @UserDefaultsProperty(key: "property") var property: [String]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } + let value: [String] = ["value1", "value2"] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [String], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDateArray() { @@ -30,22 +45,35 @@ final class ArrayTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDateArray")! - @UserDefaultsProperty(key: "property") var property: [Date]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [Date] = [.init(timeIntervalSinceNow: 100), .init(timeIntervalSinceNow: 200)] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [Date], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [Date], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDataArray() { @@ -53,22 +81,35 @@ final class ArrayTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDataArray")! - @UserDefaultsProperty(key: "property") var property: [Data]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [Data] = [.init([0]), .init([1])] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [Data], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [Data], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testIntArray() { @@ -76,21 +117,34 @@ final class ArrayTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testIntArray")! - @UserDefaultsProperty(key: "property") var property: [Int]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [Int] = [50, 70] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [Int], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [Int], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } } diff --git a/Tests/UserDefaultsPropertyTests/CacheTests.swift b/Tests/UserDefaultsPropertyTests/CacheTests.swift deleted file mode 100644 index 53f0241..0000000 --- a/Tests/UserDefaultsPropertyTests/CacheTests.swift +++ /dev/null @@ -1,28 +0,0 @@ -@testable import UserDefaultsProperty -import XCTest - -final class CacheTests: XCTestCase { - func testCacheStringArray() { - UserDefaults.standard.removePersistentDomain(forName: "testCacheStringArray") - class MyDefaults: UserDefaultsProvider, UserDefaultsCacheProvider { - var userDefaults = UserDefaults(suiteName: "testCacheStringArray")! - let cache = UserDefaultsPropertyCache() - - @UserDefaultsProperty(key: "property") - var property: [String]? - } - let value: [String] = ["value1", "value2"] - let myDefaults = MyDefaults() - - XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) - - myDefaults.property = value - XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String], value) - - myDefaults.property = nil - XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) - } -} diff --git a/Tests/UserDefaultsPropertyTests/CodableTests.swift b/Tests/UserDefaultsPropertyTests/CodableTests.swift index 3596eca..c874f08 100644 --- a/Tests/UserDefaultsPropertyTests/CodableTests.swift +++ b/Tests/UserDefaultsPropertyTests/CodableTests.swift @@ -12,26 +12,44 @@ final class CodableTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testStringCodable")! - @UserDefaultsProperty(key: "property") var property: Holder? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } - let value: Holder = .init(single: "value", - array: ["value1", "value2"], - dictionary: ["key1": "value1", "key2": "value2"]) + let value: Holder = .init( + single: "value", + array: ["value1", "value2"], + dictionary: ["key1": "value1", "key2": "value2"] + ) + let valueDictionary: NSDictionary = [ + "single": value.single, + "array": value.array, + "dictionary": value.dictionary, + ] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "single") as! String, value.single) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "array") as! [String], value.array) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "dictionary") as! [String: String], value.dictionary) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? NSDictionary, valueDictionary) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDateCodable() { @@ -44,26 +62,44 @@ final class CodableTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDateCodable")! - @UserDefaultsProperty(key: "property") var property: Holder? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } - let value: Holder = .init(single: .init(timeIntervalSinceNow: 100), - array: [.init(timeIntervalSinceNow: 100), .init(timeIntervalSinceNow: 200)], - dictionary: ["key1": .init(timeIntervalSinceNow: 100), "key2": .init(timeIntervalSinceNow: 200)]) + let value: Holder = .init( + single: .init(timeIntervalSinceNow: 100), + array: [.init(timeIntervalSinceNow: 100), .init(timeIntervalSinceNow: 200)], + dictionary: ["key1": .init(timeIntervalSinceNow: 100), "key2": .init(timeIntervalSinceNow: 200)] + ) + let valueDictionary: NSDictionary = [ + "single": value.single, + "array": value.array, + "dictionary": value.dictionary, + ] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "single") as! Date, value.single) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "array") as! [Date], value.array) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "dictionary") as! [String: Date], value.dictionary) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? NSDictionary, valueDictionary) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDataCodable() { @@ -76,26 +112,44 @@ final class CodableTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDataCodable")! - @UserDefaultsProperty(key: "property") var property: Holder? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } - let value: Holder = .init(single: .init([1]), - array: [.init([2]), .init([4])], - dictionary: ["key1": .init([5]), "key2": .init([6])]) + let value: Holder = .init( + single: .init([1]), + array: [.init([2]), .init([4])], + dictionary: ["key1": .init([5]), "key2": .init([6])] + ) + let valueDictionary: NSDictionary = [ + "single": value.single, + "array": value.array, + "dictionary": value.dictionary, + ] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "single") as! Data, value.single) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "array") as! [Data], value.array) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "dictionary") as! [String: Data], value.dictionary) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? NSDictionary, valueDictionary) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testIntCodable() { @@ -108,25 +162,43 @@ final class CodableTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testIntCodable")! - @UserDefaultsProperty(key: "property") var property: Holder? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } - let value: Holder = .init(single: 1, - array: [1, 2], - dictionary: ["key1": 5, "key2": 10]) + let value: Holder = .init( + single: 1, + array: [1, 2], + dictionary: ["key1": 5, "key2": 10] + ) + let valueDictionary: NSDictionary = [ + "single": value.single, + "array": value.array, + "dictionary": value.dictionary, + ] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "single") as! Int, value.single) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "array") as! [Int], value.array) - XCTAssertEqual((myDefaults.userDefaults.value(forKey: "property") as? NSDictionary)?.value(forKey: "dictionary") as! [String: Int], value.dictionary) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? NSDictionary, valueDictionary) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } } diff --git a/Tests/UserDefaultsPropertyTests/IntKeyDictionaryTests.swift b/Tests/UserDefaultsPropertyTests/IntKeyDictionaryTests.swift index 285e111..5884106 100644 --- a/Tests/UserDefaultsPropertyTests/IntKeyDictionaryTests.swift +++ b/Tests/UserDefaultsPropertyTests/IntKeyDictionaryTests.swift @@ -7,22 +7,35 @@ final class IntKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testIntDictionary")! - @UserDefaultsProperty(key: "property") var property: [Int: String]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [Int: String] = [1: "value1", 2: "value2"] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(self.convertArrayToDictionary(myDefaults.userDefaults.value(forKey: "property") as! NSDictionary), value) + XCTAssertEqual(self.convertArrayToDictionary(myDefaults.userDefaults.object(forKey: "property") as! NSDictionary), value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func convertArrayToDictionary(_ value: NSDictionary) -> [Int: Value] { diff --git a/Tests/UserDefaultsPropertyTests/OtherKeyDictionaryTests.swift b/Tests/UserDefaultsPropertyTests/OtherKeyDictionaryTests.swift index 6327e93..54add4c 100644 --- a/Tests/UserDefaultsPropertyTests/OtherKeyDictionaryTests.swift +++ b/Tests/UserDefaultsPropertyTests/OtherKeyDictionaryTests.swift @@ -7,22 +7,35 @@ final class OtherKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDateDictionary")! - @UserDefaultsProperty(key: "property") var property: [Date: String]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [Date: String] = [.init(timeIntervalSinceNow: 10): "value1", .init(timeIntervalSinceNow: 50): "value2"] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(self.convertArrayToDictionary(myDefaults.userDefaults.value(forKey: "property") as! NSArray), value) + XCTAssertEqual(self.convertArrayToDictionary(myDefaults.userDefaults.object(forKey: "property") as! NSArray), value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func convertArrayToDictionary(_ array: NSArray) -> [Key: Value] { diff --git a/Tests/UserDefaultsPropertyTests/PrimitiveTests.swift b/Tests/UserDefaultsPropertyTests/PrimitiveTests.swift index ca9b05a..2d23dc2 100644 --- a/Tests/UserDefaultsPropertyTests/PrimitiveTests.swift +++ b/Tests/UserDefaultsPropertyTests/PrimitiveTests.swift @@ -7,22 +7,35 @@ final class PrimitiveTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testString")! - @UserDefaultsProperty(key: "property") var property: String? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: String = "value" let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? String, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? String, value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDate() { @@ -30,22 +43,35 @@ final class PrimitiveTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDate")! - @UserDefaultsProperty(key: "property") var property: Date? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: Date = .init(timeIntervalSinceNow: 100) let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? Date, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Date, value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testData() { @@ -53,22 +79,35 @@ final class PrimitiveTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testData")! - @UserDefaultsProperty(key: "property") var property: Data? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: Data = .init([0]) let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? Data, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Data, value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testInt() { @@ -76,21 +115,34 @@ final class PrimitiveTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testInt")! - @UserDefaultsProperty(key: "property") var property: Int? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: Int = 50 let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? Int, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Int, value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } } diff --git a/Tests/UserDefaultsPropertyTests/PrimitiveWithDefaultTests.swift b/Tests/UserDefaultsPropertyTests/PrimitiveWithDefaultTests.swift new file mode 100644 index 0000000..0e44faa --- /dev/null +++ b/Tests/UserDefaultsPropertyTests/PrimitiveWithDefaultTests.swift @@ -0,0 +1,132 @@ +@testable import UserDefaultsProperty +import XCTest + +final class PrimitiveWithDefaultTests: XCTestCase { + func testString() { + UserDefaults.standard.removePersistentDomain(forName: "testStringDefault") + class MyDefaults: UserDefaultsProvider { + var userDefaults = UserDefaults(suiteName: "testStringDefault")! + + var property: String + { + get { + return _uf_get( + forKey: "property", + withFallback: "default" + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } + } + let value: String = "value" + let myDefaults = MyDefaults() + + XCTAssertEqual(myDefaults.property, "default") + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) + + myDefaults.property = value + XCTAssertEqual(myDefaults.property, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? String, value) + } + + func testDate() { + UserDefaults.standard.removePersistentDomain(forName: "testDateDefault") + class MyDefaults: UserDefaultsProvider { + var userDefaults = UserDefaults(suiteName: "testDateDefault")! + + var property: Date + { + get { + return _uf_get( + forKey: "property", + withFallback: .distantFuture + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } + } + let value: Date = .init(timeIntervalSinceNow: 100) + let myDefaults = MyDefaults() + + XCTAssertEqual(myDefaults.property, .distantFuture) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) + + myDefaults.property = value + XCTAssertEqual(myDefaults.property, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Date, value) + } + + func testData() { + UserDefaults.standard.removePersistentDomain(forName: "testDataDefault") + class MyDefaults: UserDefaultsProvider { + var userDefaults = UserDefaults(suiteName: "testDataDefault")! + + var property: Data + { + get { + return _uf_get( + forKey: "property", + withFallback: .init(base64Encoded: "nDs=")! + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } + } + let value: Data = .init([0]) + let myDefaults = MyDefaults() + + XCTAssertEqual(myDefaults.property, .init(base64Encoded: "nDs=")!) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) + + myDefaults.property = value + XCTAssertEqual(myDefaults.property, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Data, value) + } + + func testInt() { + UserDefaults.standard.removePersistentDomain(forName: "testIntDefault") + class MyDefaults: UserDefaultsProvider { + var userDefaults = UserDefaults(suiteName: "testIntDefault")! + + var property: Int + { + get { + return _uf_get( + forKey: "property", + withFallback: 123 + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } + } + let value: Int = 50 + let myDefaults = MyDefaults() + + XCTAssertEqual(myDefaults.property, 123) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) + + myDefaults.property = value + XCTAssertEqual(myDefaults.property, value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? Int, value) + } +} diff --git a/Tests/UserDefaultsPropertyTests/StringKeyDictionaryTests.swift b/Tests/UserDefaultsPropertyTests/StringKeyDictionaryTests.swift index d790e21..f9a4287 100644 --- a/Tests/UserDefaultsPropertyTests/StringKeyDictionaryTests.swift +++ b/Tests/UserDefaultsPropertyTests/StringKeyDictionaryTests.swift @@ -7,22 +7,35 @@ final class StringKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testStringArray")! - @UserDefaultsProperty(key: "property") var property: [String: String]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [String: String] = ["key1": "value1", "key2": "value2"] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String: String], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [String: String], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDateDictionary() { @@ -30,22 +43,35 @@ final class StringKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDateDictionary")! - @UserDefaultsProperty(key: "property") var property: [String: Date]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [String: Date] = ["key1": .init(timeIntervalSinceNow: 100), "key2": .init(timeIntervalSinceNow: 200)] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String: Date], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [String: Date], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testDataDictionary() { @@ -53,22 +79,35 @@ final class StringKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testDataDictionary")! - @UserDefaultsProperty(key: "property") var property: [String: Data]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [String: Data] = ["key1": .init([0]), "key2": .init([1])] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String: Data], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [String: Data], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } func testIntDictionary() { @@ -76,21 +115,34 @@ final class StringKeyDictionaryTests: XCTestCase { class MyDefaults: UserDefaultsProvider { var userDefaults = UserDefaults(suiteName: "testIntArray")! - @UserDefaultsProperty(key: "property") var property: [String: Int]? + { + get { + return _uf_get( + forKey: "property", + withFallback: nil + ) + } + set { + _uf_set( + value: newValue, + forKey: "property" + ) + } + } } let value: [String: Int] = ["key1": 50, "key2": 70] let myDefaults = MyDefaults() XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) myDefaults.property = value XCTAssertEqual(myDefaults.property, value) - XCTAssertEqual(myDefaults.userDefaults.value(forKey: "property") as? [String: Int], value) + XCTAssertEqual(myDefaults.userDefaults.object(forKey: "property") as? [String: Int], value) myDefaults.property = nil XCTAssertEqual(myDefaults.property, nil) - XCTAssertTrue(myDefaults.userDefaults.value(forKey: "property") == nil) + XCTAssertTrue(myDefaults.userDefaults.object(forKey: "property") == nil) } } From 2d73544b0562c2e144ef0c921b062a3a1943cdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Thu, 7 Mar 2024 13:30:22 +0300 Subject: [PATCH 2/6] f --- .github/workflows/swift.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 300935a..03a4308 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -13,6 +13,9 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable - name: Generate xcodeproj run: swift package generate-xcodeproj - name: Run tests From 909abe64e67762da3f59633430db7c3ecda3396d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Thu, 7 Mar 2024 13:32:29 +0300 Subject: [PATCH 3/6] w --- .github/workflows/swift.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 03a4308..bd01e14 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -9,13 +9,13 @@ on: jobs: build: - runs-on: macos-latest + runs-on: macos-14 steps: - uses: actions/checkout@v2 - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: 15.2 - name: Generate xcodeproj run: swift package generate-xcodeproj - name: Run tests From c6bec336c58bbd8ed574f78e73513bfd2a8c476d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Thu, 7 Mar 2024 13:35:02 +0300 Subject: [PATCH 4/6] w --- .github/workflows/swift.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index bd01e14..703948b 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -16,7 +16,5 @@ jobs: - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: 15.2 - - name: Generate xcodeproj - run: swift package generate-xcodeproj - name: Run tests run: xcodebuild test -destination 'name=iPhone 11' -scheme 'UserDefaultsProperty-Package' From bc937f7b0c1297b8e333397aab82920857bed218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Thu, 7 Mar 2024 13:38:34 +0300 Subject: [PATCH 5/6] w --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 703948b..e9c9d30 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -17,4 +17,4 @@ jobs: with: xcode-version: 15.2 - name: Run tests - run: xcodebuild test -destination 'name=iPhone 11' -scheme 'UserDefaultsProperty-Package' + run: xcodebuild test -destination 'name=iPhone 11' -list From 1138886adc27cf56cdd272d419bd6ab3330ee6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Do=C4=9Fan?= Date: Thu, 7 Mar 2024 13:43:30 +0300 Subject: [PATCH 6/6] w --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index e9c9d30..0d3f86d 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -17,4 +17,4 @@ jobs: with: xcode-version: 15.2 - name: Run tests - run: xcodebuild test -destination 'name=iPhone 11' -list + run: xcodebuild test -destination 'name=iPhone 11' -scheme 'UserDefaultsProperty'