Merge pull request #163 from TouchInstinct/feature/user_defaults_codable

Feature/user defaults codable
This commit is contained in:
Ivan Smolin 2018-09-18 12:46:04 +03:00 committed by GitHub
commit 03701b4d82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 9 deletions

View File

@ -1,5 +1,8 @@
# Changelog
### 0.8.12
- **Add**: `UserDefaults+Codable` is back. Now with generic subscript support.
### 0.8.11
- **Change**: `NumberFormattingService.computedFormatters` computed var reverted to static.

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "LeadKit"
s.version = "0.8.11"
s.version = "0.8.12"
s.summary = "iOS framework with a bunch of tools for rapid development"
s.homepage = "https://github.com/TouchInstinct/LeadKit"
s.license = "Apache License, Version 2.0"

View File

@ -321,6 +321,13 @@
67274790206CD88600725163 /* DateFormattingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727478E206CD88600725163 /* DateFormattingService.swift */; };
67274791206CD88600725163 /* DateFormattingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727478E206CD88600725163 /* DateFormattingService.swift */; };
67274792206CD88600725163 /* DateFormattingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727478E206CD88600725163 /* DateFormattingService.swift */; };
6732F23F214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */; };
6732F240214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */; };
6732F241214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */; };
6732F242214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */; };
6732F243214C189000B446F2 /* Single+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */; };
6732F244214C189100B446F2 /* Single+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */; };
6732F245214C189100B446F2 /* Single+DeferredJust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F8BB171F5DDED100C1061B /* Single+DeferredJust.swift */; };
673564F12068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673564F02068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift */; };
673564F22068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673564F02068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift */; };
673564F32068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673564F02068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift */; };
@ -856,6 +863,7 @@
6727477E206CD3BD00725163 /* ViewText+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewText+Extensions.swift"; sourceTree = "<group>"; };
67274789206CD83600725163 /* DateFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormat.swift; sourceTree = "<group>"; };
6727478E206CD88600725163 /* DateFormattingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormattingService.swift; sourceTree = "<group>"; };
6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Codable.swift"; sourceTree = "<group>"; };
673564F02068C2AD00F0CBED /* NumberFormattingService+DefaultImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormattingService+DefaultImplementation.swift"; sourceTree = "<group>"; };
673564F52068C68D00F0CBED /* NumberFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormat.swift; sourceTree = "<group>"; };
6737CFA2207220960063E056 /* SeparatorConfiguration+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SeparatorConfiguration+Extensions.swift"; sourceTree = "<group>"; };
@ -1150,6 +1158,7 @@
671461DA1EB3396E00EAB194 /* Extensions */ = {
isa = PBXGroup;
children = (
6732F23C214C09DF00B446F2 /* Foundation */,
671461DB1EB3396E00EAB194 /* Alamofire */,
EFBE57CE1EC35ED90040E00A /* Array */,
67A1FF921EBCA64A00D6C89F /* CABasicAnimation */,
@ -1592,6 +1601,22 @@
path = UIImage;
sourceTree = "<group>";
};
6732F23C214C09DF00B446F2 /* Foundation */ = {
isa = PBXGroup;
children = (
6732F23D214C09E900B446F2 /* UserDefaults */,
);
path = Foundation;
sourceTree = "<group>";
};
6732F23D214C09E900B446F2 /* UserDefaults */ = {
isa = PBXGroup;
children = (
6732F23E214C09F900B446F2 /* UserDefaults+Codable.swift */,
);
path = UserDefaults;
sourceTree = "<group>";
};
673564EF2068C29100F0CBED /* NumberFormattingService */ = {
isa = PBXGroup;
children = (
@ -3109,6 +3134,7 @@
677452A9206263360024EEEF /* CursorType+RxDataSourceDefaultImplementation.swift in Sources */,
677B06B221186C14006C947D /* Completable+DeferredJust.swift in Sources */,
671462501EB3396E00EAB194 /* StaticCursor.swift in Sources */,
6732F23F214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */,
67990AE6213EB4080040D195 /* ConfigurableView+Extensions.swift in Sources */,
6741C40F20EAC88800418D08 /* GeneralDataLoadingViewModel+Extensions.swift in Sources */,
67EB7FC7206148D000BDD9FB /* TotalCountCursorListingResult.swift in Sources */,
@ -3171,6 +3197,7 @@
671463621EB3396E00EAB194 /* SupportProtocol.swift in Sources */,
678D26A220692BFF00B05B93 /* TextFieldViewEvents.swift in Sources */,
671462861EB3396E00EAB194 /* CGContext+Initializers.swift in Sources */,
6732F244214C189100B446F2 /* Single+DeferredJust.swift in Sources */,
6774527B206252020024EEEF /* DataLoadingState.swift in Sources */,
67E3525D2119B5A50035BDDB /* BaseTextAttributes.swift in Sources */,
6714634E1EB3396E00EAB194 /* ReuseIdentifierProtocol.swift in Sources */,
@ -3238,6 +3265,7 @@
67386A8E206CF3F6004EDA6C /* DateFormattingService+DefaultImplementation.swift in Sources */,
671462961EB3396E00EAB194 /* CGSize+CGContextSize.swift in Sources */,
671463661EB3396E00EAB194 /* ViewHeightProtocol.swift in Sources */,
6732F241214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */,
67EB7FD120615B8900BDD9FB /* TotalCountCursorConfiguration.swift in Sources */,
678D267B20691D8200B05B93 /* DataModelFieldBinding.swift in Sources */,
673CF40D2063AB7C00C329F6 /* GeneralDataLoadingViewModel.swift in Sources */,
@ -3387,6 +3415,7 @@
B84CB06B20B702260090DB91 /* Encodable+Extensions.swift in Sources */,
671462731EB3396E00EAB194 /* CursorError.swift in Sources */,
6741CED020E243F800FEC4D9 /* BaseCustomViewController.swift in Sources */,
6732F242214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */,
677B06B521186C14006C947D /* Completable+DeferredJust.swift in Sources */,
6727478D206CD83600725163 /* DateFormat.swift in Sources */,
67EB7FDD20615D5B00BDD9FB /* ResettableRxCursorDataSource.swift in Sources */,
@ -3424,6 +3453,7 @@
6774529520625D170024EEEF /* GeneralDataLoadingModel.swift in Sources */,
6713C23A20AF0C4D00875921 /* NetworkOperationState.swift in Sources */,
6774529D20625E5B0024EEEF /* PaginationDataLoadingState.swift in Sources */,
6732F245214C189100B446F2 /* Single+DeferredJust.swift in Sources */,
6714632F1EB3396E00EAB194 /* ConfigurableController.swift in Sources */,
67990ACD213EA5B70040D195 /* ContentLoadingViewModel.swift in Sources */,
67EB7FF42061682F00BDD9FB /* TotalCountCursorListingResult+DefaultTotalCountCursorListingResult.swift in Sources */,
@ -3505,6 +3535,7 @@
671463111EB3396E00EAB194 /* UIViewController+DefaultXibName.swift in Sources */,
67153E41207DFBA80049D8C0 /* FloatingPoint+DegreesRadiansConvertion.swift in Sources */,
67990AE7213EB4080040D195 /* ConfigurableView+Extensions.swift in Sources */,
6732F240214C09F900B446F2 /* UserDefaults+Codable.swift in Sources */,
671462911EB3396E00EAB194 /* CGImage+Crop.swift in Sources */,
6760DC4E212F351700020BAE /* UIView+AddSubviews.swift in Sources */,
67E902582125B66E008EDF45 /* UIImageView+ExpandCollapseDisclosure.swift in Sources */,
@ -3638,6 +3669,7 @@
67990ACB213EA5B70040D195 /* ContentLoadingViewModel.swift in Sources */,
EFBE57D11EC35EF20040E00A /* Array+Extensions.swift in Sources */,
676B22A3206A626D002E9F8A /* NSAttributedString+Extensions.swift in Sources */,
6732F243214C189000B446F2 /* Single+DeferredJust.swift in Sources */,
671462D91EB3396E00EAB194 /* TimeInterval+DateComponents.swift in Sources */,
3622F5DD20E253F1009DED94 /* TableDirector+Extensions.swift in Sources */,
6714638D1EB3396E00EAB194 /* SolidFillDrawingOperation.swift in Sources */,

View File

@ -8,8 +8,8 @@ PODS:
- RxCocoa (4.2.0):
- RxSwift (~> 4.0)
- RxSwift (4.2.0)
- SwiftDate (5.0.4)
- SwiftLint (0.26.0)
- SwiftDate (5.0.7)
- SwiftLint (0.27.0)
- TableKit (2.7.0)
- UIScrollView-InfiniteScroll (1.1.0)
@ -38,8 +38,8 @@ SPEC CHECKSUMS:
RxAlamofire: 87a9c588541210cc3e4a1f843ccc3ecf3eb98b31
RxCocoa: 0b54909c902e1e581212a03e690bbd94032d8baa
RxSwift: 99e10317ddfcc7fbe01356aafd118fde4a0be104
SwiftDate: d9827f0e7edfeb8be52882beb67e75c773b634c3
SwiftLint: f6b83e8d95ee1e91e11932d843af4fdcbf3fc764
SwiftDate: f053fb89250c59af5eff777f3b832d2b9dd57403
SwiftLint: 3207c1faa2240bf8973b191820a116113cd11073
TableKit: 506650573ed96ec007649b655559ecd43f9fd505
UIScrollView-InfiniteScroll: 3ef456bcbe759c19f510a383cff96e6647c98c98

View File

@ -0,0 +1,108 @@
import RxSwift
public enum UserDefaultsError: Error {
case noSuchValue(key: String)
case unableToDecode(decodingError: Error)
}
public extension UserDefaults {
/// Returns the object with specified type associated with the first occurrence of the specified default.
///
/// - Parameters:
/// - key: A key in the current user's defaults database.
/// - decoder: JSON decoder to decode stored data.
/// - Returns: The object with specified type associated with the specified key,
/// or throw exception if the key was not found.
/// - Throws: One of cases in UserDefaultsError
func object<T: Decodable>(forKey key: String, decoder: JSONDecoder = JSONDecoder()) throws -> T {
guard let storedData = data(forKey: key) else {
throw UserDefaultsError.noSuchValue(key: key)
}
do {
return try decoder.decode(T.self, from: storedData)
} catch {
throw UserDefaultsError.unableToDecode(decodingError: error)
}
}
/// Returns the object with specified type associated with the first occurrence of the specified default.
///
/// - Parameters:
/// - key: A key in the current user's defaults database.
/// - defaultValue: A default value which will be used if there is no such value for specified key,
/// or if error occurred during mapping
/// - decoder: JSON decoder to decode stored data.
/// - Returns: The object with specified type associated with the specified key, or passed default value
/// if there is no such value for specified key or if error occurred during mapping.
func object<T: Decodable>(forKey key: String, defaultValue: T, decoder: JSONDecoder = JSONDecoder()) -> T {
return (try? object(forKey: key, decoder: decoder)) ?? defaultValue
}
/// Set or remove the value of the specified default key in the standard application domain.
///
/// - Parameters:
/// - object: The object with specified type to store or nil to remove it from the defaults database.
/// - key: The key with which to associate with the value.
/// - encoder: JSON encoder to encode to encode passed object.
/// - Throws: EncodingError if error is occured during passed object encoding.
func set<T: Encodable>(object: T?, forKey key: String, encoder: JSONEncoder = JSONEncoder()) throws {
if let object = object {
set(try encoder.encode(object), forKey: key)
} else {
set(nil, forKey: key)
}
}
subscript<T: Codable>(key: String) -> T? {
get {
return try? object(forKey: key)
}
set {
try? set(object: newValue, forKey: key)
}
}
}
public extension Reactive where Base: UserDefaults {
/// Reactive version of object<T>(forKey:decoder:) -> T.
///
/// - Parameters:
/// - key: A key in the current user's defaults database.
/// - decoder: JSON decoder to decode stored data.
/// - Returns: Single of specified model type.
func object<T: Decodable>(forKey key: String, decoder: JSONDecoder = JSONDecoder()) -> Single<T> {
return .deferredJust { try self.base.object(forKey: key, decoder: decoder) }
}
/// Reactive version of object<T>(forKey:defaultValue:decoder:) -> T.
///
/// - Parameters:
/// - key: A key in the current user's defaults database.
/// - defaultValue: A default value which will be used if there is no such value for specified key,
/// or if error occurred during mapping
/// - decoder: JSON decoder to decode stored data.
/// - Returns: Single of specified model type.
func object<T: Decodable>(forKey key: String, defaultValue: T, decoder: JSONDecoder = JSONDecoder()) -> Single<T> {
return .deferredJust { self.base.object(forKey: key, defaultValue: defaultValue, decoder: decoder) }
}
/// Reactive version of set<T>(object:forKey:encoder:).
///
/// - Parameters:
/// - object: The object with specified type to store in the defaults database.
/// - key: The key with which to associate with the value.
/// - encoder: JSON encoder to encode to encode passed object.
/// - Returns: Completable.
func set<T: Encodable>(object: T?, forKey key: String, encoder: JSONEncoder = JSONEncoder()) -> Completable {
return .deferredJust {
try self.base.set(object: object, forKey: key, encoder: encoder)
}
}
}

View File

@ -29,7 +29,7 @@ public extension PrimitiveSequence where Trait == CompletableTrait {
/// - Parameter workUnit: Element factory function to invoke for each observer
/// that subscribes to the resulting sequence.
/// - Returns: A single whose observers trigger an invocation of the given element factory function.
static func deferredJust(_ workUnit: @escaping () throws -> Void) -> Completable {
static func deferredJust(_ workUnit: @escaping ThrowableVoidBlock) -> Completable {
return .create { observer in
do {
try workUnit()

View File

@ -127,7 +127,7 @@ public extension TableDirector {
at indexPath: IndexPath,
with animation: UITableViewRowAnimation,
manualBeginEndUpdates: Bool = false) {
sections[indexPath.section].insert(rows: rows, at: indexPath.row)
let indexPaths: [IndexPath] = rows.indices.map {
IndexPath(row: indexPath.row + $0, section: indexPath.section)

View File

@ -20,7 +20,8 @@
// THE SOFTWARE.
//
import Foundation
/// Closure that takes no arguments and return Void.
public typealias VoidBlock = () -> Void
/// Closure that takes no arguments, may throw error and return Void.
public typealias ThrowableVoidBlock = () throws -> Void