-
-
Save khanlou/d5328cf31fe681e027385d75ef335d13 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ObjectStorage.swift | |
// Chet | |
// | |
// Created by Soroush Khanlou on 3/8/19. | |
// Copyright © 2019 Hirsh Group LLC. All rights reserved. | |
// | |
import Foundation | |
protocol ObjectStorageProtocol { | |
associatedtype Stored | |
init(location: StorageLocation) | |
func save(object: Stored) | |
func fetchObject() -> Stored? | |
func deleteObject() | |
} | |
final class CodableObjectStorage<T: Codable>: ObjectStorageProtocol { | |
private let fileManager = FileManager.default | |
let storageLocation: StorageLocation | |
var filePresenter: NSFilePresenter | |
init(location: StorageLocation) { | |
self.storageLocation = location | |
self.filePresenter = InnerFilePresenter(presentedItemURL: storageLocation.storageLocation) | |
} | |
func save(object: T) { | |
guard let storageURL = storageURL else { return } | |
var error: NSError? | |
NSFileCoordinator(filePresenter: filePresenter).coordinate(writingItemAt: storageURL, options: .forReplacing, error: &error, byAccessor: { url in | |
do { | |
try fileManager.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) | |
let encoder = JSONEncoder() | |
let data = try encoder.encode(object) | |
try data.write(to: url) | |
} catch { | |
print("Error writing the object: \(error)") | |
} | |
}) | |
} | |
func fetchObject() -> T? { | |
var error: NSError? | |
var result: T? | |
guard let storageURL = storageURL else { return nil } | |
NSFileCoordinator(filePresenter: filePresenter).coordinate(readingItemAt: storageURL, options: .withoutChanges, error: &error, byAccessor: { url in | |
do { | |
let data = try Data(contentsOf: url) | |
let decoder = JSONDecoder() | |
result = try decoder.decode(T.self, from: data) | |
} catch { | |
if (error as NSError).code != NSFileReadNoSuchFileError { | |
print("Error reading the object: \(error)") | |
} | |
} | |
}) | |
return result | |
} | |
func mutatingObject(default defaultAutoclosure: @autoclosure () -> T, _ block: (inout T) -> Void) { | |
guard let storageURL = storageURL else { return } | |
var error: NSError? | |
NSFileCoordinator(filePresenter: filePresenter).coordinate(writingItemAt: storageURL, options: .forMerging, error: &error, byAccessor: { url in | |
var result: T | |
do { | |
let data = try Data(contentsOf: url) | |
let decoder = JSONDecoder() | |
result = try decoder.decode(T.self, from: data) | |
} catch { | |
if (error as NSError).code == NSFileReadNoSuchFileError { | |
result = defaultAutoclosure() | |
} else { | |
print("Error fetching for mutation: \(error)") | |
return | |
} | |
} | |
block(&result) | |
do { | |
try fileManager.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) | |
let encoder = JSONEncoder() | |
let data = try encoder.encode(result) | |
try data.write(to: url) | |
} catch { | |
print("Error writing the object for mutation: \(error)") | |
} | |
}) | |
} | |
func deleteObject() { | |
var error: NSError? | |
guard let storageURL = storageURL else { return } | |
NSFileCoordinator(filePresenter: filePresenter).coordinate(writingItemAt: storageURL, options: .forDeleting, error: &error, byAccessor: { url in | |
do { | |
try fileManager.removeItem(at: url) | |
} catch { | |
guard (error as NSError).code != NSFileReadNoSuchFileError else { return } | |
print("Error deleting object file.") | |
} | |
}) | |
} | |
private var storageURL: URL? { | |
return storageLocation.storageLocation | |
} | |
class InnerFilePresenter: NSObject, NSFilePresenter { | |
var presentedItemURL: URL? | |
var presentedItemOperationQueue: OperationQueue | |
init(presentedItemURL: URL?) { | |
self.presentedItemURL = presentedItemURL | |
self.presentedItemOperationQueue = OperationQueue() | |
} | |
} | |
} | |
final class DisabledObjectStorage<T>: ObjectStorageProtocol { | |
init(location: StorageLocation) { | |
} | |
func save(object: T) { | |
} | |
func fetchObject() -> T? { | |
return nil | |
} | |
func deleteObject() { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// StorageLocation.swift | |
// | |
// Created by Soroush Khanlou on 9/20/16. | |
// | |
import Foundation | |
enum StorageLocation { | |
case cache(name: String) | |
case userData(name: String) | |
var name: String { | |
switch self { | |
case .cache(let name): | |
return name | |
case .userData(let name): | |
return name | |
} | |
} | |
var searchPathDirectory: FileManager.SearchPathDirectory { | |
switch self { | |
case .cache: | |
return .cachesDirectory | |
case .userData: | |
return .libraryDirectory | |
} | |
} | |
var fileExtension: String { | |
switch self { | |
case .cache: | |
return ".cache" | |
case .userData: | |
return ".userData" | |
} | |
} | |
var fileManager: FileManager { | |
return FileManager.default | |
} | |
var path: String { | |
return storageLocation?.path ?? "" | |
} | |
var storageLocation: URL? { | |
return appStorageDirectory?.appendingPathComponent(filename) | |
} | |
var appStorageDirectory: URL? { | |
return generalStoreDirectory?.appendingPathComponent("urbanarchive", isDirectory: true) | |
} | |
private var generalStoreDirectory: URL? { | |
let URLs = fileManager.urls(for: searchPathDirectory, in: .userDomainMask) | |
return URLs.first | |
} | |
private var filename: String { | |
return sanitizedName + fileExtension | |
} | |
private var sanitizedName: String { | |
let invalidFilenameCharacters = CharacterSet(charactersIn: ":/\\?%*|\"<>") | |
let validName = name.components(separatedBy: invalidFilenameCharacters).joined() | |
return validName.replacingOccurrences(of: " ", with: "-") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment