diff --git a/Package.swift b/Package.swift index 64ae30a..a72ba2d 100644 --- a/Package.swift +++ b/Package.swift @@ -166,6 +166,10 @@ package name: "SabyAppleStorage", dependencies: ["SabyConcurrency", "SabySize"], path: "Source/AppleStorage"), + .target( + name: "SabyApplePreference", + dependencies: ["SabyConcurrency"], + path: "Source/ApplePreference"), .target( name: "SabyAppleLogger", dependencies: [], @@ -182,6 +186,10 @@ package name: "SabyAppleStorageTest", dependencies: ["SabyAppleStorage", "SabyTestWait"], path: "Test/AppleStorage"), + .testTarget( + name: "SabyApplePreferenceTest", + dependencies: ["SabyApplePreference"], + path: "Test/ApplePreference"), .testTarget( name: "SabyAppleLoggerTest", dependencies: ["SabyAppleLogger"], diff --git a/Source/ApplePreference/Implement/FileValuePreference.swift b/Source/ApplePreference/Implement/FileValuePreference.swift new file mode 100644 index 0000000..f428d29 --- /dev/null +++ b/Source/ApplePreference/Implement/FileValuePreference.swift @@ -0,0 +1,136 @@ +// +// FileValuePreference.swift +// SabyApplePreference +// +// Created by WOF on 2023/10/12. +// + +import Foundation +import SabySafe +import SabyConcurrency + +public final class FileValuePreference: ValuePreference { + typealias Context = FileValuePreferenceContext + + let fileLock = Lock() + + let contextLoad: () throws -> Context + let context: Atomic?> + + let encoder = JSONEncoder() + + public init(directoryURL: URL, fileName: String, migration: @escaping () throws -> Void) { + self.contextLoad = { + try Context.load( + directoryURL: directoryURL, + fileName: fileName, + migration: migration + ) + } + self.context = Atomic(try? contextLoad()) + } +} + +extension FileValuePreference { + public func set(_ value: Value) throws -> Void { + try execute { context in + _ = context.value.mutate { _ in + value + } + } + } + + public func clear() throws -> Void { + try execute { context in + _ = context.value.mutate { _ in + nil + } + } + } + + public func get() throws -> Value? { + try execute { context in + context.value.capture { $0 } + } + } + + public func save() throws -> Void { + try execute { context in + let values = context.value.capture { $0 } + let data = try self.encoder.encode(values) + + self.fileLock.lock() + try data.write(to: context.url) + self.fileLock.unlock() + } + } +} + +extension FileValuePreference { + fileprivate func execute( + block: (Context) throws -> Result + ) throws -> Result { + let current = try context.capture { $0 } ?? contextLoad() + context.mutate { _ in current } + + return try block(current) + } +} + +struct FileValuePreferenceContext { + let url: URL + let value: Atomic + + private init(url: URL, value: Atomic) { + self.url = url + self.value = value + } + + static func load( + directoryURL: URL, + fileName: String, + migration: () throws -> Void + ) throws -> FileValuePreferenceContext { + try migration() + + let decoder = JSONDecoder() + let fileManager = FileManager.default + + guard directoryURL.isFileURL else { + throw PreferenceError.directoryURLIsNotFileURL + } + + if !fileManager.fileExists(atPath: directoryURL.path) { + try fileManager.createDirectory( + at: directoryURL, + withIntermediateDirectories: true + ) + } + + let url = directoryURL.appendingPathComponent(fileName) + if !fileManager.fileExists(atPath: url.path) { + return FileValuePreferenceContext( + url: url, + value: Atomic(nil) + ) + } + + guard + let data = try? Data(contentsOf: url), + let value = try? decoder.decode( + Value?.self, + from: data + ) + else { + return FileValuePreferenceContext( + url: url, + value: Atomic(nil) + ) + } + + return FileValuePreferenceContext( + url: url, + value: Atomic(value) + ) + } +} diff --git a/Source/ApplePreference/Preference.swift b/Source/ApplePreference/Preference.swift new file mode 100644 index 0000000..4cedc1b --- /dev/null +++ b/Source/ApplePreference/Preference.swift @@ -0,0 +1,27 @@ +// +// Preference.swift +// SabyApplePreference +// +// Created by WOF on 2023/10/12. +// + +import Foundation + +public protocol Preference { + /// ``init(directoryURL:fileName:migration:)`` + /// execute migration and then create or load preference from + /// `{Directory url}/{File name}` path. + init(directoryURL: URL, fileName: String, migration: @escaping () throws -> Void) +} + +extension Preference { + /// ``init(directoryName:fileName:)`` create or load storage from + /// `{Directory url}/{File name}` path. + public init(directoryURL: URL, fileName: String) { + self.init(directoryURL: directoryURL, fileName: fileName, migration: {}) + } +} + +public enum PreferenceError: Error { + case directoryURLIsNotFileURL +} diff --git a/Source/ApplePreference/ValuePreference.swift b/Source/ApplePreference/ValuePreference.swift new file mode 100644 index 0000000..8f10e1b --- /dev/null +++ b/Source/ApplePreference/ValuePreference.swift @@ -0,0 +1,17 @@ +// +// ValuePreference.swift +// SabyApplePreference +// +// Created by WOF on 2023/10/12. +// + +import Foundation + +public protocol ValuePreference: Preference { + associatedtype Value + + func set(_ value: Value) throws -> Void + func clear() throws -> Void + func get() throws -> Value? + func save() throws -> Void +} diff --git a/Source/AppleStorage/Implement/CoreDataArrayStorage.swift b/Source/AppleStorage/Implement/CoreDataArrayStorage.swift index d4e546f..b52c91b 100644 --- a/Source/AppleStorage/Implement/CoreDataArrayStorage.swift +++ b/Source/AppleStorage/Implement/CoreDataArrayStorage.swift @@ -23,33 +23,16 @@ public final class CoreDataArrayStorage: Array let encoder = JSONEncoder() let decoder = JSONDecoder() - public init(directoryName: String, fileName: String, migrations: [() -> Promise]) { + public init(directoryURL: URL, fileName: String, migration: @escaping () -> Promise) { let schema = SabyCoreDataArrayStorageSchema() self.entity = schema.entity self.contextLoad = { Context.load( - directoryName: directoryName, + directoryURL: directoryURL, fileName: fileName, - migrations: migrations, - model: schema.model - ) - } - self.contextPromise = Atomic(contextLoad()) - } - - public init(baseURL: URL, directoryName: String, fileName: String, migrations: [() -> Promise]) { - let schema = SabyCoreDataArrayStorageSchema() - - self.entity = schema.entity - - self.contextLoad = { - Context.load( - baseURL: baseURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations, + migration: migration, model: schema.model ) } @@ -350,56 +333,18 @@ extension NSManagedObjectContext { extension NSManagedObjectContext { static func load( - directoryName: String, - fileName: String, - migrations: [() -> Promise], - model: NSManagedObjectModel - ) -> Promise { - Promise.async { - guard - let libraryDirectoryURL = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first - else { - throw StorageError.libraryDirectoryNotFound - } - - return load( - baseURL: libraryDirectoryURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations, - model: model - ) - } - } - - static func load( - baseURL: URL, - directoryName: String, + directoryURL: URL, fileName: String, - migrations: [() -> Promise], + migration: @escaping () -> Promise, model: NSManagedObjectModel ) -> Promise { - var promise = Promise.resolved(()) - for migration in migrations { - promise = promise.then { - migration() - } - } - - return promise.then { + migration().then { let fileManager = FileManager.default - guard baseURL.isFileURL else { - throw StorageError.baseURLIsNotFileURL - } - guard fileManager.fileExists(atPath: baseURL.path) else { - throw StorageError.baseURLIsNotExist + guard directoryURL.isFileURL else { + throw StorageError.directoryURLIsNotFileURL } - let directoryURL = baseURL.appendingPathComponent(directoryName) if !fileManager.fileExists(atPath: directoryURL.path) { try fileManager.createDirectory( at: directoryURL, diff --git a/Source/AppleStorage/Implement/FileArrayStorage.swift b/Source/AppleStorage/Implement/FileArrayStorage.swift index 0dac16c..12062fe 100644 --- a/Source/AppleStorage/Implement/FileArrayStorage.swift +++ b/Source/AppleStorage/Implement/FileArrayStorage.swift @@ -20,24 +20,12 @@ public final class FileArrayStorage: ArrayStor let encoder = JSONEncoder() - public init(directoryName: String, fileName: String, migrations: [() -> Promise]) { + public init(directoryURL: URL, fileName: String, migration: @escaping () -> Promise) { self.contextLoad = { Context.load( - directoryName: directoryName, + directoryURL: directoryURL, fileName: fileName, - migrations: migrations - ) - } - self.contextPromise = Atomic(contextLoad()) - } - - public init(baseURL: URL, directoryName: String, fileName: String, migrations: [() -> Promise]) { - self.contextLoad = { - Context.load( - baseURL: baseURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations + migration: migration ) } self.contextPromise = Atomic(contextLoad()) @@ -213,54 +201,18 @@ struct FileArrayStorageContext { } static func load( - directoryName: String, - fileName: String, - migrations: [() -> Promise] - ) -> Promise { - Promise.async { - guard - let libraryDirectoryURL = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first - else { - throw StorageError.libraryDirectoryNotFound - } - - return load( - baseURL: libraryDirectoryURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations - ) - } - } - - static func load( - baseURL: URL, - directoryName: String, + directoryURL: URL, fileName: String, - migrations: [() -> Promise] + migration: @escaping () -> Promise ) -> Promise { - var promise = Promise.resolved(()) - for migration in migrations { - promise = promise.then { - migration() - } - } - - return promise.then { + migration().then { let decoder = JSONDecoder() let fileManager = FileManager.default - guard baseURL.isFileURL else { - throw StorageError.baseURLIsNotFileURL - } - guard fileManager.fileExists(atPath: baseURL.path) else { - throw StorageError.baseURLIsNotExist + guard directoryURL.isFileURL else { + throw StorageError.directoryURLIsNotFileURL } - let directoryURL = baseURL.appendingPathComponent(directoryName) if !fileManager.fileExists(atPath: directoryURL.path) { try fileManager.createDirectory( at: directoryURL, diff --git a/Source/AppleStorage/Implement/FileDictionaryStorage.swift b/Source/AppleStorage/Implement/FileDictionaryStorage.swift index afa1b26..2aa0f91 100644 --- a/Source/AppleStorage/Implement/FileDictionaryStorage.swift +++ b/Source/AppleStorage/Implement/FileDictionaryStorage.swift @@ -21,24 +21,12 @@ public final class FileDictionaryStorage< let encoder = JSONEncoder() - public init(directoryName: String, fileName: String, migrations: [() -> Promise]) { + public init(directoryURL: URL, fileName: String, migration: @escaping () -> Promise) { self.contextLoad = { Context.load( - directoryName: directoryName, + directoryURL: directoryURL, fileName: fileName, - migrations: migrations - ) - } - self.contextPromise = Atomic(contextLoad()) - } - - public init(baseURL: URL, directoryName: String, fileName: String, migrations: [() -> Promise]) { - self.contextLoad = { - Context.load( - baseURL: baseURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations + migration: migration ) } self.contextPromise = Atomic(contextLoad()) @@ -126,54 +114,18 @@ struct FileDictionaryStorageContext { } static func load( - directoryName: String, - fileName: String, - migrations: [() -> Promise] - ) -> Promise { - Promise.async { - guard - let libraryDirectoryURL = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first - else { - throw StorageError.libraryDirectoryNotFound - } - - return load( - baseURL: libraryDirectoryURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations - ) - } - } - - static func load( - baseURL: URL, - directoryName: String, + directoryURL: URL, fileName: String, - migrations: [() -> Promise] + migration: @escaping () -> Promise ) -> Promise { - var promise = Promise.resolved(()) - for migration in migrations { - promise = promise.then { - migration() - } - } - - return promise.then { + migration().then { let decoder = JSONDecoder() let fileManager = FileManager.default - guard baseURL.isFileURL else { - throw StorageError.baseURLIsNotFileURL - } - guard fileManager.fileExists(atPath: baseURL.path) else { - throw StorageError.baseURLIsNotExist + guard directoryURL.isFileURL else { + throw StorageError.directoryURLIsNotFileURL } - let directoryURL = baseURL.appendingPathComponent(directoryName) if !fileManager.fileExists(atPath: directoryURL.path) { try fileManager.createDirectory( at: directoryURL, diff --git a/Source/AppleStorage/Implement/FileValueStorage.swift b/Source/AppleStorage/Implement/FileValueStorage.swift index 5393844..f83a072 100644 --- a/Source/AppleStorage/Implement/FileValueStorage.swift +++ b/Source/AppleStorage/Implement/FileValueStorage.swift @@ -19,24 +19,12 @@ public final class FileValueStorage: ValueStorage { let encoder = JSONEncoder() - public init(directoryName: String, fileName: String, migrations: [() -> Promise]) { + public init(directoryURL: URL, fileName: String, migration: @escaping () -> Promise) { self.contextLoad = { Context.load( - directoryName: directoryName, + directoryURL: directoryURL, fileName: fileName, - migrations: migrations - ) - } - self.contextPromise = Atomic(contextLoad()) - } - - public init(baseURL: URL, directoryName: String, fileName: String, migrations: [() -> Promise]) { - self.contextLoad = { - Context.load( - baseURL: baseURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations + migration: migration ) } self.contextPromise = Atomic(contextLoad()) @@ -110,54 +98,18 @@ struct FileValueStorageContext { } static func load( - directoryName: String, - fileName: String, - migrations: [() -> Promise] - ) -> Promise { - Promise.async { - guard - let libraryDirectoryURL = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first - else { - throw StorageError.libraryDirectoryNotFound - } - - return load( - baseURL: libraryDirectoryURL, - directoryName: directoryName, - fileName: fileName, - migrations: migrations - ) - } - } - - static func load( - baseURL: URL, - directoryName: String, + directoryURL: URL, fileName: String, - migrations: [() -> Promise] + migration: @escaping () -> Promise ) -> Promise { - var promise = Promise.resolved(()) - for migration in migrations { - promise = promise.then { - migration() - } - } - - return promise.then { + migration().then { let decoder = JSONDecoder() let fileManager = FileManager.default - guard baseURL.isFileURL else { - throw StorageError.baseURLIsNotFileURL - } - guard fileManager.fileExists(atPath: baseURL.path) else { - throw StorageError.baseURLIsNotExist + guard directoryURL.isFileURL else { + throw StorageError.directoryURLIsNotFileURL } - let directoryURL = baseURL.appendingPathComponent(directoryName) if !fileManager.fileExists(atPath: directoryURL.path) { try fileManager.createDirectory( at: directoryURL, diff --git a/Source/AppleStorage/Storage.swift b/Source/AppleStorage/Storage.swift index 9e1627c..9d55eff 100644 --- a/Source/AppleStorage/Storage.swift +++ b/Source/AppleStorage/Storage.swift @@ -9,31 +9,20 @@ import Foundation import SabyConcurrency public protocol Storage { - /// ``init(directoryName:fileName:migrations:)`` - /// execute migrations orderly and then create or load storage from - /// `{Library directory}/{Directory name}/{File name}` path. - init(directoryName: String, fileName: String, migrations: [() -> Promise]) - /// ``init(baseURL:directoryName:fileName:migrations:)`` - /// execute migrations orderly and then create or load storage from - /// `{Base url}/{Directory name}/{File name}` path. - init(baseURL: URL, directoryName: String, fileName: String, migrations: [() -> Promise]) + /// ``init(directoryURL:fileName:migration:)`` + /// execute migration and then create or load preference from + /// `{Directory url}/{File name}` path. + init(directoryURL: URL, fileName: String, migration: @escaping () -> Promise) } extension Storage { /// ``init(directoryName:fileName:)`` create or load storage from - /// `{Library directory}/{Directory name}/{File name}` path. - public init(directoryName: String, fileName: String) { - self.init(directoryName: directoryName, fileName: fileName, migrations: []) - } - /// ``init(baseURL:directoryName:fileName:)`` create or load storage from - /// `{Base url}/{Directory name}/{File name}` path. - public init(baseURL: URL, directoryName: String, fileName: String) { - self.init(baseURL: baseURL, directoryName: directoryName, fileName: fileName, migrations: []) + /// `{Directory url}/{File name}` path. + public init(directoryURL: URL, fileName: String) { + self.init(directoryURL: directoryURL, fileName: fileName, migration: { .resolved(()) }) } } public enum StorageError: Error { - case libraryDirectoryNotFound - case baseURLIsNotFileURL - case baseURLIsNotExist + case directoryURLIsNotFileURL } diff --git a/Test/ApplePreference/FileValuePreferenceTest.swift b/Test/ApplePreference/FileValuePreferenceTest.swift new file mode 100644 index 0000000..c07f636 --- /dev/null +++ b/Test/ApplePreference/FileValuePreferenceTest.swift @@ -0,0 +1,91 @@ +// +// FileValuePreferenceTest.swift +// SabyApplePreferenceTest +// +// Created by WOF on 2023/10/19. +// + +import XCTest +import SabyConcurrency +@testable import SabyApplePreference + +private struct DummyItem: Codable, Equatable { + var key: UUID +} + +final class FileValuePreferenceTest: XCTestCase { + fileprivate let testCount = 500 + fileprivate var preference: FileValuePreference! + fileprivate let directoryURL = FileManager.default.temporaryDirectory + fileprivate var fileName: String! + + fileprivate var testObjects: [DummyItem] { + var result: [DummyItem] = [] + for _ in 0 ..< testCount { + result.append(DummyItem(key: UUID())) + } + + return result + } + + override func setUpWithError() throws { + fileName = UUID().uuidString + preference = FileValuePreference( + directoryURL: directoryURL, + fileName: fileName + ) + } + + override func tearDownWithError() throws { + let fileURL = directoryURL.appendingPathComponent(fileName) + + if FileManager.default.fileExists(atPath: fileURL.absoluteString) { + try FileManager.default.removeItem(at: fileURL) + } + } + + func test__set() throws { + try testObjects.forEach { + try preference.set($0) + } + + try preference.save() + + let value = try preference.get() + XCTAssertNotEqual(value, nil) + } + + func test__delete() throws { + let testObjects = testObjects + try testObjects.forEach { + try preference.set($0) + } + + try preference.save() + try preference.clear() + try preference.save() + XCTAssertEqual(try preference.get(), nil) + } + + func test__get() throws { + let testCount = testCount + let testObjects = testObjects + let randomIndex = (0 ..< testCount).randomElement()! + try testObjects.forEach { + try preference.set($0) + } + + try preference.set(testObjects[randomIndex]) + + try preference.save() + XCTAssertEqual(try preference.get(), testObjects[randomIndex]) + } + + func test__save() throws { + let value = DummyItem(key: UUID()) + + try preference.set(value) + try preference.save() + XCTAssertEqual(try preference.get(), value) + } +} diff --git a/Test/AppleStorage/Implement/CoreDataArrayStorageTest.swift b/Test/AppleStorage/Implement/CoreDataArrayStorageTest.swift index a0c2621..9f9374c 100644 --- a/Test/AppleStorage/Implement/CoreDataArrayStorageTest.swift +++ b/Test/AppleStorage/Implement/CoreDataArrayStorageTest.swift @@ -20,7 +20,7 @@ class CoreDataArrayStorageTest: XCTestCase { var encoder: JSONEncoder! override func setUpWithError() throws { - storage = CoreDataArrayStorage(directoryName: "saby", fileName: "\(UUID())") + storage = CoreDataArrayStorage(directoryURL: FileManager.default.temporaryDirectory, fileName: "\(UUID())") encoder = JSONEncoder() } diff --git a/Test/AppleStorage/Implement/FileArrayStorageTest.swift b/Test/AppleStorage/Implement/FileArrayStorageTest.swift index 166a1a4..1c624bd 100644 --- a/Test/AppleStorage/Implement/FileArrayStorageTest.swift +++ b/Test/AppleStorage/Implement/FileArrayStorageTest.swift @@ -15,8 +15,9 @@ fileprivate struct DummyItem: Codable, KeyIdentifiable { final class FileArrayStorageTest: XCTestCase { fileprivate var storage: FileArrayStorage! - private var directoryName: String! - var encoder: JSONEncoder! + fileprivate var encoder: JSONEncoder! + fileprivate let directoryURL = FileManager.default.temporaryDirectory + fileprivate var fileName: String! private struct TestItemGroup { private static let testCount = 100 @@ -36,21 +37,19 @@ final class FileArrayStorageTest: XCTestCase { } override func setUpWithError() throws { - directoryName = "saby.array.storage.\(UUID())" + fileName = UUID().uuidString storage = FileArrayStorage( - directoryName: directoryName, - fileName: String(describing: DummyItem.self) + directoryURL: directoryURL, + fileName: fileName ) encoder = JSONEncoder() } override func tearDownWithError() throws { - let path = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first!.appendingPathComponent(directoryName).path - if FileManager.default.fileExists(atPath: path) { - try FileManager.default.removeItem(atPath: path) + let fileURL = directoryURL.appendingPathComponent(fileName) + + if FileManager.default.fileExists(atPath: fileURL.absoluteString) { + try FileManager.default.removeItem(at: fileURL) } } diff --git a/Test/AppleStorage/Implement/FileDictionaryStorageTest.swift b/Test/AppleStorage/Implement/FileDictionaryStorageTest.swift index 8b651fe..6331651 100644 --- a/Test/AppleStorage/Implement/FileDictionaryStorageTest.swift +++ b/Test/AppleStorage/Implement/FileDictionaryStorageTest.swift @@ -16,7 +16,8 @@ fileprivate struct DummyItem: Codable, Equatable { final class FileDictionaryStorageTest: XCTestCase { fileprivate let testCount = 500 fileprivate var storage: FileDictionaryStorage! - fileprivate var directoryName: String! + fileprivate let directoryURL = FileManager.default.temporaryDirectory + fileprivate var fileName: String! fileprivate var testObjects: [(String, DummyItem)] { var result: [(String, DummyItem)] = [] @@ -28,20 +29,18 @@ final class FileDictionaryStorageTest: XCTestCase { } override func setUpWithError() throws { - directoryName = "saby.dictionary.storage.\(UUID())" + fileName = UUID().uuidString storage = FileDictionaryStorage( - directoryName: directoryName, - fileName: String(describing: DummyItem.self) + directoryURL: directoryURL, + fileName: fileName ) } override func tearDownWithError() throws { - let path = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first!.appendingPathComponent(directoryName).path - if FileManager.default.fileExists(atPath: path) { - try FileManager.default.removeItem(atPath: path) + let fileURL = directoryURL.appendingPathComponent(fileName) + + if FileManager.default.fileExists(atPath: fileURL.absoluteString) { + try FileManager.default.removeItem(at: fileURL) } } diff --git a/Test/AppleStorage/Implement/FileValueStorageTest.swift b/Test/AppleStorage/Implement/FileValueStorageTest.swift index 6eb6e3d..26614e5 100644 --- a/Test/AppleStorage/Implement/FileValueStorageTest.swift +++ b/Test/AppleStorage/Implement/FileValueStorageTest.swift @@ -16,7 +16,8 @@ private struct DummyItem: Codable, Equatable { final class FileValueStorageTest: XCTestCase { fileprivate let testCount = 500 fileprivate var storage: FileValueStorage! - fileprivate var directoryName: String! + fileprivate let directoryURL = FileManager.default.temporaryDirectory + fileprivate var fileName: String! fileprivate var testObjects: [DummyItem] { var result: [DummyItem] = [] @@ -28,20 +29,18 @@ final class FileValueStorageTest: XCTestCase { } override func setUpWithError() throws { - directoryName = "saby.dictionary.value.storage.\(UUID())" + fileName = UUID().uuidString storage = FileValueStorage( - directoryName: directoryName, - fileName: String(describing: DummyItem.self) + directoryURL: directoryURL, + fileName: fileName ) } override func tearDownWithError() throws { - let path = FileManager.default.urls( - for: .libraryDirectory, - in: .userDomainMask - ).first!.appendingPathComponent(directoryName).path - if FileManager.default.fileExists(atPath: path) { - try FileManager.default.removeItem(atPath: path) + let fileURL = directoryURL.appendingPathComponent(fileName) + + if FileManager.default.fileExists(atPath: fileURL.absoluteString) { + try FileManager.default.removeItem(at: fileURL) } } diff --git a/Test/Concurrency/Contract/Chain/ContractFilterTest.swift b/Test/Concurrency/Contract/Chain/ContractFilterTest.swift index 83351ec..97ddb23 100644 --- a/Test/Concurrency/Contract/Chain/ContractFilterTest.swift +++ b/Test/Concurrency/Contract/Chain/ContractFilterTest.swift @@ -34,7 +34,7 @@ final class ContractFilterTest: XCTestCase { state: .resolved(URL(string: "https://a.example")!), timeout: .seconds(1) ) { - contract0.resolve("%") + contract0.resolve("") contract0.resolve("https://a.example") } } @@ -49,7 +49,7 @@ final class ContractFilterTest: XCTestCase { state: .resolved(URL(string: "https://a.example")!), timeout: .seconds(1) ) { - contract0.resolve("%") + contract0.resolve("") contract0.resolve("https://a.example") } } @@ -102,7 +102,7 @@ final class ContractFilterTest: XCTestCase { state: .resolved(URL(string: "https://a.example")!), timeout: .seconds(1) ) { - contract0.resolve("%") + contract0.resolve("") contract0.resolve("https://a.example") } } @@ -117,7 +117,7 @@ final class ContractFilterTest: XCTestCase { state: .resolved(URL(string: "https://a.example")!), timeout: .seconds(1) ) { - contract0.resolve("%") + contract0.resolve("") contract0.resolve("https://a.example") } }