Merge pull request #54 from TouchInstinct/fix/user_defaults
Fix/user defaults
This commit is contained in:
commit
99f7dd98cd
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,5 +70,5 @@ public class FixedPageCursor<Cursor: CursorType>: CursorType where Cursor.LoadRe
|
|||
.flatMap { _ in self.loadNextBatch() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,5 +79,5 @@ public class MapCursor<Cursor: CursorType, T>: CursorType where Cursor.LoadResul
|
|||
return startIndex..<self.elements.count
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,5 +57,5 @@ public class StaticCursor<Element>: CursorType {
|
|||
return Observable.just(0..<self.count)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -115,5 +115,5 @@ public extension Observable {
|
|||
networkService.decreaseRequestCounter()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -65,5 +65,5 @@ public extension CGContext {
|
|||
space: colorSpace,
|
||||
bitmapInfo: bitmapInfo.rawValue)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -49,5 +49,5 @@ public extension CGImage {
|
|||
|
||||
return ctx?.makeImage()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public extension CGImage {
|
|||
}
|
||||
|
||||
view.layer.render(in: ctx)
|
||||
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,5 +45,5 @@ public extension CGImage {
|
|||
|
||||
return cropping(to: cropRect)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ public extension CGImage {
|
|||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ extension IndexPath {
|
|||
if Mirror(reflecting: self).subjectType == IndexPath.self { // check for UIMutableIndexPath
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
return IndexPath(item: item, section: section)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,5 @@ public extension Observable {
|
|||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 } }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ ViewControllerIdentifier: RawRepresentable, ViewControllerIdentifier.RawValue ==
|
|||
public static func instantiateViewController(_ viewController: ViewControllerIdentifier) -> UIViewController {
|
||||
return uiStoryboard.instantiateViewController(withIdentifier: viewController.rawValue)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,5 +28,5 @@ public extension UIDevice {
|
|||
public static var isSimulator: Bool {
|
||||
return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,5 +32,5 @@ extension UIView: StaticNibNameProtocol {
|
|||
open class var nibName: String {
|
||||
return className(of: self)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,5 +47,5 @@ extension UIView {
|
|||
public func stopZRotation() {
|
||||
layer.removeAnimation(forKey: UIView.rotationKeyPath)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,5 +32,5 @@ extension UIViewController: StoryboardIdentifierProtocol {
|
|||
open class var storyboardIdentifier: String {
|
||||
return className(of: self)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -72,5 +72,5 @@ extension ConfigurableController where Self: UIViewController {
|
|||
localize()
|
||||
bindViews()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,5 @@ public protocol CursorType {
|
|||
///
|
||||
/// - Returns: Observable of LoadResultType
|
||||
func loadNextBatch() -> Observable<LoadResultType>
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import Foundation
|
|||
*/
|
||||
public protocol EstimatedViewHeightProtocol {
|
||||
associatedtype ViewModelType
|
||||
|
||||
|
||||
/**
|
||||
method which returns estimated view height for specific view model
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import Foundation
|
|||
*/
|
||||
public protocol AbstractReuseIdentifierProtocol {
|
||||
associatedtype IdentifierType
|
||||
|
||||
|
||||
/**
|
||||
- returns: reuse identifier with protocol associated type
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import Foundation
|
|||
*/
|
||||
public protocol ViewHeightProtocol {
|
||||
associatedtype ViewModelType
|
||||
|
||||
|
||||
/**
|
||||
method which returns view height for specific view model
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue