import Foundation import CoreData import Combine import XCTest import Defaults let fixtureURL = URL(string: "https://sindresorhus.com")! let fixtureURL2 = URL(string: "https://example.com")! enum FixtureEnum: String, Codable { case tenMinutes = "10 Minutes" case halfHour = "30 Minutes" case oneHour = "1 Hour" } let fixtureDate = Date() @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) final class ExamplePersistentHistory: NSPersistentHistoryToken { let value: String init(value: String) { self.value = value super.init() } required init?(coder: NSCoder) { self.value = coder.decodeObject(forKey: "value") as! String super.init() } override func encode(with coder: NSCoder) { coder.encode(value, forKey: "value") } override class var supportsSecureCoding: Bool { true } } extension Defaults.Keys { static let key = Key("key", default: false) static let url = Key("url", default: fixtureURL) static let `enum` = Key("enum", default: .oneHour) static let data = Key("data", default: Data([])) static let date = Key("date", default: fixtureDate) // NSSecureCoding @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) static let persistentHistoryValue = ExamplePersistentHistory(value: "ExampleToken") @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) static let persistentHistory = NSSecureCodingKey("persistentHistory", default: persistentHistoryValue) } final class DefaultsTests: XCTestCase { override func setUp() { super.setUp() Defaults.removeAll() } override func tearDown() { super.setUp() Defaults.removeAll() } func testKey() { let key = Defaults.Key("independentKey", default: false) XCTAssertFalse(Defaults[key]) Defaults[key] = true XCTAssertTrue(Defaults[key]) } func testOptionalKey() { let key = Defaults.Key("independentOptionalKey") XCTAssertNil(Defaults[key]) Defaults[key] = true XCTAssertTrue(Defaults[key]!) Defaults[key] = nil XCTAssertNil(Defaults[key]) Defaults[key] = false XCTAssertFalse(Defaults[key]!) } func testKeyRegistersDefault() { let keyName = "registersDefault" XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), false) _ = Defaults.Key(keyName, default: true) XCTAssertEqual(UserDefaults.standard.bool(forKey: keyName), true) // Test that it works with multiple keys with `Defaults`. let keyName2 = "registersDefault2" _ = Defaults.Key(keyName2, default: keyName2) XCTAssertEqual(UserDefaults.standard.string(forKey: keyName2), keyName2) } func testKeyWithUserDefaultSubscript() { let key = Defaults.Key("keyWithUserDeaultSubscript", default: false) XCTAssertFalse(UserDefaults.standard[key]) UserDefaults.standard[key] = true XCTAssertTrue(UserDefaults.standard[key]) } func testKeys() { XCTAssertFalse(Defaults[.key]) Defaults[.key] = true XCTAssertTrue(Defaults[.key]) } @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) func testNSSecureCodingKeys() { XCTAssertEqual(Defaults.Keys.persistentHistoryValue.value, Defaults[.persistentHistory].value) let newPersistentHistory = ExamplePersistentHistory(value: "NewValue") Defaults[.persistentHistory] = newPersistentHistory XCTAssertEqual(newPersistentHistory.value, Defaults[.persistentHistory].value) } func testUrlType() { XCTAssertEqual(Defaults[.url], fixtureURL) let newUrl = URL(string: "https://twitter.com")! Defaults[.url] = newUrl XCTAssertEqual(Defaults[.url], newUrl) } func testEnumType() { XCTAssertEqual(Defaults[.enum], FixtureEnum.oneHour) } func testDataType() { XCTAssertEqual(Defaults[.data], Data([])) let newData = Data([0xFF]) Defaults[.data] = newData XCTAssertEqual(Defaults[.data], newData) } func testDateType() { XCTAssertEqual(Defaults[.date], fixtureDate) let newDate = Date() Defaults[.date] = newDate XCTAssertEqual(Defaults[.date], newDate) } func testRemoveAll() { let key = Defaults.Key("removeAll", default: false) let key2 = Defaults.Key("removeAll2", default: false) Defaults[key] = true Defaults[key2] = true XCTAssertTrue(Defaults[key]) XCTAssertTrue(Defaults[key2]) Defaults.removeAll() XCTAssertFalse(Defaults[key]) XCTAssertFalse(Defaults[key2]) } func testCustomSuite() { let customSuite = UserDefaults(suiteName: "com.sindresorhus.customSuite")! let key = Defaults.Key("customSuite", default: false, suite: customSuite) XCTAssertFalse(customSuite[key]) XCTAssertFalse(Defaults[key]) Defaults[key] = true XCTAssertTrue(customSuite[key]) XCTAssertTrue(Defaults[key]) Defaults.removeAll(suite: customSuite) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveKeyCombine() { let key = Defaults.Key("observeKey", default: false) let expect = expectation(description: "Observation closure being called") let publisher = Defaults .publisher(key, options: []) .map { ($0.oldValue, $0.newValue) } .collect(2) let cancellable = publisher.sink { tuples in for (i, expected) in [(false, true), (true, false)].enumerated() { XCTAssertEqual(expected.0, tuples[i].0) XCTAssertEqual(expected.1, tuples[i].1) } expect.fulfill() } Defaults[key] = true Defaults.reset(key) cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveNSSecureCodingKeyCombine() { let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue")) let expect = expectation(description: "Observation closure being called") let publisher = Defaults .publisher(key, options: []) .map { ($0.oldValue.value, $0.newValue.value) } .collect(3) let expectedValues = [ ("TestValue", "NewTestValue"), ("NewTestValue", "NewTestValue2"), ("NewTestValue2", "TestValue") ] let cancellable = publisher.sink { actualValues in for (expected, actual) in zip(expectedValues, actualValues) { XCTAssertEqual(expected.0, actual.0) XCTAssertEqual(expected.1, actual.1) } expect.fulfill() } Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2") Defaults.reset(key) cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveOptionalKeyCombine() { let key = Defaults.Key("observeOptionalKey") let expect = expectation(description: "Observation closure being called") let publisher = Defaults .publisher(key, options: []) .map { ($0.oldValue, $0.newValue) } .collect(3) let expectedValues: [(Bool?, Bool?)] = [(nil, true), (true, false), (false, nil)] let cancellable = publisher.sink { actualValues in for (expected, actual) in zip(expectedValues, actualValues) { XCTAssertEqual(expected.0, actual.0) XCTAssertEqual(expected.1, actual.1) } expect.fulfill() } Defaults[key] = true Defaults[key] = false Defaults.reset(key) cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveNSSecureCodingOptionalKeyCombine() { let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey") let expect = expectation(description: "Observation closure being called") let publisher = Defaults .publisher(key, options: []) .map { ($0.oldValue?.value, $0.newValue?.value) } .collect(3) let expectedValues: [(String?, String?)] = [ (nil, "NewTestValue"), ("NewTestValue", "NewTestValue2"), ("NewTestValue2", nil) ] let cancellable = publisher.sink { actualValues in for (expected, actual) in zip(expectedValues, actualValues) { XCTAssertEqual(expected.0, actual.0) XCTAssertEqual(expected.1, actual.1) } expect.fulfill() } XCTAssertNil(Defaults[key]) Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2") Defaults.reset(key) cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveAllCombine() { let key = Defaults.Key("observeAllKey", default: false) let expect = expectation(description: "Observation closure being called") let publisher = Defaults.publisherAll().collect(3) let cancellable = publisher.sink { actualValues in XCTAssertEqual(3, actualValues.count) expect.fulfill() } Defaults[key] = true Defaults[key] = false cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveMultipleKeysCombine() { let key1 = Defaults.Key("observeKey1", default: "x") let key2 = Defaults.Key("observeKey2", default: true) let expect = expectation(description: "Observation closure being called") let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) let cancellable = publisher.sink { _ in expect.fulfill() } Defaults[key1] = "y" Defaults[key2] = false cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveMultipleNSSecureKeysCombine() { let key1 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue")) let key2 = Defaults.NSSecureCodingKey("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue")) let expect = expectation(description: "Observation closure being called") let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) let cancellable = publisher.sink { _ in expect.fulfill() } Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1") Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2") cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveMultipleOptionalKeysCombine() { let key1 = Defaults.Key("observeOptionalKey1") let key2 = Defaults.Key("observeOptionalKey2") let expect = expectation(description: "Observation closure being called") let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) let cancellable = publisher.sink { _ in expect.fulfill() } Defaults[key1] = "x" Defaults[key2] = false cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testObserveMultipleNSSecureOptionalKeysCombine() { let key1 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey1") let key2 = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingKey2") let expect = expectation(description: "Observation closure being called") let publisher = Defaults.publisher(keys: key1, key2, options: []).collect(2) let cancellable = publisher.sink { _ in expect.fulfill() } Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1") Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2") cancellable.cancel() waitForExpectations(timeout: 10) } @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) func testReceiveValueBeforeSubscriptionCombine() { let key = Defaults.Key("receiveValueBeforeSubscription", default: "hello") let expect = expectation(description: "Observation closure being called") let publisher = Defaults .publisher(key) .compactMap { $0.newValue } .eraseToAnyPublisher() .collect(2) let cancellable = publisher.sink { values in XCTAssertEqual(["hello", "world"], values) expect.fulfill() } Defaults[key] = "world"; cancellable.cancel() waitForExpectations(timeout: 10) } func testObserveKey() { let key = Defaults.Key("observeKey", default: false) let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertFalse(change.oldValue) XCTAssertTrue(change.newValue) observation.invalidate() expect.fulfill() } Defaults[key] = true waitForExpectations(timeout: 10) } @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) func testObserveNSSecureCodingKey() { let key = Defaults.NSSecureCodingKey("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue")) let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertEqual(change.oldValue.value, "TestValue") XCTAssertEqual(change.newValue.value, "NewTestValue") observation.invalidate() expect.fulfill() } Defaults[key] = ExamplePersistentHistory(value: "NewTestValue") waitForExpectations(timeout: 10) } func testObserveOptionalKey() { let key = Defaults.Key("observeOptionalKey") let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertNil(change.oldValue) XCTAssertTrue(change.newValue!) observation.invalidate() expect.fulfill() } Defaults[key] = true waitForExpectations(timeout: 10) } @available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) func testObserveNSSecureCodingOptionalKey() { let key = Defaults.NSSecureCodingOptionalKey("observeNSSecureCodingOptionalKey") let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertNil(change.oldValue) XCTAssertEqual(change.newValue?.value, "NewOptionalValue") observation.invalidate() expect.fulfill() } Defaults[key] = ExamplePersistentHistory(value: "NewOptionalValue") waitForExpectations(timeout: 10) } func testObserveKeyURL() { let fixtureURL = URL(string: "https://sindresorhus.com")! let fixtureURL2 = URL(string: "https://example.com")! let key = Defaults.Key("observeKeyURL", default: fixtureURL) let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertEqual(change.oldValue, fixtureURL) XCTAssertEqual(change.newValue, fixtureURL2) observation.invalidate() expect.fulfill() } Defaults[key] = fixtureURL2 waitForExpectations(timeout: 10) } func testObserveKeyEnum() { let key = Defaults.Key("observeKeyEnum", default: .oneHour) let expect = expectation(description: "Observation closure being called") var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in XCTAssertEqual(change.oldValue, .oneHour) XCTAssertEqual(change.newValue, .tenMinutes) observation.invalidate() expect.fulfill() } Defaults[key] = .tenMinutes waitForExpectations(timeout: 10) } func testResetKey() { let defaultFixture1 = "foo1" let defaultFixture2 = 0 let defaultFixture3 = "foo3" let newFixture1 = "bar1" let newFixture2 = 1 let newFixture3 = "bar3" let key1 = Defaults.Key("key1", default: defaultFixture1) let key2 = Defaults.Key("key2", default: defaultFixture2) Defaults[key1] = newFixture1 Defaults[key2] = newFixture2 Defaults.reset(key1) XCTAssertEqual(Defaults[key1], defaultFixture1) XCTAssertEqual(Defaults[key2], newFixture2) if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) { let key3 = Defaults.NSSecureCodingKey("key3", default: ExamplePersistentHistory(value: defaultFixture3)) Defaults[key3] = ExamplePersistentHistory(value: newFixture3) Defaults.reset(key3) XCTAssertEqual(Defaults[key3].value, defaultFixture3) } } func testResetMultipleKeys() { let defaultFxiture1 = "foo1" let defaultFixture2 = 0 let defaultFixture3 = "foo3" let newFixture1 = "bar1" let newFixture2 = 1 let newFixture3 = "bar3" let key1 = Defaults.Key("akey1", default: defaultFxiture1) let key2 = Defaults.Key("akey2", default: defaultFixture2) let key3 = Defaults.Key("akey3", default: defaultFixture3) Defaults[key1] = newFixture1 Defaults[key2] = newFixture2 Defaults[key3] = newFixture3 Defaults.reset(key1, key2) XCTAssertEqual(Defaults[key1], defaultFxiture1) XCTAssertEqual(Defaults[key2], defaultFixture2) XCTAssertEqual(Defaults[key3], newFixture3) } func testResetOptionalKey() { let newString1 = "bar1" let newString2 = "bar2" let newString3 = "bar3" let key1 = Defaults.Key("optionalKey1") let key2 = Defaults.Key("optionalKey2") Defaults[key1] = newString1 Defaults[key2] = newString2 Defaults.reset(key1) XCTAssertEqual(Defaults[key1], nil) XCTAssertEqual(Defaults[key2], newString2) if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *) { let key3 = Defaults.NSSecureCodingOptionalKey("optionalKey3") Defaults[key3] = ExamplePersistentHistory(value: newString3) Defaults.reset(key3) XCTAssertEqual(Defaults[key3], nil) } } func testResetMultipleOptionalKeys() { let newFixture1 = "bar1" let newFixture2 = 1 let newFixture3 = "bar3" let key1 = Defaults.Key("aoptionalKey1") let key2 = Defaults.Key("aoptionalKey2") let key3 = Defaults.Key("aoptionalKey3") Defaults[key1] = newFixture1 Defaults[key2] = newFixture2 Defaults[key3] = newFixture3 Defaults.reset(key1, key2) XCTAssertEqual(Defaults[key1], nil) XCTAssertEqual(Defaults[key2], nil) XCTAssertEqual(Defaults[key3], newFixture3) } func testObserveWithLifetimeTie() { let key = Defaults.Key("lifetimeTie", default: false) let expect = expectation(description: "Observation closure being called") weak var observation: Defaults.Observation! observation = Defaults.observe(key, options: []) { change in observation.invalidate() expect.fulfill() }.tieToLifetime(of: self) Defaults[key] = true waitForExpectations(timeout: 10) } func testObserveWithLifetimeTieManualBreak() { let key = Defaults.Key("lifetimeTieManualBreak", default: false) weak var observation: Defaults.Observation? = Defaults.observe(key, options: []) { _ in }.tieToLifetime(of: self) observation!.removeLifetimeTie() for i in 1...10 { if observation == nil { break } sleep(1) if i == 10 { XCTFail() } } } }