Skip to content

Commit

Permalink
Allow customizing error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ApolloZhu committed Sep 17, 2019
1 parent e9dee30 commit 202a557
Show file tree
Hide file tree
Showing 13 changed files with 86 additions and 61 deletions.
41 changes: 31 additions & 10 deletions Sources/EFStorageKeychainAccess/EFStorage+KeychainAccess.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ extension Keychain: EFUnderlyingStorage {
}
}

public enum AsIsKeychainAccessStorable {
case string(String)
case data(Data)
}

public protocol KeychainAccessStorable {
func asKeychainStorable() -> KeychainAccessStorable!
func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error>
static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self?
}

Expand All @@ -56,19 +61,35 @@ public class EFStorageKeychainAccessRef<Content: KeychainAccessStorable>: EFSing
return
}
switch newValue.asKeychainStorable() {
case let string as String:
try? storage.set(string, key: key)
case let data as Data:
try? storage.set(data, key: key)
default:
assertionFailure("""
\(newValue) of type \(type(of: newValue)) \
is not storable in keychain.
""")
case .success(.string(let string)):
do {
try storage.set(string, key: key)
} catch {
onStorageFailure(error)
}
case .success(.data(let data)):
do {
try storage.set(data, key: key)
} catch {
onStorageFailure(error)
}
case .failure(let error):
onConversionFailure(for: newValue, dueTo: error)
}
}
}

public dynamic func onStorageFailure(_ error: Error) {
assertionFailure("keychain failed to save because \(error.localizedDescription)")
}

public dynamic func onConversionFailure(for content: Content, dueTo error: Error) {
assertionFailure("""
\(content) of type \(type(of: content)) \
is not storable in keychain because \(error.localizedDescription)
""")
}

deinit {
_efStorageLog("CLEAR \(String(describing: self))")
}
Expand Down
11 changes: 4 additions & 7 deletions Sources/EFStorageKeychainAccess/KeychainAccess+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ import KeychainAccess
import Foundation

public extension KeychainAccessStorable where Self: Codable {
func asKeychainStorable() -> KeychainAccessStorable! {
do {
return try JSONEncoder().encode(self).asKeychainStorable()
} catch {
assertionFailure(error.localizedDescription)
return nil
}
func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return Result { try JSONEncoder().encode(self) }
.mapError { print($0);return $0 }
.flatMap { $0.asKeychainStorable() }
}
static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self? {
return Data.fromKeychain(keychain, forKey: key).flatMap {
Expand Down
4 changes: 2 additions & 2 deletions Sources/EFStorageKeychainAccess/KeychainAccess+NSCoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import KeychainAccess
import Foundation

public extension KeychainAccessStorable where Self: NSCoding {
func asKeychainStorable() -> KeychainAccessStorable! {
return NSKeyedArchiver.archivedData(withRootObject: self)
func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return .success(.data(NSKeyedArchiver.archivedData(withRootObject: self)))
}
static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self? {
guard let data = try? keychain.getData(key) else { return nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import KeychainAccess
import Foundation

extension String: KeychainAccessStorable {
public func asKeychainStorable() -> KeychainAccessStorable! {
return self
public func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return .success(.string(self))
}
public static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self? {
return try? keychain.getString(key)
}
}

extension Data: KeychainAccessStorable {
public func asKeychainStorable() -> KeychainAccessStorable! {
return self
public func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return .success(.data(self))
}
public static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self? {
return try? keychain.getData(key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import KeychainAccess

public extension KeychainAccessStorable where Self: RawRepresentable, Self.RawValue: KeychainAccessStorable {
func asKeychainStorable() -> KeychainAccessStorable! {
func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return rawValue.asKeychainStorable()
}
static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Self? {
Expand Down
20 changes: 12 additions & 8 deletions Sources/EFStorageUserDefaults/EFStorage+UserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension UserDefaults: EFUnderlyingStorage {

/// NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary
public protocol UserDefaultsStorable {
func asUserDefaultsStorable() -> UserDefaultsStorable!
func asUserDefaultsStorable() -> AsIsUserDefaultsStorable!
static func fromUserDefaults(_ userDefaults: UserDefaults, forKey key: String) -> Self?
}

Expand All @@ -43,17 +43,21 @@ public class EFStorageUserDefaultsRef<Content: UserDefaultsStorable>: EFSingleIn
guard let newValue = content else {
return storage.removeObject(forKey: key)
}
guard let storable = newValue.asUserDefaultsStorable() else {
assertionFailure("""
\(newValue) of type \(type(of: newValue)) \
is not storable in user defaults.
""")
return storage.removeObject(forKey: key)
if let storable = newValue.asUserDefaultsStorable() {
storage.set(storable, forKey: key)
} else {
onConversionFailure(for: newValue)
}
storage.set(storable, forKey: key)
}
}

public dynamic func onConversionFailure(for content: Content) {
assertionFailure("""
\(content) of type \(type(of: content)) \
is not storable in user defaults.
""")
}

deinit {
_efStorageLog("CLEAR \(String(describing: self))")
}
Expand Down
9 changes: 7 additions & 2 deletions Sources/EFStorageUserDefaults/UserDefaults+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ public extension UserDefaultsStorable where Self: Codable {
do {
return try JSONEncoder().encode(self)
} catch {
assertionFailure(error.localizedDescription)
return nil
return onConversionFailure(dueTo: error)
}
}

dynamic func onConversionFailure(dueTo error: Error) -> UserDefaultsStorable! {
assertionFailure(error.localizedDescription)
return nil
}

static func fromUserDefaults(_ userDefaults: UserDefaults, forKey key: String) -> Self? {
return Data.fromUserDefaults(userDefaults, forKey: key).flatMap {
try? JSONDecoder().decode(Self.self, from: $0)
Expand Down
2 changes: 1 addition & 1 deletion Sources/EFStorageUserDefaults/UserDefaults+NSCoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

public extension UserDefaultsStorable where Self: NSCoding {
func asUserDefaultsStorable() -> UserDefaultsStorable! {
func asUserDefaultsStorable() -> AsIsUserDefaultsStorable! {
return NSKeyedArchiver.archivedData(withRootObject: self)
}
static func fromUserDefaults(_ userDefaults: UserDefaults, forKey key: String) -> Self? {
Expand Down
11 changes: 5 additions & 6 deletions Sources/EFStorageUserDefaults/UserDefaults+Primitives.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

extension URL: UserDefaultsStorable {
public func asUserDefaultsStorable() -> UserDefaultsStorable! {
public func asUserDefaultsStorable() -> AsIsUserDefaultsStorable! {
return absoluteString
}
public static func fromUserDefaults(_ userDefaults: UserDefaults, forKey key: String) -> URL? {
Expand All @@ -17,12 +17,10 @@ extension URL: UserDefaultsStorable {
}

// MARK: - As Is UserDefaults Storable

@usableFromInline
protocol AsIsUserDefaultsStorable: UserDefaultsStorable { }
public protocol AsIsUserDefaultsStorable: UserDefaultsStorable { }

extension AsIsUserDefaultsStorable {
public func asUserDefaultsStorable() -> UserDefaultsStorable! {
public func asUserDefaultsStorable() -> AsIsUserDefaultsStorable! {
return self
}
public static func fromUserDefaults(_ userDefaults: UserDefaults, forKey key: String) -> Self? {
Expand All @@ -43,9 +41,10 @@ extension Date: AsIsUserDefaultsStorable { }
extension Float: AsIsUserDefaultsStorable { }
extension Double: AsIsUserDefaultsStorable { }
#if canImport(CoreGraphics)
// CGFloat is in CoreGraphics on Apple platforms, but in Foundation on others.
import CoreGraphics
extension CGFloat: AsIsUserDefaultsStorable { }
#endif
extension CGFloat: AsIsUserDefaultsStorable { }

extension Int: AsIsUserDefaultsStorable { }
extension Int8: AsIsUserDefaultsStorable { }
Expand Down
22 changes: 13 additions & 9 deletions Sources/EFStorageYYCache/EFStorage+YYCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension YYCache: EFFailableUnderlyingStorage {
}

public protocol YYCacheStorable {
func asYYCacheStorable() -> YYCacheStorable!
func asYYCacheStorable() -> Result<NSCoding, Error>
static func fromYYCache(_ yyCache: YYCache, forKey key: String) -> Self?
}

Expand All @@ -44,18 +44,22 @@ public class EFStorageYYCacheRef<Content: YYCacheStorable>
storage?.removeObject(forKey: key)
return
}
guard let storable = newValue.asYYCacheStorable() as? NSCoding else {
assertionFailure("""
\(newValue) of type \(type(of: newValue)) \
is not storable in YYCache.
""")
storage?.removeObject(forKey: key)
return
switch newValue.asYYCacheStorable() {
case .success(let storable):
storage?.setObject(storable, forKey: key)
case .failure(let error):
onConversionFailure(for: newValue, dueTo: error)
}
storage?.setObject(storable, forKey: key)
}
}

public dynamic func onConversionFailure(for content: Content, dueTo error: Error) {
assertionFailure("""
\(content) of type \(type(of: content)) \
is not storable in YYCache because \(error.localizedDescription)
""")
}

deinit {
_efStorageLog("CLEAR \(String(describing: self))")
}
Expand Down
9 changes: 2 additions & 7 deletions Sources/EFStorageYYCache/YYCache+Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,8 @@ import Foundation
import YYCache

extension YYCacheStorable where Self: Codable {
func asYYCacheStorable() -> YYCacheStorable! {
do {
return try JSONEncoder().encode(self) as NSData
} catch {
assertionFailure(error.localizedDescription)
return nil
}
func asYYCacheStorable() -> Result<NSCoding, Error> {
return Result { try JSONEncoder().encode(self) as NSData }
}
static func fromYYCache(_ yyCache: YYCache, forKey key: String) -> Self? {
return NSData.fromYYCache(yyCache, forKey: key).flatMap {
Expand Down
4 changes: 2 additions & 2 deletions Sources/EFStorageYYCache/YYCache+NSCoding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import Foundation
import YYCache

public extension YYCacheStorable where Self: NSCoding {
func asYYCacheStorable() -> YYCacheStorable! {
return self
func asYYCacheStorable() -> Result<NSCoding, Error> {
return .success(self)
}
static func fromYYCache(_ yyCache: YYCache, forKey key: String) -> Self? {
return yyCache.object(forKey: key) as? Self
Expand Down
4 changes: 2 additions & 2 deletions Tests/EFStorageTests/EFStorageTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import XCTest
import KeychainAccess

extension Bool: KeychainAccessStorable {
public func asKeychainStorable() -> KeychainAccessStorable! {
return "\(self)"
public func asKeychainStorable() -> Result<AsIsKeychainAccessStorable, Error> {
return "\(self)".asKeychainStorable()
}
public static func fromKeychain(_ keychain: Keychain, forKey key: String) -> Bool? {
guard let string = try? keychain.getString(key) else { return nil }
Expand Down

0 comments on commit 202a557

Please sign in to comment.