Merge pull request #54 from TouchInstinct/fix/user_defaults

Fix/user defaults
This commit is contained in:
Nikolai Ashanin 2017-03-20 16:40:16 +03:00 committed by GitHub
commit 99f7dd98cd
43 changed files with 332 additions and 134 deletions

View File

@ -1,8 +1,21 @@
disabled_rules:
- force_cast
- trailing_whitespace
- variable_name
excluded:
- Carthage
- Pods
- RxAlamofire
line_length: 128
line_length: 128
type_body_length:
- 500 # warning
- 700 # error
file_length:
warning: 500
error: 1200
warning_threshold: 1
custom_rules:
uiwebview_disabled:
included: ".*.swift"
name: "UIWebView Usage Disabled"
regex: "(UIWebView)"
message: "Do not use UIWebView. Use WKWebView Instead. https://developer.apple.com/reference/uikit/uiwebview"
severity: error

View File

@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
6727419D1E65B99E0075836A /* MappableUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */; };
672741A01E65C1E00075836A /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419F1E65C1E00075836A /* Post.swift */; };
67788F9F1E69661800484DEE /* CGFloat+Pixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67788F9E1E69661800484DEE /* CGFloat+Pixels.swift */; };
78011A641D47ABC500EA16A2 /* UIView+DefaultReuseIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78011A631D47ABC500EA16A2 /* UIView+DefaultReuseIdentifier.swift */; };
78011AB31D48B53600EA16A2 /* ApiRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78011AB21D48B53600EA16A2 /* ApiRequestParameters.swift */; };
780D23431DA412470084620D /* CGImage+Alpha.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780D23421DA412470084620D /* CGImage+Alpha.swift */; };
@ -51,7 +54,6 @@
78C54AFD1E432EEF0051EFBA /* UIViewController+TopVisibleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78C54AFC1E432EEF0051EFBA /* UIViewController+TopVisibleViewController.swift */; };
78CFEE2E1C5C456B00F50370 /* LeadKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFEE2D1C5C456B00F50370 /* LeadKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
78CFEE351C5C456B00F50370 /* LeadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78CFEE2A1C5C456B00F50370 /* LeadKit.framework */; };
78CFEE3A1C5C456B00F50370 /* LeadKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE391C5C456B00F50370 /* LeadKitTests.swift */; };
78CFEE541C5C45E500F50370 /* UIView+LoadFromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */; };
78CFEE551C5C45E500F50370 /* NibNameProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE4A1C5C45E500F50370 /* NibNameProtocol.swift */; };
78CFEE561C5C45E500F50370 /* ReuseIdentifierProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78CFEE4B1C5C45E500F50370 /* ReuseIdentifierProtocol.swift */; };
@ -86,6 +88,9 @@
/* Begin PBXFileReference section */
12F36034A5278991B658B53E /* Pods_LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MappableUserDefaultsTests.swift; sourceTree = "<group>"; };
6727419F1E65C1E00075836A /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
67788F9E1E69661800484DEE /* CGFloat+Pixels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Pixels.swift"; sourceTree = "<group>"; };
78011A631D47ABC500EA16A2 /* UIView+DefaultReuseIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+DefaultReuseIdentifier.swift"; sourceTree = "<group>"; };
78011AB21D48B53600EA16A2 /* ApiRequestParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiRequestParameters.swift; sourceTree = "<group>"; };
780D23421DA412470084620D /* CGImage+Alpha.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Alpha.swift"; sourceTree = "<group>"; };
@ -132,7 +137,6 @@
78CFEE2D1C5C456B00F50370 /* LeadKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LeadKit.h; sourceTree = "<group>"; };
78CFEE2F1C5C456B00F50370 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
78CFEE341C5C456B00F50370 /* LeadKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LeadKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
78CFEE391C5C456B00F50370 /* LeadKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeadKitTests.swift; sourceTree = "<group>"; };
78CFEE3B1C5C456B00F50370 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
78CFEE481C5C45E500F50370 /* UIView+LoadFromNib.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+LoadFromNib.swift"; path = "LeadKit/Extensions/UIView/UIView+LoadFromNib.swift"; sourceTree = SOURCE_ROOT; };
78CFEE4A1C5C45E500F50370 /* NibNameProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibNameProtocol.swift; sourceTree = "<group>"; };
@ -180,6 +184,22 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6727419E1E65BF3C0075836A /* Models */ = {
isa = PBXGroup;
children = (
6727419F1E65C1E00075836A /* Post.swift */,
);
path = Models;
sourceTree = "<group>";
};
67788F9D1E6965F800484DEE /* CGFloat */ = {
isa = PBXGroup;
children = (
67788F9E1E69661800484DEE /* CGFloat+Pixels.swift */,
);
path = CGFloat;
sourceTree = "<group>";
};
78011A651D47AF3000EA16A2 /* Enums */ = {
isa = PBXGroup;
children = (
@ -394,8 +414,9 @@
78CFEE381C5C456B00F50370 /* LeadKitTests */ = {
isa = PBXGroup;
children = (
78CFEE391C5C456B00F50370 /* LeadKitTests.swift */,
6727419E1E65BF3C0075836A /* Models */,
78CFEE3B1C5C456B00F50370 /* Info.plist */,
6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */,
);
path = LeadKitTests;
sourceTree = "<group>";
@ -405,6 +426,7 @@
children = (
786D78E61D53C355006B2CEA /* Alamofire */,
780D23441DA416E80084620D /* CGContext */,
67788F9D1E6965F800484DEE /* CGFloat */,
780D23411DA412330084620D /* CGImage */,
789CC6091DE584C000F789D3 /* CursorType */,
78C36F7C1D801E2F00E7EBEA /* Double */,
@ -791,6 +813,7 @@
78CFEE551C5C45E500F50370 /* NibNameProtocol.swift in Sources */,
787609221E1403830093CE36 /* Observable+DeferredJust.swift in Sources */,
78C54AFD1E432EEF0051EFBA /* UIViewController+TopVisibleViewController.swift in Sources */,
67788F9F1E69661800484DEE /* CGFloat+Pixels.swift in Sources */,
783AF06B1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift in Sources */,
78CFEE561C5C45E500F50370 /* ReuseIdentifierProtocol.swift in Sources */,
78A0FCC81DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift in Sources */,
@ -828,7 +851,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
78CFEE3A1C5C456B00F50370 /* LeadKitTests.swift in Sources */,
6727419D1E65B99E0075836A /* MappableUserDefaultsTests.swift in Sources */,
672741A01E65C1E00075836A /* Post.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -70,5 +70,5 @@ public class FixedPageCursor<Cursor: CursorType>: CursorType where Cursor.LoadRe
.flatMap { _ in self.loadNextBatch() }
}
}
}

View File

@ -79,5 +79,5 @@ public class MapCursor<Cursor: CursorType, T>: CursorType where Cursor.LoadResul
return startIndex..<self.elements.count
}
}
}

View File

@ -57,5 +57,5 @@ public class StaticCursor<Element>: CursorType {
return Observable.just(0..<self.count)
}
}
}

View File

@ -25,19 +25,16 @@ import CocoaLumberjack
open class Log {
/// Logger for CocoaLumberJack
open let fileLogger = DDFileLogger()
public init() {
DDLog.add(fileLogger)
DDLog.add(DDFileLogger())
DDLog.add(DDASLLogger.sharedInstance())
DDLog.add(DDTTYLogger.sharedInstance())
DDLog.add(DDASLLogger.sharedInstance)
DDLog.add(DDTTYLogger.sharedInstance)
let logFormatter = LogFormatter()
DDASLLogger.sharedInstance().logFormatter = logFormatter
DDTTYLogger.sharedInstance().logFormatter = logFormatter
DDASLLogger.sharedInstance.logFormatter = logFormatter
DDTTYLogger.sharedInstance.logFormatter = logFormatter
let assertionHandler = NSAssertionHandler()

View File

@ -22,9 +22,8 @@
import Foundation
import CocoaLumberjack
import CocoaLumberjack.DDDispatchQueueLogFormatter
class LogFormatter: DDDispatchQueueLogFormatter {
class LogFormatter: NSObject, DDLogFormatter {
fileprivate let dateFormatter: DateFormatter
override init() {
@ -35,7 +34,7 @@ class LogFormatter: DDDispatchQueueLogFormatter {
super.init()
}
override func format(message logMessage: DDLogMessage) -> String {
func format(message logMessage: DDLogMessage) -> String? {
let level: String
switch logMessage.flag {

View File

@ -115,5 +115,5 @@ public extension Observable {
networkService.decreaseRequestCounter()
})
}
}

View File

@ -45,7 +45,7 @@ public extension Reactive where Base: Alamofire.SessionManager {
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func responseModel<T: ImmutableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = DispatchQueue.global())
mappingQueue: DispatchQueue = DispatchQueue.global())
-> Observable<(response: HTTPURLResponse, model: T)> {
return apiRequest(requestParameters: requestParameters)
@ -58,7 +58,7 @@ public extension Reactive where Base: Alamofire.SessionManager {
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func responseModel<T: ImmutableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = DispatchQueue.global())
mappingQueue: DispatchQueue = DispatchQueue.global())
-> Observable<(response: HTTPURLResponse, models: [T])> {
return apiRequest(requestParameters: requestParameters)
@ -71,7 +71,7 @@ public extension Reactive where Base: Alamofire.SessionManager {
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and target object
func responseObservableModel<T: ObservableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = DispatchQueue.global())
mappingQueue: DispatchQueue = DispatchQueue.global())
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
return apiRequest(requestParameters: requestParameters)
@ -84,7 +84,7 @@ public extension Reactive where Base: Alamofire.SessionManager {
/// - Parameter mappingQueue: The dispatch queue to use for mapping
/// - Returns: Observable with HTTP URL Response and array of target objects
func responseObservableModel<T: ObservableMappable>(requestParameters: ApiRequestParameters,
mappingQueue: DispatchQueue = DispatchQueue.global())
mappingQueue: DispatchQueue = DispatchQueue.global())
-> Observable<(response: HTTPURLResponse, models: [T])> where T.ModelType == T {
return apiRequest(requestParameters: requestParameters)

View File

@ -65,5 +65,5 @@ public extension CGContext {
space: colorSpace,
bitmapInfo: bitmapInfo.rawValue)
}
}

View File

@ -0,0 +1,34 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import UIKit
extension CGFloat {
/// Initializes instance with given number of pixels converted to points
///
/// - Parameter pixels: desired number of pixels on screen
init(pixels: CGFloat) {
self.init(pixels / UIScreen.main.nativeScale)
}
}

View File

@ -49,5 +49,5 @@ public extension CGImage {
return ctx?.makeImage()
}
}

View File

@ -71,7 +71,7 @@ public extension CGImage {
}
view.layer.render(in: ctx)
return ctx.makeImage()
}

View File

@ -45,5 +45,5 @@ public extension CGImage {
return cropping(to: cropRect)
}
}

View File

@ -49,5 +49,5 @@ public extension CGImage {
return ctx.makeImage()
}
}

View File

@ -94,7 +94,7 @@ public extension CGImage {
} else {
ctx.stroke(inset, width: border)
}
return ctx.makeImage()
}
@ -191,13 +191,13 @@ public extension CGImage {
y: padding,
width: CGFloat(width),
height: CGFloat(height))
ctx.addRect(imageLocation)
ctx.clip()
ctx.draw(self, in: imageLocation)
return ctx.makeImage()
}
}

View File

@ -31,7 +31,7 @@ public extension CursorType where LoadResultType == CountableRange<Int> {
var loadedElements: [Self.Element] {
return self[0..<count]
}
}
public extension CursorType where LoadResultType == CountableClosedRange<Int> {
@ -43,5 +43,5 @@ public extension CursorType where LoadResultType == CountableClosedRange<Int> {
var loadedElements: [Self.Element] {
return self[0...count - 1]
}
}

View File

@ -44,7 +44,7 @@ public extension Double {
- returns: rounded value
*/
public func roundValue(withPersicion persicion: UInt,
roundType: RoundingType = .normal) -> Double {
roundType: RoundingType = .normal) -> Double {
let divider = pow(10.0, Double(persicion))
switch roundType {

View File

@ -30,8 +30,8 @@ extension IndexPath {
if Mirror(reflecting: self).subjectType == IndexPath.self { // check for UIMutableIndexPath
return self
}
return IndexPath(item: item, section: section)
}
}

View File

@ -41,5 +41,5 @@ public extension Observable {
return Disposables.create()
}
}
}

View File

@ -33,8 +33,8 @@ public extension Sequence {
/// - transform: Transform closure
/// - Returns: Observable of array which contains transform return type
func concurrentRxMap<R>(concurrentOperationCount: Int = ProcessInfo.processInfo.activeProcessorCount,
qos: DispatchQoS = .default,
transform: @escaping ((Iterator.Element) throws -> R)) -> Observable<[R]> {
qos: DispatchQoS = .default,
transform: @escaping ((Iterator.Element) throws -> R)) -> Observable<[R]> {
let operationsCount = Swift.max(1, concurrentOperationCount)
@ -61,5 +61,5 @@ public extension Sequence {
.toArray()
.map { $0.sorted { $0.0.idx < $0.1.idx }.flatMap { $0.results } }
}
}

View File

@ -43,5 +43,5 @@ ViewControllerIdentifier: RawRepresentable, ViewControllerIdentifier.RawValue ==
public static func instantiateViewController(_ viewController: ViewControllerIdentifier) -> UIViewController {
return uiStoryboard.instantiateViewController(withIdentifier: viewController.rawValue)
}
}

View File

@ -66,8 +66,8 @@ public extension String {
- returns: string size calculation result
*/
public func size(withAttributes attributes: [String: AnyObject]?,
maxWidth: CGFloat = CGFloat.greatestFiniteMagnitude,
maxHeight: CGFloat = CGFloat.greatestFiniteMagnitude) -> StringSizeCalculationResult {
maxWidth: CGFloat = CGFloat.greatestFiniteMagnitude,
maxHeight: CGFloat = CGFloat.greatestFiniteMagnitude) -> StringSizeCalculationResult {
let size = self.boundingRect(with: CGSize(width: maxWidth, height: maxHeight),
options: [.usesLineFragmentOrigin, .usesFontLeading],
@ -75,8 +75,8 @@ public extension String {
context: nil).size
let fontLineHeight = (attributes?[NSFontAttributeName] as? UIFont)?.lineHeight
return StringSizeCalculationResult(size: size, fontLineHeight: fontLineHeight)
}
}

View File

@ -30,6 +30,8 @@ extension TimeInterval {
private static let secondsInHour = secondsInMinute * minutesInHour
private static let secondsInDay = secondsInHour * hoursInDay
public typealias TimeComponents = (days: Int, hours: Int, minutes: Int, seconds: Int)
/**
Deserialize TimeInterval from string
Works fine for values like 0:0, 0:0:0, 000:00:00, 0.00:00:00
@ -57,7 +59,7 @@ extension TimeInterval {
Returns a tuple with date components, contained in TimeInterval value
Supported components: days, hours, minutes, seconds
*/
public var timeComponents: (days: Int, hours: Int, minutes: Int, seconds: Int) {
public var timeComponents: TimeComponents {
var ti = Int(self)
let days = (ti / TimeInterval.secondsInDay) % TimeInterval.secondsInDay
ti -= days * TimeInterval.secondsInDay

View File

@ -51,9 +51,9 @@ public extension UICollectionView {
*/
public func registerNib<T>(forCellClass cellClass: T.Type,
forUserInterfaceIdiom interfaceIdiom: UIUserInterfaceIdiom)
forUserInterfaceIdiom interfaceIdiom: UIUserInterfaceIdiom)
where T: ReuseIdentifierProtocol, T: UICollectionViewCell, T: NibNameProtocol {
let nib = UINib(nibName: T.nibName(forConfiguration: interfaceIdiom))
register(nib, forCellWithReuseIdentifier: T.reuseIdentifier)
}

View File

@ -28,5 +28,5 @@ public extension UIDevice {
public static var isSimulator: Bool {
return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
}
}

View File

@ -29,9 +29,14 @@ extension UIStoryboard {
- returns: UIViewController subclass instance
*/
public func instantiateViewController<T>() -> T where T: UIViewController, T: StoryboardIdentifierProtocol {
return self.instantiateViewController(withIdentifier: T.storyboardIdentifier) as! T
guard let viewController = self.instantiateViewController(withIdentifier: T.storyboardIdentifier) as? T else {
fatalError("Can't instantiate view controller with type \(T.self)")
}
return viewController
}
}

View File

@ -32,5 +32,5 @@ extension UIView: StaticNibNameProtocol {
open class var nibName: String {
return className(of: self)
}
}

View File

@ -36,12 +36,12 @@ public extension UIView {
- returns: UIView subclass instance
*/
public static func loadFromNib<T>
(forUserInterfaceIdiom interfaceIdiom: UIUserInterfaceIdiom) -> T where T: NibNameProtocol, T: UIView {
return loadFromNib(named: T.nibName(forConfiguration: interfaceIdiom))
}
/**
method which return UIView subclass instance loaded from nib using nib name
provided by StaticNibNameProtocol implementation
@ -51,7 +51,7 @@ public extension UIView {
public static func loadFromNib<T>() -> T where T: StaticNibNameProtocol, T: UIView {
return loadFromNib(named: T.nibName)
}
/**
method which loads UIView (or subclass) instance from nib using given nib name parameter
@ -60,7 +60,12 @@ public extension UIView {
- returns: UIView subclass instance
*/
public static func loadFromNib<T>(named nibName: String) -> T {
return UINib(nibName: nibName).instantiate(withOwner: nil, options: nil).first as! T
guard let nibView = UINib(nibName: nibName).instantiate(withOwner: nil, options: nil).first as? T else {
fatalError("Can't nstantiate nib view with type \(T.self)")
}
return nibView
}
}

View File

@ -47,5 +47,5 @@ extension UIView {
public func stopZRotation() {
layer.removeAnimation(forKey: UIView.rotationKeyPath)
}
}

View File

@ -32,5 +32,5 @@ extension UIViewController: StoryboardIdentifierProtocol {
open class var storyboardIdentifier: String {
return className(of: self)
}
}

View File

@ -33,7 +33,7 @@ public enum UserDefaultsError: Error {
case noSuchValue(key: String)
case unableToMap(mappingError: Error)
}
fileprivate typealias JSONObject = [String: Any]
@ -74,7 +74,7 @@ public extension UserDefaults {
///
/// - returns: The array of objects with specified type associated with the specified key,
/// or throw exception if the key was not found.
public func object<T>(forKey key: String) throws -> [T] where T: ImmutableMappable {
public func objects<T>(forKey key: String) throws -> [T] where T: ImmutableMappable {
let jsonArray = try storedValue(forKey: key) as [JSONObject]
do {
@ -104,8 +104,8 @@ public extension UserDefaults {
///
/// - returns: The array of objects 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.
public func object<T>(forKey key: String, defaultValue: [T]) -> [T] where T: ImmutableMappable {
return (try? object(forKey: key)) ?? defaultValue
public func objects<T>(forKey key: String, defaultValue: [T]) -> [T] where T: ImmutableMappable {
return (try? objects(forKey: key)) ?? defaultValue
}
/// Sets or removes the value of the specified default key in the standard application domain.
@ -113,7 +113,7 @@ public extension UserDefaults {
/// - Parameters:
/// - model: 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.
public func set<T>(_ model: T?, forKey key: String) where T: ImmutableMappable {
public func set<T>(model: T?, forKey key: String) where T: ImmutableMappable {
if let model = model {
set(model.toJSON(), forKey: key)
} else {
@ -126,7 +126,7 @@ public extension UserDefaults {
/// - Parameters:
/// - models: The array of 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.
public func set<T, S>(_ models: S?, forKey key: String) where T: ImmutableMappable, S: Sequence, S.Iterator.Element == T {
public func set<T, S>(models: S?, forKey key: String) where T: ImmutableMappable, S: Sequence, S.Iterator.Element == T {
if let models = models {
set(models.map { $0.toJSON() }, forKey: key)
} else {
@ -166,7 +166,7 @@ public extension Reactive where Base: UserDefaults {
///
/// - returns: Observable of specified array type.
func object<T>(forKey key: String) -> Observable<[T]> where T: ImmutableMappable {
return Observable.deferredJust { try self.base.object(forKey: key) }
return Observable.deferredJust { try self.base.objects(forKey: key) }
}
/// Reactive version of object<T>(forKey:defaultValue:) -> [T].
@ -179,7 +179,7 @@ public extension Reactive where Base: UserDefaults {
///
/// - returns: Observable of specified array type.
func object<T>(forKey key: String, defaultValue: [T]) -> Observable<[T]> where T: ImmutableMappable {
return Observable.deferredJust { self.base.object(forKey: key, defaultValue: defaultValue) }
return Observable.deferredJust { self.base.objects(forKey: key, defaultValue: defaultValue) }
}
/// Reactive version of set<T>(_:forKey:).
@ -190,9 +190,9 @@ public extension Reactive where Base: UserDefaults {
/// - parameter key: The key with which to associate with the value.
///
/// - returns: Observable of Void type.
func set<T>(_ model: T?, forKey key: String) -> Observable<Void> where T: ImmutableMappable {
func set<T>(model: T?, forKey key: String) -> Observable<Void> where T: ImmutableMappable {
return Observable.create { observer in
observer.onNext(self.base.set(model, forKey: key))
observer.onNext(self.base.set(model: model, forKey: key))
observer.onCompleted()
return Disposables.create()
@ -207,11 +207,11 @@ public extension Reactive where Base: UserDefaults {
/// - parameter key: The key with which to associate with the value.
///
/// - returns: Observable of Void type.
func set<T, S>(_ models: S?, forKey key: String) -> Observable<Void>
func set<T, S>(models: S?, forKey key: String) -> Observable<Void>
where T: ImmutableMappable, S: Sequence, S.Iterator.Element == T {
return Observable.create { observer in
observer.onNext(self.base.set(models, forKey: key))
observer.onNext(self.base.set(models: models, forKey: key))
observer.onCompleted()
return Disposables.create()

View File

@ -72,5 +72,5 @@ extension ConfigurableController where Self: UIViewController {
localize()
bindViews()
}
}

View File

@ -41,5 +41,5 @@ public protocol CursorType {
///
/// - Returns: Observable of LoadResultType
func loadNextBatch() -> Observable<LoadResultType>
}

View File

@ -27,7 +27,7 @@ import Foundation
*/
public protocol EstimatedViewHeightProtocol {
associatedtype ViewModelType
/**
method which returns estimated view height for specific view model

View File

@ -27,7 +27,7 @@ import Foundation
*/
public protocol AbstractReuseIdentifierProtocol {
associatedtype IdentifierType
/**
- returns: reuse identifier with protocol associated type
*/

View File

@ -48,7 +48,7 @@ public protocol StoryboardProtocol {
- returns: UIViewController instance
*/
static func instantiateViewController(_: ViewControllerIdentifier) -> UIViewController
}
public extension StoryboardProtocol {
@ -60,5 +60,5 @@ public extension StoryboardProtocol {
}
return controller
}
}

View File

@ -27,7 +27,7 @@ import Foundation
*/
public protocol ViewHeightProtocol {
associatedtype ViewModelType
/**
method which returns view height for specific view model

View File

@ -1,35 +0,0 @@
//
// LeadKitTests.swift
// LeadKitTests
//
// Created by Иван Смолин on 30/01/16.
// Copyright © 2016 Touch Instinct. All rights reserved.
//
import XCTest
class LeadKitTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -0,0 +1,94 @@
//
// MappableUserDefaultsTests.swift
// LeadKit
//
// Created by Ivan Smolin on 28/02/2017.
// Copyright © 2017 Touch Instinct. All rights reserved.
//
import XCTest
import LeadKit
import RxSwift
class MappableUserDefaultsTests: XCTestCase {
lazy var post: Post = {
return Post(userId: 1, id: 1, title: "First post", body: "")
}()
lazy var posts: [Post] = {
return [Post(userId: 1, id: 1, title: "First post", body: ""),
Post(userId: 1, id: 2, title: "Second post", body: ""),
Post(userId: 2, id: 3, title: "Third post", body: ""),
Post(userId: 2, id: 4, title: "Forth post", body: "")]
}()
let userDefaults = UserDefaults.standard
static let postKey = "post"
static let postsKey = "posts"
let disposeBag = DisposeBag()
override func setUp() {
super.setUp()
}
override func tearDown() {
userDefaults.set(nil, forKey: MappableUserDefaultsTests.postKey)
userDefaults.set(nil, forKey: MappableUserDefaultsTests.postsKey)
super.tearDown()
}
func testPostSave() {
userDefaults.set(model: post, forKey: MappableUserDefaultsTests.postKey)
do {
let savedPost: Post = try userDefaults.object(forKey: MappableUserDefaultsTests.postKey)
XCTAssertTrue(savedPost == post, "Saved post != test post")
} catch {
XCTFail(error.localizedDescription)
}
}
func testPostsSave() {
userDefaults.set(models: posts, forKey: MappableUserDefaultsTests.postsKey)
do {
let savedPosts: [Post] = try userDefaults.objects(forKey: MappableUserDefaultsTests.postsKey)
XCTAssertTrue(savedPosts == posts, "Saved posts != test posts")
} catch {
XCTFail(error.localizedDescription)
}
}
func testRxPostSave() {
userDefaults.rx.set(model: post, forKey: MappableUserDefaultsTests.postKey)
.flatMap {
self.userDefaults.rx.object(forKey: MappableUserDefaultsTests.postKey) as Observable<Post>
}
.subscribe(onNext: { savedPost in
XCTAssertTrue(savedPost == self.post, "Saved post != test post")
}, onError: { error in
XCTFail(error.localizedDescription)
})
.addDisposableTo(disposeBag)
}
func testRxPostsSave() {
userDefaults.rx.set(models: posts, forKey: MappableUserDefaultsTests.postsKey)
.flatMap {
self.userDefaults.rx.object(forKey: MappableUserDefaultsTests.postsKey) as Observable<[Post]>
}
.subscribe(onNext: { savedPosts in
XCTAssertTrue(savedPosts == self.posts, "Saved posts != test posts")
}, onError: { error in
XCTFail(error.localizedDescription)
})
.addDisposableTo(disposeBag)
}
}

View File

@ -0,0 +1,64 @@
//
// Copyright (c) 2017 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import ObjectMapper
struct Post: ImmutableMappable {
let userId: Int
let postId: Int
let title: String
let body: String
init(userId: Int, postId: Int, title: String, body: String) {
self.userId = userId
self.postId = postId
self.title = title
self.body = body
}
init(map: Map) throws {
userId = try map.value("userId")
postId = try map.value("id")
title = try map.value("title")
body = try map.value("body")
}
mutating func mapping(map: Map) {
userId >>> map["userId"]
postId >>> map["id"]
title >>> map["title"]
body >>> map["body"]
}
}
extension Post: Equatable {
static func == (lhs: Post, rhs: Post) -> Bool {
return lhs.userId == rhs.userId &&
lhs.postId == rhs.postId &&
lhs.title == rhs.title &&
lhs.body == rhs.body
}
}

View File

@ -4,7 +4,7 @@ platform :ios, '9.0'
target 'LeadKit' do
use_frameworks!
pod "CocoaLumberjack/Swift", '~> 3.0.0'
pod "CocoaLumberjack/Swift", '~> 3.1.0'
pod "RxSwift", '3.2.0'
pod "RxCocoa", '3.2.0'
pod "RxAlamofire", '3.0.0'

View File

@ -1,13 +1,9 @@
PODS:
- Alamofire (4.3.0)
- CocoaLumberjack/Core (3.0.0)
- CocoaLumberjack/Default (3.0.0):
- CocoaLumberjack/Core
- CocoaLumberjack/Extensions (3.0.0):
- Alamofire (4.4.0)
- CocoaLumberjack/Default (3.1.0)
- CocoaLumberjack/Swift (3.1.0):
- CocoaLumberjack/Default
- CocoaLumberjack/Swift (3.0.0):
- CocoaLumberjack/Extensions
- ObjectMapper (2.2.3)
- ObjectMapper (2.2.5)
- RxAlamofire (3.0.0):
- RxAlamofire/Core (= 3.0.0)
- RxAlamofire/Core (3.0.0):
@ -19,7 +15,7 @@ PODS:
- Toast-Swift (2.0.0)
DEPENDENCIES:
- CocoaLumberjack/Swift (~> 3.0.0)
- CocoaLumberjack/Swift (~> 3.1.0)
- ObjectMapper (~> 2.1)
- RxAlamofire (= 3.0.0)
- RxCocoa (= 3.2.0)
@ -27,14 +23,14 @@ DEPENDENCIES:
- Toast-Swift (~> 2.0.0)
SPEC CHECKSUMS:
Alamofire: 856a113053a7bc9cbe5d6367a555d773fc5cfef7
CocoaLumberjack: c823149bccc5519a9447aeb433be7b1212a7d6a5
ObjectMapper: d3b3de11267f5d971f390eb8d63dd509116a4329
Alamofire: dc44b1600b800eb63da6a19039a0083d62a6a62d
CocoaLumberjack: 8311463ddf9ee86a06ef92a071dd656c89244500
ObjectMapper: fb30f71e08470d1e5a20b199fafe1246281db898
RxAlamofire: 0b1fa48f545fffe7f7a28af2086bcaa3b5946cc9
RxCocoa: ccdf43101a70407097a29082f648ba1676075b30
RxSwift: 46574f70d416b7923c237195939cc488a7fbf3a0
Toast-Swift: 5b2f8f720f7e78e48511f693df1f9c9a6e38a25a
PODFILE CHECKSUM: 633d32257b0584c0270a56c06f3a7e022e383d80
PODFILE CHECKSUM: ee07f67e4cce90b2d448e0c18941bc51ab6180cc
COCOAPODS: 1.2.0