Skip to content

Commit

Permalink
Changed default encoding to JSONEncoder (removed BSONCoder). Adjusted…
Browse files Browse the repository at this point in the history
… tests, exposed error model.

Relates to #38
Relates to #43
  • Loading branch information
vexy committed Dec 20, 2023
1 parent 4859e3c commit c318e4e
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 56 deletions.
18 changes: 3 additions & 15 deletions Sources/Fridge/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,7 @@
import Foundation

enum FridgeErrors: Error {
//add case grabFailed(reason: Error) or similar
case grabFailed
case pushFailed
case decodingFailed
}

enum FreezingErrors: Error {
case dataStoringError
case dataReadingError
case unexpected
}

enum FridgeStreamError: Error {
case streamReadError
case streamWriteError
case networkingIssues(reason: String)
case storageIssues(reason: String)
case parsingIssues(reason: String)
}
66 changes: 42 additions & 24 deletions Sources/Fridge/Fridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,40 +120,44 @@ extension Fridge {
Function expect that both provided object conforms to `Encodable`. Failing to provide conformance will result in throwing an error.

- Returns: Foundation based `Decodable` object
- Throws: `FridgeErrors` depending on the condidion of the failure
- Throws: `FridgeErrors` depending on the condidion of the failure or `JSONEncodingError` in case of JSON parsing issues
- Author: Vexy (https://github.com//vexy)
- Since: `Fridge 0.9`
- SeeAlso: [FridgeErrors](Errors.swift)
*/
public static func freeze🧊<E: Encodable>(_ object: E, id: String) throws {
let freezer = Freezer()
do {
try freezer.freeze(object: object, identifier: id)
} catch let err {
print("<Fridge.Freezer> Error occured: \(err.localizedDescription)")
throw FreezingErrors.dataStoringError
}
// initialize the compartment with given ID
let freezingCompartment = FridgeCompartment(key: id)

// try to encode the data
let objectData = try JSONEncoder().encode(object)

// flush the data into the disk
// rethrow errors if any
try freezingCompartment.store(data: objectData)
}

/**
Tries to freeze an array of given object into persistant storage.

Function expect that passed array conforms to `Codable`. Failing to provide conformance will result in throwing an error.
Function expect that passed array conforms to `Encodable`. Failing to provide conformance will result in throwing an error.

- Returns: Foundation based `Decodable` object
- Throws: `FridgeErrors` depending on the condidion of the failure
- Throws: `FridgeErrors` depending on the condidion of the failure or `JSONEncodingError` in case of JSON parsing issues
- Author: Vexy (https://github.com//vexy)
- Since: `Fridge 0.9.3`
- SeeAlso: [FridgeErrors](Errors.swift)
*/
public static func freeze🧊<C: Codable>(_ objects: [C], id: String) throws {
let freezer = Freezer()
do {
try freezer.freeze(objects: objects, identifier: id)
} catch let err {
print("<Fridge.Freezer> Error occured: \(err.localizedDescription)")
throw FreezingErrors.dataStoringError
}
public static func freeze🧊<C: Encodable>(_ objects: [C], id: String) throws {
// initialize the compartment with given ID
let freezingCompartment = FridgeCompartment(key: id)

// try to encode the data
let objectsData = try JSONEncoder().encode(objects)

// flush the data into the disk
// rethrow errors if any
try freezingCompartment.store(data: objectsData)
}

//MARK: --
Expand All @@ -170,9 +174,16 @@ extension Fridge {
- SeeAlso: [FridgeErrors](Errors.swift)
*/
public static func unfreeze🪅🎉<D: Decodable>(_ key: String) throws -> D {
let unfreezer = Freezer()
let unfrozenObject: D = try unfreezer.unfreeze(identifier: key) // propagate any Errors further
return unfrozenObject
// setup compartment
let unfreezingCompartment = FridgeCompartment(key: key)

// try to load raw data
let rawData = try unfreezingCompartment.load()

// try to perform data decoding
let decodedData = try JSONDecoder().decode(D.self, from: rawData)
//
return decodedData
}

/**
Expand All @@ -187,9 +198,16 @@ extension Fridge {
- SeeAlso: [FridgeErrors](Errors.swift)
*/
public static func unfreeze🪅🎉<C: Codable>(_ key: String) throws -> [C] {
let unfreezer = Freezer()
let unfrozenObject: [C] = try unfreezer.unfreeze(identifier: key) // propagate any Errors further
return unfrozenObject
// setup compartment
let unfreezingCompartment = FridgeCompartment(key: key)

// try to load raw data
let rawData = try unfreezingCompartment.load()

// try to perform data decoding
let decodedData = try JSONDecoder().decode([C].self, from: rawData)
//
return decodedData
}
}

Expand Down
38 changes: 29 additions & 9 deletions Sources/Fridge/FridgeCompartment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,52 @@ internal struct FridgeCompartment {
// key used to identify raw content
private let key: String

/// Retuns new `FridgeCompartment` with given `key` identifier
/// Retuns new `FridgeCompartment` with given `key` identifier.
/// Automatically removes (overwrites) any previous instances previously used.
init(key: String) {
self.key = key
}

/// Returns the compartment name formatted by key and internal file type identifier
private var storageName: String {
key + BLOB_EXTENSION
}

/// Returns `URL` based file path of this compartment
var objectPath: URL {
var objectURLPath: URL {
// TODO: Alter between DocumentsDirectory and CacheDirectory later
guard let documentDirectoryURL = _fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
fatalError("<Fridge.Storage> Unable to compute DocumentsDirectory path")
}
return documentDirectoryURL.appendingPathComponent(storageName)
}

/// Returns the compartment name formatted by key and static identifier
private var storageName: String {
key + BLOB_EXTENSION
}

/// Returns `true` if raw data already exists at this compartment, `false` otherwise
var alreadyExist: Bool {
_fileManager.fileExists(atPath: objectPath.path)
_fileManager.fileExists(atPath: objectURLPath.path)
}

/// Removes compartment from persistent storage
func remove() {
try? _fileManager.removeItem(atPath: objectPath.path)
if alreadyExist {
try? _fileManager.removeItem(atPath: objectURLPath.path)
}
}
}

extension FridgeCompartment {
/// Tries to store object raw data to the system storage
func store(data: Data) throws {
do {
try data.write(to: objectURLPath, options: .atomic)
} catch {
// transform any error into generic write error
throw FridgeErrors.storageIssues(reason: "Writing to data file failed.")
}
}

/// Tries to load raw data from the system storage
func load() throws -> Data {
return try Data(contentsOf: objectURLPath)
}
}
16 changes: 8 additions & 8 deletions Sources/Fridge/Grabber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,21 @@ import Foundation
final internal class Grabber {
func grab<D: Decodable>(from url: URL) async throws -> D {
guard let rawData = try? await URLSession.shared.data(from: url).0 else {
throw FridgeErrors.grabFailed
throw FridgeErrors.networkingIssues(reason: "Networking failed")
}
guard let decodedObject = try? JSONDecoder().decode(D.self, from: rawData) else {
throw FridgeErrors.decodingFailed
throw FridgeErrors.parsingIssues(reason: "Unable to parse data.")
}
// return decoded object
return decodedObject
}

func grab<D: Decodable>(using urlRequest: URLRequest) async throws -> D {
guard let rawData = try? await URLSession.shared.data(for: urlRequest).0 else {
throw FridgeErrors.grabFailed
throw FridgeErrors.networkingIssues(reason: "Networking failed")
}
guard let decodedObject = try? JSONDecoder().decode(D.self, from: rawData) else {
throw FridgeErrors.grabFailed
throw FridgeErrors.parsingIssues(reason: "Unable to parse data.")
}
// return decoded object
return decodedObject
Expand Down Expand Up @@ -90,26 +90,26 @@ final internal class Grabber {
extension Grabber {
/// Constructs a JSON based `URLRequest` from given url `String`
private func constructURLRequest(from string: String) throws -> URLRequest {
guard let urlObject = URL(string: string) else { throw FridgeErrors.decodingFailed }
guard let urlObject = URL(string: string) else { throw FridgeErrors.parsingIssues(reason: "Decoding failed.") }
var request = URLRequest(url: urlObject)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Fridge.grab", forHTTPHeaderField: "User-Agent")
request.setValue("Fridge.Grabber", forHTTPHeaderField: "User-Agent")
return request
}

/// Serialize given object and attach it to request body
private func serializeObject<E: Encodable>(_ objectToSerialize: E) throws -> Data {
guard let serializedObject = try? JSONEncoder().encode(objectToSerialize.self) else {
throw FridgeErrors.decodingFailed
throw FridgeErrors.parsingIssues(reason: "Decoding failed.")
}
return serializedObject
}

/// Tries to decode given data into given `Decodable` object
private func deserializeData<D: Decodable>(_ rawData: Data) throws -> D {
guard let decodedObject = try? JSONDecoder().decode(D.self, from: rawData) else {
throw FridgeErrors.decodingFailed
throw FridgeErrors.parsingIssues(reason: "Decoding failed")
}
return decodedObject
}
Expand Down
7 changes: 7 additions & 0 deletions Tests/FridgeTests/FridgeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ final class FridgeTests: XCTestCase {
XCTAssert(retrievedObject.arr_field[1] == 0xB)
XCTAssert(retrievedObject.arr_field[2] == 0xC)
}

func testThrowsOnInvalidIdentifier() {
do {
let _: String = try Fridge.unfreeze🪅🎉("some.non.existing.key")
XCTFail("Should'ev failed")
} catch { }
}

//MARK:-
/*
Expand Down

0 comments on commit c318e4e

Please sign in to comment.