Commit d76697b6 authored by jz's avatar jz

add view & model

parent 1c2272a7
......@@ -326,9 +326,13 @@
"${PODS_ROOT}/Target Support Files/Pods-GMAI_Example/Pods-GMAI_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/EVReflection/EVReflection.framework",
"${BUILT_PRODUCTS_DIR}/GM-Swift-Observable/GM_Swift_Observable.framework",
"${BUILT_PRODUCTS_DIR}/GMAI/GMAI.framework",
"${BUILT_PRODUCTS_DIR}/GMBase/GMBase.framework",
"${BUILT_PRODUCTS_DIR}/GMBaseSwift/GMBaseSwift.framework",
"${BUILT_PRODUCTS_DIR}/GMCache/GMCache.framework",
"${BUILT_PRODUCTS_DIR}/GMFoundation/GMFoundation.framework",
"${BUILT_PRODUCTS_DIR}/GMHud/GMHud.framework",
"${BUILT_PRODUCTS_DIR}/GMJSONModel/GMJSONModel.framework",
"${BUILT_PRODUCTS_DIR}/GMKit/GMKit.framework",
......@@ -336,9 +340,12 @@
"${BUILT_PRODUCTS_DIR}/GMNetworking/GMNetworking.framework",
"${BUILT_PRODUCTS_DIR}/GMPhobos/GMPhobos.framework",
"${BUILT_PRODUCTS_DIR}/GMRefresh/GMRefresh.framework",
"${BUILT_PRODUCTS_DIR}/GMRouter/GMRouter.framework",
"${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework",
"${BUILT_PRODUCTS_DIR}/MJExtension/MJExtension.framework",
"${BUILT_PRODUCTS_DIR}/MJRefresh/MJRefresh.framework",
"${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework",
"${BUILT_PRODUCTS_DIR}/Qiniu/Qiniu.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
"${BUILT_PRODUCTS_DIR}/TMCache/TMCache.framework",
......@@ -348,9 +355,13 @@
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EVReflection.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GM_Swift_Observable.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMAI.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMBase.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMBaseSwift.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMCache.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMFoundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMHud.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMJSONModel.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMKit.framework",
......@@ -358,9 +369,12 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMNetworking.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMPhobos.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMRefresh.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GMRouter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJExtension.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJRefresh.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Qiniu.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TMCache.framework",
......
......@@ -7,10 +7,16 @@ source 'git@git.wanmeizhensuo.com:gengmeiios/GMSpecs.git'
target 'GMAI_Example' do
pod 'GMAI', :path => '../'
pod 'GMBaseSwift', '3.3.7'
# pod 'EVReflection', '5.10.0'
target 'GMAI_Tests' do
inherit! :search_paths
end
end
pre_install do |installer|
# workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
end
......@@ -15,9 +15,20 @@ PODS:
- AFNetworking/UIKit (3.1.0):
- AFNetworking/NSURLSession
- Alamofire (4.7.0)
- EVReflection (5.10.0):
- EVReflection/Core (= 5.10.0)
- EVReflection/Core (5.10.0)
- GM-Swift-Observable (4.0.1)
- GMAI (0.1.0):
- EVReflection
- GMBase
- GMBaseSwift
- GMCache
- GMFoundation
- GMNetworking
- GMRouter
- Qiniu
- SDWebImage
- GMBase (1.1.5):
- GMHud
- GMJSONModel
......@@ -28,8 +39,17 @@ PODS:
- MBProgressHUD
- SDWebImage
- "UITableView+FDTemplateLayoutCell (= 1.4)"
- GMBaseSwift (3.3.7):
- EVReflection (= 5.10.0)
- GM-Swift-Observable
- GMHud
- GMNetworking
- GMPhobos
- GMRefresh
- SnapKit (= 4.0.0)
- GMCache (1.0.1):
- TMCache (= 2.1.0)
- GMFoundation (1.0.5)
- GMHud (1.0.3):
- MBProgressHUD (= 0.9.2)
- GMJSONModel (1.7.4)
......@@ -84,23 +104,31 @@ PODS:
- GMRefresh (1.0.4):
- GMPhobos
- MJRefresh
- GMRouter (0.1.5):
- MJExtension
- Masonry (1.1.0)
- MBProgressHUD (0.9.2)
- MJExtension (3.2.1)
- MJRefresh (3.3.1)
- Qiniu (7.3.0)
- SDWebImage (5.4.2):
- SDWebImage/Core (= 5.4.2)
- SDWebImage/Core (5.4.2)
- SnapKit (4.2.0)
- SnapKit (4.0.0)
- TMCache (2.1.0)
- "UITableView+FDTemplateLayoutCell (1.4)"
DEPENDENCIES:
- GMAI (from `../`)
- GMBaseSwift (= 3.3.7)
SPEC REPOS:
"git@git.wanmeizhensuo.com:gengmeiios/GMSpecs.git":
- GM-Swift-Observable
- GMBase
- GMBaseSwift
- GMCache
- GMFoundation
- GMHud
- GMJSONModel
- GMKit
......@@ -108,12 +136,16 @@ SPEC REPOS:
- GMNetworking
- GMPhobos
- GMRefresh
- GMRouter
https://github.com/cocoapods/specs.git:
- AFNetworking
- Alamofire
- EVReflection
- Masonry
- MBProgressHUD
- MJExtension
- MJRefresh
- Qiniu
- SDWebImage
- SnapKit
- TMCache
......@@ -126,9 +158,13 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67
Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25
GMAI: d13c3aef135a5fa2075123173cb20f1eb650596d
EVReflection: 1abc1a81927ab0d30170238cf9b79bff489e9728
GM-Swift-Observable: 756d8fc13638b9faa68cb10266b2ffb47a911595
GMAI: 6dbdb0af8d3239093438c761e84f6ed88b5a53ad
GMBase: 2b77f591a35a589ed331c490af093b68b0fc73be
GMBaseSwift: 168898c825ef16ffbf8bd261cb4b295cfb492bd2
GMCache: b78d8e46db864405e91d226ce640cc80d966c611
GMFoundation: 3b621bde6b0661ae61393b55691974f570f46208
GMHud: 18d41f4900a204f27be14e9504fcee2060ae3b2c
GMJSONModel: 5e81a98de668e9f93cf6ff77869f77b0d1a806be
GMKit: 09fe863069d9750c89fae2939770b08fc74b9027
......@@ -136,14 +172,17 @@ SPEC CHECKSUMS:
GMNetworking: 592b9b71f2a7d92203483276158ce3139ac789d2
GMPhobos: 1e2d68c456b69bf156276d7242877498107474db
GMRefresh: c01ff8de5ada92e1362602fb6991f99124b7dbe3
GMRouter: 808430d1275e92809eb1180ed3cb89525295da31
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
MBProgressHUD: 1569cf7ace17a8bac47aabfbb8580a49690386d1
MJExtension: 635f2c663dcb1bf76fa4b715b2570a5710aec545
MJRefresh: eeda70fbf0ad277f3178cef1cd0c3532591d6237
Qiniu: a2fb1f87b40f52bc1f386d93c6d48aab09472eae
SDWebImage: 4ca2dc4eefae4224bea8f504251cda485a363745
SnapKit: fe8a619752f3f27075cc9a90244d75c6c3f27e2a
SnapKit: a42d492c16e80209130a3379f73596c3454b7694
TMCache: 95ebcc9b3c7e90fb5fd8fc3036cba3aa781c9bed
"UITableView+FDTemplateLayoutCell": 234e1582bcc4e18461af91155123bb96538ed030
PODFILE CHECKSUM: a3a39944abac09536207b087a26344c6d8478f6b
PODFILE CHECKSUM: 841939c0a048be2edb77418fd6de6e35a738768b
COCOAPODS: 1.7.4
MIT 3 License
Copyright (c) 2015, EVICT B.V.
All rights reserved.
http://evict.nl, mailto://edwin@evict.nl
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of EVICT B.V. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This diff is collapsed.
//
// ConversionOptions.swift
// EVReflection
//
// Created by Edwin Vermeer on 9/5/16.
// Copyright © 2015 evict. All rights reserved.
//
/**
For specifying what conversion options should be executed
*/
public struct ConversionOptions: OptionSet, CustomStringConvertible {
/// The numeric representation of the options
public let rawValue: Int
/**
Initialize with a raw value
- parameter rawValue: the numeric representation
- returns: The ConversionOptions
*/
public init(rawValue: Int) { self.rawValue = rawValue }
/// No conversion options
public static let None = ConversionOptions(rawValue: 0)
/// Execute property converters
public static let PropertyConverter = ConversionOptions(rawValue: 1)
/// Execute property mapping
public static let PropertyMapping = ConversionOptions(rawValue: 2)
/// Skip specific property values
public static let SkipPropertyValue = ConversionOptions(rawValue: 4)
/// Do a key cleanup (CameCase, snake_case)
public static let KeyCleanup = ConversionOptions(rawValue: 8)
/// Execute the decoding function for all values
public static let Decoding = ConversionOptions(rawValue: 16)
/// Execute an encoding function on all values
public static let Encoding = ConversionOptions(rawValue: 32)
// Just for bein able to show all
public static var All: ConversionOptions = [PropertyConverter, PropertyMapping, SkipPropertyValue, KeyCleanup, Decoding, Encoding]
/// Default used for NSCoding
public static var DefaultNSCoding: ConversionOptions = [None]
/// Default used for comparing / hashing functions
public static var DefaultComparing: ConversionOptions = [PropertyConverter, PropertyMapping, SkipPropertyValue]
/// Default used for deserialization
public static var DefaultDeserialize: ConversionOptions = [PropertyConverter, PropertyMapping, SkipPropertyValue, KeyCleanup, Decoding]
/// Default used for serialization
public static var DefaultSerialize: ConversionOptions = [PropertyConverter, PropertyMapping, SkipPropertyValue, Encoding]
/// Get a nice description of the ConversionOptions
public var description: String {
let strings = ["PropertyConverter", "PropertyMapping", "SkipPropertyValue", "KeyCleanup", "Decoding", "Encoding"]
var members = [String]()
for (flag, string) in strings.enumerated() where contains(ConversionOptions(rawValue:1<<(flag + 1))) {
members.append(string)
}
if members.count == 0 {
members.append("None")
}
return members.description
}
}
//
// DeserializationStatus.swift
// EVReflection
//
// Created by Edwin Vermeer on 9/5/16.
// Copyright © 2015 evict. All rights reserved.
//
/**
Type of status messages after deserialization
*/
public struct DeserializationStatus: OptionSet, CustomStringConvertible {
/// The numeric representation of the options
public let rawValue: Int
/**
Initialize with a raw value
- parameter rawValue: the numeric representation
- returns: the DeserializationStatus
*/
public init(rawValue: Int) { self.rawValue = rawValue }
/// No status message
public static let None = DeserializationStatus(rawValue: 0)
/// Incorrect key error
public static let IncorrectKey = DeserializationStatus(rawValue: 1)
/// Missing key error
public static let MissingKey = DeserializationStatus(rawValue: 2)
/// Invalid type error
public static let InvalidType = DeserializationStatus(rawValue: 4)
/// Invalid value error
public static let InvalidValue = DeserializationStatus(rawValue: 8)
/// Invalid class error
public static let InvalidClass = DeserializationStatus(rawValue: 16)
/// Missing protocol error
public static let MissingProtocol = DeserializationStatus(rawValue: 32)
/// Custom status message
public static let Custom = DeserializationStatus(rawValue: 64)
/// Get a nice description of the DeserializationStatus
public var description: String {
let strings = ["IncorrectKey", "MissingKey", "InvalidType", "InvalidValue", "InvalidClass", "MissingProtocol", "Custom"]
var members = [String]()
for (flag, string) in strings.enumerated() where contains(DeserializationStatus(rawValue:1<<(flag))) {
members.append(string)
}
if members.count == 0 {
members.append("None")
}
return members.description
}
}
//
// ArrayExtension.swift
// EVReflection
//
// Created by Edwin Vermeer on 9/2/15.
// Copyright © 2015 evict. All rights reserved.
//
import Foundation
/**
Extending Array with an some EVReflection functions where the elements can be of type NSObject
*/
public extension Array where Element: NSObject {
/**
Initialize an array based on a json string
- parameter json: The json string
- parameter conversionOptions: Option set for the various conversion options.
*/
init(json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize, forKeyPath: String? = nil) {
self.init()
let arrayTypeInstance = getArrayTypeInstance(self)
let newArray = EVReflection.arrayFromJson(type: arrayTypeInstance, json: json, conversionOptions: conversionOptions, forKeyPath: forKeyPath)
for item in newArray {
self.append(item)
}
}
/**
Initialize an array based on a json string
- parameter json: The json string
- parameter conversionOptions: Option set for the various conversion options.
*/
init(data: Data?, conversionOptions: ConversionOptions = .DefaultDeserialize, forKeyPath: String? = nil) {
self.init()
let arrayTypeInstance = getArrayTypeInstance(self)
let newArray = EVReflection.arrayFromData(nil, type:arrayTypeInstance, data: data, conversionOptions: conversionOptions, forKeyPath: forKeyPath)
for item in newArray {
self.append(item)
}
}
/**
Initialize an array based on a dictionary
- parameter json: The json string
- parameter conversionOptions: Option set for the various conversion options.
*/
init(dictionaryArray: [NSDictionary], conversionOptions: ConversionOptions = .DefaultDeserialize) {
self.init()
for item in dictionaryArray {
let arrayTypeInstance = getArrayTypeInstance(self)
EVReflection.setPropertiesfromDictionary(item, anyObject: arrayTypeInstance)
self.append(arrayTypeInstance)
}
}
/**
Initialize an array based on a dictionary
- parameter json: The json string
- parameter conversionOptions: Option set for the various conversion options.
*/
init(dictionary: NSDictionary, forKeyPath: String, conversionOptions: ConversionOptions = .DefaultDeserialize) {
self.init()
guard let dictionaryArray = dictionary.value(forKeyPath: forKeyPath) as? [NSDictionary] else {
evPrint(.UnknownKeypath, "ERROR: The forKeyPath '\(forKeyPath)' resulted in an empty array")
return
}
for item in dictionaryArray {
let arrayTypeInstance = getArrayTypeInstance(self)
EVReflection.setPropertiesfromDictionary(item, anyObject: arrayTypeInstance)
self.append(arrayTypeInstance)
}
}
/**
Get the type of the object where this array is for
- parameter arr: this array
- returns: The object type
*/
func getArrayTypeInstance<T: NSObject>(_ arr: Array<T>) -> T {
return arr.getTypeInstance()
}
/**
Get the type of the object where this array is for
- returns: The object type
*/
func getTypeInstance<T: NSObject>(
) -> T {
let nsobjectype: NSObject.Type = T.self
let nsobject: NSObject = nsobjectype.init()
if let obj = nsobject as? T {
return obj
}
// Could not instantiate array item instance.
return T()
}
/**
Get the string representation of the type of the object where this array is for
- returns: The object type
*/
func getTypeAsString() -> String {
let item = self.getTypeInstance()
return NSStringFromClass(type(of:item))
}
}
/**
Extending Array with an some EVReflection functions where the elements can be of type EVReflectable
*/
public extension Array where Element: EVReflectable {
/**
Convert this array to a json string
- parameter conversionOptions: Option set for the various conversion options.
- parameter prettyPrinted: Define if you want enters and indents
- returns: The json string
*/
func toJsonString(_ conversionOptions: ConversionOptions = .DefaultSerialize, prettyPrinted: Bool = false) -> String {
return "[\n" + self.map({($0).toJsonString(conversionOptions, prettyPrinted: prettyPrinted)}).joined(separator: ", \n") + "\n]"
}
/**
Convert this array to a json data
- parameter conversionOptions: Option set for the various conversion options.
- parameter prettyPrinted: Define if you want enters and indents
- parameter encoding: The string encoding defaulsts to .utf8
- returns: The json data
*/
func toJsonData(_ conversionOptions: ConversionOptions = .DefaultSerialize, prettyPrinted: Bool = false, encoding: String.Encoding = .utf8) -> Data {
return self.toJsonString(conversionOptions, prettyPrinted: prettyPrinted).data(using: encoding) ?? Data()
}
/**
Returns the dictionary representation of this array.
- parameter conversionOptions: Option set for the various conversion options.
- returns: The array of dictionaries
*/
func toDictionaryArray(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> NSArray {
return self.map({($0).toDictionary(conversionOptions)}) as NSArray
}
}
/**
Extending Array with an some EVReflection functions where the elements can be of type NSObject
*/
public extension Array where Element: NSDictionary {
/**
Initialize a dictionary array based on a json string
- parameter json: The json string
*/
init(jsonArray: String) {
self.init()
let dictArray = EVReflection.dictionaryArrayFromJson(jsonArray)
for item in dictArray {
self.append(item as! Element)
}
}
/**
Initialize a dictionary array based on a json string
- parameter json: The json string
*/
init(dataArray: Data) {
self.init(jsonArray: String(data: dataArray, encoding: .utf8) ?? "")
}
/**
Convert this array to a json string
- parameter conversionOptions: Option set for the various conversion options.
- returns: The json string
*/
func toJsonStringArray(prettyPrinted: Bool = false) -> String {
let jsonArray: [String] = self.map { ($0 as NSDictionary).toJsonString(prettyPrinted: prettyPrinted) as String }
return "[\n" + jsonArray.joined(separator: ", \n") + "\n]"
}
}
public extension NSArray {
func nestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[T]] {
return (self.map {
(($0 as? NSArray)?.map {
element($0 as? NSDictionary ?? NSDictionary())
}) ?? []
})
}
func doubleNestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[[T]]] {
return (self.map {
(($0 as? NSArray)?.nestedArrayMap { element($0) }) ?? [[]]
})
}
func tripleNestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[[[T]]]] {
return (self.map {
(($0 as? NSArray)?.doubleNestedArrayMap { element($0) }) ?? [[[]]]
})
}
func quadrupleNestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[[[[T]]]]] {
return (self.map {
(($0 as? NSArray)?.tripleNestedArrayMap { element($0) }) ?? [[[[]]]]
})
}
func quintupleNestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[[[[[T]]]]]] {
return (self.map {
(($0 as? NSArray)?.quadrupleNestedArrayMap { element($0) }) ?? [[[[[]]]]]
})
}
func sextupleNestedArrayMap<T>(_ element: (NSDictionary)->T) -> [[[[[[[T]]]]]]] {
return (self.map {
(($0 as? NSArray)?.quintupleNestedArrayMap { element($0) }) ?? [[[[[[]]]]]]
})
}
// If you need deeper nesting, whell, then you probably see the pattern above that you need to implement :-)
// just name them septuple, octuple, nonuple and decuple
// I'm not sure how far swift can handle it, but you should not want something like that.
}
//
// EVCustomReflectable.swift
// EVReflection
//
// Created by Edwin Vermeer on 27/10/2016.
// Copyright © 2016 evict. All rights reserved.
//
import Foundation
// Protocol that can be used for sub objects to define that parsing will be done in the parent using the 'setValue forKey' function
public protocol EVCustomReflectable {
static func constructWith(value: Any?) -> EVCustomReflectable?
func constructWith(value: Any?) -> EVCustomReflectable?
func toCodableValue() -> Any
}
//
// DictionaryExtension.swift
// EVReflection
//
// Created by Edwin Vermeer on 9/2/15.
// Copyright © 2015 evict. All rights reserved.
//
import Foundation
/**
Dictionary extension for creating a json strin from an array of enum values
*/
public extension NSMutableDictionary {
/**
Initialize a Dictionary based on a json string
*/
convenience init(json: String) {
self.init()
let jsonDict = EVReflection.dictionaryFromJson(json)
for (key, value) in jsonDict {
self[key] = value
}
}
/**
Initialize a Dictionary based on json data
*/
convenience init(data: Data) {
self.init(json: String(data: data, encoding: .utf8) ?? "")
}
}
public extension NSDictionary {
/**
Create a json string based on this dictionary
- parameter prettyPrinted: compact of pretty printed
*/
func toJsonString(prettyPrinted: Bool = false) -> String {
let data = self.toJsonData(prettyPrinted: prettyPrinted)
return String(data: data, encoding: .utf8) ?? ""
}
/**
Create a json data based on this dictionary
- parameter prettyPrinted: compact of pretty printed
*/
func toJsonData(prettyPrinted: Bool = false) -> Data {
do {
if prettyPrinted {
return try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
}
return try JSONSerialization.data(withJSONObject: self, options: [])
} catch { }
return Data()
}
}
public extension NSMutableDictionary {
/**
Merge a 2nd dictionary into this one
- parameter dictionary: The 2nd dictionary that will be merged into this one
*/
func unionInPlace(dictionary: NSDictionary) {
for (key, value) in dictionary {
self[key] = value
}
}
/**
Merge a sequence into this dictionary
- parameter dictionary: The sequence that will be merged into this dictionary
*/
func unionInPlace<S: Sequence>(sequence: S) where
S.Iterator.Element == (Key,Value) {
for (key, value) in sequence {
self[key] = value
}
}
}
//
// EVObject.swift
//
// Created by Edwin Vermeer on 5/2/15.
// Copyright (c) 2015 evict. All rights reserved.
//
import Foundation
/**
Object that implements EVReflectable and NSCoding. Use this object as your base class
instead of NSObject and you wil automatically have support for all these protocols.
*/
@objcMembers
open class EVObject: NSObject, NSCoding, EVReflectable {
// These are redundant in Swift 2+: CustomDebugStringConvertible, CustomStringConvertible, Hashable, Equatable
/**
Implementation of the setValue forUndefinedKey so that we can catch exceptions for when we use an optional Type like Int? in our object. Instead of using Int? you should use NSNumber?
This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector
- parameter value: The value that you wanted to set
- parameter key: The name of the property that you wanted to set
*/
open override func setValue(_ value: Any!, forUndefinedKey key: String) {
if let kvc = self as? EVGenericsKVC {
kvc.setGenericValue(value as AnyObject?, forUndefinedKey: key)
} else {
self.addStatusMessage(.IncorrectKey, message: "The class '\(EVReflection.swiftStringFromClass(self))' is not key value coding-compliant for the key '\(key)'")
evPrint(.IncorrectKey, "\nWARNING: The class '\(EVReflection.swiftStringFromClass(self))' is not key value coding-compliant for the key '\(key)'\n There is no support for optional type, array of optionals or enum properties.\nAs a workaround you can implement the function 'setValue forUndefinedKey' for this. See the unit tests for more information\n")
}
}
/**
Implementation of the NSObject isEqual comparisson method
This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector
- parameter object: The object where you want to compare with
- returns: Returns true if the object is the same otherwise false
*/
open override func isEqual(_ object: Any?) -> Bool { // for isEqual:
if let obj = object as? EVObject {
return EVReflection.areEqual(self, rhs: obj)
}
return false
}
/**
Returns the pritty description of this object
- returns: The pritty description
*/
open override var description: String {
get {
return EVReflection.description(self, prettyPrinted: true)
}
}
/**
Returns the pritty description of this object
- returns: The pritty description
*/
open override var debugDescription: String {
get {
return EVReflection.description(self, prettyPrinted: true)
}
}
/**
This basic init override is needed so we can use EVObject as a base class.
*/
public required override init() {
super.init()
}
/**
Decode any object
This method is in EVObject and not in NSObject because you would get the error: Initializer requirement 'init(coder:)' can
only be satisfied by a `required` initializer in the definition of non-final class 'NSObject'
-parameter coder: The NSCoder that will be used for decoding the object.
*/
public convenience required init?(coder: NSCoder) {
self.init()
EVReflection.decodeObjectWithCoder(self, aDecoder: coder, conversionOptions: .DefaultNSCoding)
}
/**
Encode this object using a NSCoder
- parameter aCoder: The NSCoder that will be used for encoding the object
*/
open func encode(with aCoder: NSCoder) {
EVReflection.encodeWithCoder(self , aCoder: aCoder, conversionOptions: .DefaultNSCoding)
}
//MARK - Default implementation of protocol functions that we can override
/**
By default there is no aditional validation. Override this function to add your own class level validation rules
- parameter dict: The dictionary with keys where the initialisation is called with
*/
open func initValidation(_ dict: NSDictionary) {
}
/**
Override this method when you want custom property mapping.
This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet
- returns: Return an array with value pairs of the object property name and json key name.
*/
open func propertyMapping() -> [(keyInObject: String?, keyInResource: String?)] {
return []
}
/**
Override this method when you want custom property value conversion
This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet
- returns: Returns an array where each item is a combination of the folowing 3 values: A string for the property name where the custom conversion is for, a setter function and a getter function.
*/
open func propertyConverters() -> [(key: String, decodeConverter: ((Any?)->()), encodeConverter: (() -> Any?))] {
return []
}
/**
You can add general value decoding to an object when you implement this function. You can for instance use it to base64 decode, url decode, html decode, unicode, etc.
- parameter value: The value that we will be decoded
- parameter key: The key for the value
- returns: The decoded value
*/
open func decodePropertyValue(value: Any, key: String) -> Any? {
return value
}
/**
You can add general value encoding to an object when you implement this function. You can for instance use it to base64 encode, url encode, html encode, unicode, etc.
- parameter value: The value that we will be encoded
- parameter key: The key for the value
- returns: The encoded value.
*/
open func encodePropertyValue(value: Any, key: String) -> Any {
return value
}
/**
This is a general functon where you can filter for specific values (like nil or empty string) when creating a dictionary
- parameter value: The value that we will test
- parameter key: The key for the value
- returns: True if the value needs to be ignored.
*/
open func skipPropertyValue(_ value: Any, key: String) -> Bool {
return false
}
/**
When a property is declared as a base type for multiple inherited classes, then this function will let you pick the right specific type based on the suplied dictionary.
- parameter dict: The dictionary for the specific type
- returns: The specific type
*/
open func getSpecificType(_ dict: NSDictionary) -> EVReflectable? {
return nil
}
/**
Return a custom object for the object
- returns: The custom object (single value, dictionary or array)
*/
open func customConverter() -> AnyObject? {
return nil
}
}
This diff is collapsed.
This diff is collapsed.
//
// EVWorkaroundHelpers.swift
// EVReflection
//
// Created by Edwin Vermeer on 2/7/16.
// Copyright © 2016 evict. All rights reserved.
//
import Foundation
/**
Protocol for the workaround when using generics. See WorkaroundSwiftGenericsTests.swift
*/
public protocol EVGenericsKVC {
/**
Implement this protocol in a class with generic properties so that we can still use a standard mechanism for setting property values.
*/
func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String)
/**
Add a function so that we can get an instance of T
*/
func getGenericType() -> NSObject
}
/**
Protocol for the workaround when using an enum with a rawValue of an undefined type
*/
public protocol EVRaw {
/**
For implementing a function that will return the rawValue for a non sepecific enum
*/
var anyRawValue: Any { get }
}
/**
Default implementation for getting the rawValue for any other type
*/
public extension EVRaw where Self: RawRepresentable {
var anyRawValue: Any {
get {
return rawValue as Any
}
}
}
/**
Protocol for the workaround when using an array with nullable values
*/
public protocol EVArrayConvertable {
/**
For implementing a function for converting a generic array to a specific array.
*/
func convertArray(_ key: String, array: Any) -> NSArray
}
/**
Add a property to an enum to get the associated value
*/
public protocol EVAssociated {
}
/**
The implrementation of the protocol for getting the associated value
*/
public extension EVAssociated {
/**
Easy access to the associated value of an enum.
:returns: The label of the enum plus the associated value
*/
var associated: (label: String, value: Any?, values: [Any]) {
get {
let mirror = Mirror(reflecting: self)
if mirror.displayStyle == .enum {
if let associated = mirror.children.first {
let values = Mirror(reflecting: associated.value).children
var valuesArray = [Any]()
for item in values {
valuesArray.append(item.value)
}
return (associated.label!, associated.value, valuesArray)
}
print("WARNING: Enum option of \(self) does not have an associated value")
return ("\(self)", nil, [])
}
print("WARNING: You can only extend an enum with the EnumExtension")
return ("\(self)", nil, [])
}
}
}
/**
Dictionary extension for creating a dictionary from an array of enum values
*/
public extension Dictionary {
/**
Create a dictionairy based on all associated values of an enum array
- parameter associated: array of dictionairy values which have an associated value
*/
init<T: EVAssociated>(associated: [T]?) {
self.init()
if associated != nil {
for myEnum in associated! {
self[(myEnum.associated.label as? Key)!] = myEnum.associated.value as? Value
}
}
}
}
//
// PrintOptions.swift
// EVReflection
//
// Created by Edwin Vermeer on 9/5/16.
// Copyright © 2015 evict. All rights reserved.
//
/**
For specifying what should be printed
*/
public struct PrintOptions: OptionSet, CustomStringConvertible {
/// The numeric representation of the options
public let rawValue: Int
/**
Initialize with a raw value
- parameter rawValue: the numeric representation
- returns: The Print options
*/
public init(rawValue: Int) { self.rawValue = rawValue }
/// No print
public static let None = PrintOptions(rawValue: 0)
/// print array init uknown keypath
public static let UnknownKeypath = PrintOptions(rawValue: 1)
/// print EIncorrectKey
public static let IncorrectKey = PrintOptions(rawValue: 2)
/// print should extend an NSObject
public static let ShouldExtendNSObject = PrintOptions(rawValue: 4)
/// print invalid json
public static let IsInvalidJson = PrintOptions(rawValue: 8)
/// print Missing protocol error
public static let MissingProtocol = PrintOptions(rawValue: 16)
/// print Missing key error
public static let MissingKey = PrintOptions(rawValue: 32)
/// print Invalid type error
public static let InvalidType = PrintOptions(rawValue: 64)
/// print Invalid value error
public static let InvalidValue = PrintOptions(rawValue: 128)
/// print Invalid class error
public static let InvalidClass = PrintOptions(rawValue: 256)
/// print enum without associated value
public static let EnumWithoutAssociatedValue = PrintOptions(rawValue: 512)
/// print enum without associated value
public static let UseWorkaround = PrintOptions(rawValue: 1024)
/// All the options
public static var All: PrintOptions = [UnknownKeypath, IncorrectKey, ShouldExtendNSObject, IsInvalidJson, MissingProtocol, MissingKey, InvalidType, InvalidValue, InvalidClass, EnumWithoutAssociatedValue, UseWorkaround]
/// The active print options
public static var Active: PrintOptions = All
/// Get a nice description of the PrintOptions
public var description: String {
let strings = ["UnknownKeypath", "IncorrectKey", "ShouldExtendNSObject", "IsInvalidJson", "MissingProtocol", "MissingKey", "InvalidType", "InvalidValue", "InvalidClass", "EnumWithoutAssociatedValue", "UseWorkaround"]
var members = [String]()
for (flag, string) in strings.enumerated() where contains(PrintOptions(rawValue:1<<(flag + 1))) {
members.append(string)
}
if members.count == 0 {
members.append("None")
}
return members.description
}
}
public func evPrint(_ options: PrintOptions, _ value: String) {
if PrintOptions.Active.contains(options) {
print("🌀 \(value)")
}
}
//
// Event.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 21/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
// Events are implemented as structs, what has both advantages and disadvantages
// Notably they are copied when inside other value types, and mutated on add/remove/notify
// If you require a reference type for Event, use EventReference<T> instead
/// A struct representing a collection of subscriptions with means to add, remove and notify them.
public struct Event<T>: UnownableEvent {
public typealias ValueType = T
public typealias SubscriptionType = EventSubscription<T>
public typealias HandlerType = SubscriptionType.HandlerType
public private(set) var subscriptions = [SubscriptionType]()
public init() { }
public mutating func notify(_ value: T) {
subscriptions = subscriptions.filter { $0.valid() }
for subscription in subscriptions {
subscription.handler(value)
}
}
@discardableResult
public mutating func add(_ subscription: SubscriptionType) -> SubscriptionType {
subscriptions.append(subscription)
return subscription
}
@discardableResult
public mutating func add(_ handler: @escaping HandlerType) -> SubscriptionType {
return add(SubscriptionType(owner: nil, handler: handler))
}
public mutating func remove(_ subscription: SubscriptionType) {
var newsubscriptions = [SubscriptionType]()
var first = true
for existing in subscriptions {
if first && existing === subscription {
first = false
} else {
newsubscriptions.append(existing)
}
}
subscriptions = newsubscriptions
}
public mutating func removeAll() {
subscriptions.removeAll()
}
@discardableResult
public mutating func add(owner: AnyObject, _ handler: @escaping HandlerType) -> SubscriptionType {
return add(SubscriptionType(owner: owner, handler: handler))
}
public mutating func unshare() {
// _subscriptions.unshare()
}
}
@discardableResult
public func += <T: UnownableEvent> (event: inout T, handler: @escaping (T.ValueType) -> ()) -> EventSubscription<T.ValueType> {
return event.add(handler)
}
@discardableResult
public func += <T: OwnableEvent> (event: T, handler: @escaping (T.ValueType) -> ()) -> EventSubscription<T.ValueType> {
var e = event
return e.add(handler)
}
public func -= <T: UnownableEvent> (event: inout T, subscription: EventSubscription<T.ValueType>) {
return event.remove(subscription)
}
public func -= <T: OwnableEvent> (event: T, subscription: EventSubscription<T.ValueType>) {
var e = event
return e.remove(subscription)
}
//
// EventReference.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 23/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
/// A class enclosing an Event struct. Thus exposing it as a reference type.
open class EventReference<T>: OwnableEvent {
public typealias ValueType = T
public typealias SubscriptionType = EventSubscription<T>
public typealias HandlerType = EventSubscription<T>.HandlerType
public private(set) var event: Event<T>
open func notify(_ value: T) {
event.notify(value)
}
@discardableResult
open func add(_ subscription: SubscriptionType) -> SubscriptionType {
return event.add(subscription)
}
@discardableResult
open func add(_ handler: @escaping (T) -> ()) -> EventSubscription<T> {
return event.add(handler)
}
open func remove(_ subscription: SubscriptionType) {
return event.remove(subscription)
}
open func removeAll() {
event.removeAll()
}
@discardableResult
open func add(owner: AnyObject, _ handler: @escaping HandlerType) -> SubscriptionType {
return event.add(owner: owner, handler)
}
public convenience init() {
self.init(event: Event<T>())
}
public init(event: Event<T>) {
self.event = event
}
}
//
// EventSubscription.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 21/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
// Implemented as a class, so it can be compared using === and !==.
// There is no way for event to get notified when the owner was deallocated,
// therefore it will be invalidated only upon next attempt to trigger.
// Event subscriptions are neither freed nor removed from events upon invalidation.
// Events remove invalidated subscriptions themselves when firing.
// Invalidation immediately frees handler and owned objects.
/// A class representing a subscription for `Event<T>`.
public class EventSubscription<T> {
public typealias HandlerType = (T) -> ()
private var _valid: () -> Bool
/// Handler to be caled when value changes.
public private(set) var handler: HandlerType
/// array of owned objects
private var _owned = [AnyObject]()
/// When invalid subscription is to be notified, it is removed instead.
public func valid() -> Bool {
if !_valid() {
invalidate()
return false
} else {
return true
}
}
/// Marks the event for removal, frees the handler and owned objects
public func invalidate() {
_valid = { false }
handler = { _ in () }
_owned = []
}
/// Init with a handler and an optional owner.
/// If owner is present, valid() is tied to its lifetime.
public init(owner o: AnyObject?, handler h: @escaping HandlerType) {
if o == nil {
_valid = { true }
} else {
_valid = { [weak o] in o != nil }
}
handler = h
}
/// Add an object to be owned while the event is not invalidated
public func addOwnedObject(_ o: AnyObject) {
_owned.append(o)
}
/// Remove object from owned objects
public func removeOwnedObject(_ o: AnyObject) {
_owned = _owned.filter{ $0 !== o }
}
}
//
// Observable.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 20/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
/// A struct representing information associated with value change event.
public struct ValueChange<T> {
public let oldValue: T
public let newValue: T
public init(_ o: T, _ n: T) {
oldValue = o
newValue = n
}
}
// Implemented as a struct in order to have desired value and mutability sementics.
/// A struct representing an observable value.
public struct Observable<T>: UnownableObservable {
public typealias ValueType = T
public private(set) var beforeChange = EventReference<ValueChange<T>>()
public private(set) var afterChange = EventReference<ValueChange<T>>()
public var value : T {
willSet { beforeChange.notify(ValueChange(value, newValue)) }
didSet { afterChange.notify(ValueChange(oldValue, value)) }
}
public mutating func unshare(removeSubscriptions: Bool) {
if removeSubscriptions {
beforeChange = EventReference<ValueChange<T>>()
afterChange = EventReference<ValueChange<T>>()
} else {
var beforeEvent = beforeChange.event
beforeEvent.unshare()
beforeChange = EventReference<ValueChange<T>>(event: beforeEvent)
var afterEvent = afterChange.event
afterEvent.unshare()
afterChange = EventReference<ValueChange<T>>(event: afterEvent)
}
}
public init(_ v : T) {
value = v
}
}
//
// Chaining.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 23/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
public class ObservableChainingProxy<O1: AnyObservable, O2: AnyObservable>: OwnableObservable {
public typealias ValueType = O2.ValueType?
public var value: ValueType { return nil }
private weak var _beforeChange: EventReference<ValueChange<ValueType>>? = nil
private weak var _afterChange: EventReference<ValueChange<ValueType>>? = nil
public var beforeChange: EventReference<ValueChange<ValueType>> {
if let event = _beforeChange {
return event
} else {
let event = OwningEventReference<ValueChange<ValueType>>()
event.owned = self
_beforeChange = event
return event
}
}
public var afterChange: EventReference<ValueChange<ValueType>> {
if let event = _afterChange {
return event
} else {
let event = OwningEventReference<ValueChange<ValueType>>()
event.owned = self
_afterChange = event
return event
}
}
private let base: O1
private let path: (O1.ValueType) -> O2?
private func targetChangeToValueChange(_ vc: ValueChange<O2.ValueType>) -> ValueChange<ValueType> {
let oldValue = Optional.some(vc.oldValue)
let newValue = Optional.some(vc.newValue)
return ValueChange(oldValue, newValue)
}
private func objectChangeToValueChange(_ oc: ValueChange<O1.ValueType>) -> ValueChange<ValueType> {
let oldValue = path(oc.oldValue)?.value
let newValue = path(oc.newValue)?.value
return ValueChange(oldValue, newValue)
}
init(base: O1, path: @escaping (O1.ValueType) -> O2?) {
self.base = base
self.path = path
let beforeSubscription = EventSubscription(owner: self) { [weak self] in
self!.beforeChange.notify(self!.targetChangeToValueChange($0))
}
let afterSubscription = EventSubscription(owner: self) { [weak self] in
self!.afterChange.notify(self!.targetChangeToValueChange($0))
}
base.beforeChange.add(owner: self) { [weak self] oc in
let oldTarget = path(oc.oldValue)
oldTarget?.beforeChange.remove(beforeSubscription)
oldTarget?.afterChange.remove(afterSubscription)
self!.beforeChange.notify(self!.objectChangeToValueChange(oc))
}
base.afterChange.add(owner: self) { [weak self] oc in
self!.afterChange.notify(self!.objectChangeToValueChange(oc))
let newTarget = path(oc.newValue)
newTarget?.beforeChange.add(beforeSubscription)
newTarget?.afterChange.add(afterSubscription)
}
}
public func to<O3: AnyObservable>(path f: @escaping (O2.ValueType) -> O3?) -> ObservableChainingProxy<ObservableChainingProxy<O1, O2>, O3> {
func cascadeNil(_ oOrNil: ValueType) -> O3? {
if let o = oOrNil {
return f(o)
} else {
return nil
}
}
return ObservableChainingProxy<ObservableChainingProxy<O1, O2>, O3>(base: self, path: cascadeNil)
}
public func to<O3: AnyObservable>(path f: @escaping (O2.ValueType) -> O3) -> ObservableChainingProxy<ObservableChainingProxy<O1, O2>, O3> {
func cascadeNil(_ oOrNil: ValueType) -> O3? {
if let o = oOrNil {
return f(o)
} else {
return nil
}
}
return ObservableChainingProxy<ObservableChainingProxy<O1, O2>, O3>(base: self, path: cascadeNil)
}
}
public struct ObservableChainingBase<O1: AnyObservable> {
fileprivate let base: O1
public func to<O2: AnyObservable>(_ path: @escaping (O1.ValueType) -> O2?) -> ObservableChainingProxy<O1, O2> {
return ObservableChainingProxy(base: base, path: path)
}
public func to<O2: AnyObservable>(_ path: @escaping (O1.ValueType) -> O2) -> ObservableChainingProxy<O1, O2> {
return ObservableChainingProxy(base: base, path: { .some(path($0)) })
}
}
public func chain<O: AnyObservable>(_ o: O) -> ObservableChainingBase<O> {
return ObservableChainingBase(base: o)
}
public func / <O1, O2, O3: AnyObservable> (o: ObservableChainingProxy<O1, O2>, f: @escaping (O2.ValueType) -> O3?) -> ObservableChainingProxy<ObservableChainingProxy<O1, O2>, O3> {
return o.to(path: f)
}
public func / <O1: AnyObservable, O2: AnyObservable> (o: O1, f: @escaping (O1.ValueType) -> O2?) -> ObservableChainingProxy<O1, O2> {
return ObservableChainingProxy(base: o, path: f)
}
//
// ObservableProxy.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 24/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
// two generic parameters are needed to be able to override `value` in `ObservableReference<T>`
open class ObservableProxy<T, O: AnyObservable> : OwnableObservable where O.ValueType == T {
public typealias ValueType = T
public private(set) var beforeChange = EventReference<ValueChange<T>>()
public private(set) var afterChange = EventReference<ValueChange<T>>()
// private storage in case subclasses override value with a setter
private var _value: T
open var value: T {
return _value
}
public init(_ o: O) {
self._value = o.value
o.beforeChange.add(owner: self) { [weak self] change in
self!.beforeChange.notify(change)
}
o.afterChange.add(owner: self) { [weak self] change in
let nV = change.newValue
self!._value = nV
self!.afterChange.notify(change)
}
}
}
public func proxy <O: AnyObservable> (_ o: O) -> ObservableProxy<O.ValueType, O> {
return ObservableProxy(o)
}
//
// ObservableReference.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 21/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
public class ObservableReference<T> : ObservableProxy<T, Observable<T>>, WritableObservable {
public typealias ValueType = T
private var storage: Observable<T>
public override var value: T {
get { return storage.value }
set { storage.value = newValue }
}
public init(_ v : T) {
storage = Observable(v)
super.init(storage)
}
}
//
// OwningEventReference.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 28/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
/// A subclass of event reference allowing it to own other object[s].
/// Additionally, the reference makes added events own itself.
/// This retain cycle allows owned objects to live as long as valid subscriptions exist.
public class OwningEventReference<T>: EventReference<T> {
internal var owned: AnyObject? = nil
public override func add(_ subscription: SubscriptionType) -> SubscriptionType {
let subscr = super.add(subscription)
if owned != nil {
subscr.addOwnedObject(self)
}
return subscr
}
public override func add(_ handler: @escaping (T) -> ()) -> EventSubscription<T> {
let subscr = super.add(handler)
if owned != nil {
subscr.addOwnedObject(self)
}
return subscr
}
public override func remove(_ subscription: SubscriptionType) {
subscription.removeOwnedObject(self)
super.remove(subscription)
}
public override func removeAll() {
for subscription in event.subscriptions {
subscription.removeOwnedObject(self)
}
super.removeAll()
}
public override func add(owner: AnyObject, _ handler: @escaping HandlerType) -> SubscriptionType {
let subscr = super.add(owner: owner, handler)
if owned != nil {
subscr.addOwnedObject(self)
}
return subscr
}
public override init(event: Event<T>) {
super.init(event: event)
}
}
//
// Protocols.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 21/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
/// Arbitrary Event.
public protocol AnyEvent {
associatedtype ValueType
/// Notify all valid subscriptions of the change. Remove invalid ones.
mutating func notify(_ value: ValueType)
/// Add an existing subscription.
@discardableResult
mutating func add(_ subscription: EventSubscription<ValueType>) -> EventSubscription<ValueType>
/// Create, add and return a subscription for given handler.
@discardableResult
mutating func add(_ handler : @escaping (ValueType) -> ()) -> EventSubscription<ValueType>
/// Remove given subscription, if present.
mutating func remove(_ subscription : EventSubscription<ValueType>)
/// Remove all subscriptions.
mutating func removeAll()
/// Create, add and return a subscription with given handler and owner.
@discardableResult
mutating func add(owner : AnyObject, _ handler : @escaping (ValueType) -> ()) -> EventSubscription<ValueType>
}
/// Event which is a value type.
public protocol UnownableEvent: AnyEvent { }
/// Event which is a reference type
public protocol OwnableEvent: AnyEvent { }
/// Arbitrary observable.
public protocol AnyObservable {
associatedtype ValueType
/// Value of the observable.
var value: ValueType { get }
/// Event fired before value is changed
var beforeChange: EventReference<ValueChange<ValueType>> { get }
/// Event fired after value is changed
var afterChange: EventReference<ValueChange<ValueType>> { get }
}
/// Observable which can be written to
public protocol WritableObservable: AnyObservable {
var value: ValueType { get set }
}
/// Observable which is a value type. Elementary observables are value types.
public protocol UnownableObservable: WritableObservable {
/// Unshares events
mutating func unshare(removeSubscriptions: Bool)
}
/// Observable which is a reference type. Compound observables are reference types.
public protocol OwnableObservable: AnyObservable {
}
// observable <- value
infix operator <-
// value = observable^
postfix operator ^
// observable ^= value
public func ^= <T : WritableObservable> (x: inout T, y: T.ValueType) {
x.value = y
}
// observable += { valuechange in ... }
@discardableResult
public func += <T : AnyObservable> (x: inout T, y: @escaping (ValueChange<T.ValueType>) -> ()) -> EventSubscription<ValueChange<T.ValueType>> {
return x.afterChange += y
}
// observable += { (old, new) in ... }
@discardableResult
public func += <T : AnyObservable> (x: inout T, y: @escaping (T.ValueType, T.ValueType) -> ()) -> EventSubscription<ValueChange<T.ValueType>> {
return x.afterChange += y
}
// observable += { new in ... }
@discardableResult
public func += <T : AnyObservable> (x: inout T, y: @escaping (T.ValueType) -> ()) -> EventSubscription<ValueChange<T.ValueType>> {
return x.afterChange += y
}
// observable -= subscription
public func -= <T : AnyObservable> (x: inout T, s: EventSubscription<ValueChange<T.ValueType>>) {
x.afterChange.remove(s)
}
// event += { (old, new) in ... }
@discardableResult
public func += <T> (event: EventReference<ValueChange<T>>, handler: @escaping (T, T) -> ()) -> EventSubscription<ValueChange<T>> {
return event.add({ handler($0.oldValue, $0.newValue) })
}
// event += { new in ... }
@discardableResult
public func += <T> (event: EventReference<ValueChange<T>>, handler: @escaping (T) -> ()) -> EventSubscription<ValueChange<T>> {
return event.add({ handler($0.newValue) })
}
// for observable values on variables
public func <- <T : WritableObservable & UnownableObservable> (x: inout T, y: T.ValueType) {
x.value = y
}
// for observable references on variables or constants
public func <- <T : WritableObservable & OwnableObservable> (x: T, y: T.ValueType) {
var z = x
z.value = y
}
public postfix func ^ <T : AnyObservable> (x: T) -> T.ValueType {
return x.value
}
//
// TupleObservable.swift
// Observable-Swift
//
// Created by Leszek Ślażyński on 20/06/14.
// Copyright (c) 2014 Leszek Ślażyński. All rights reserved.
//
public class PairObservable<O1: AnyObservable, O2: AnyObservable> : OwnableObservable {
public typealias T1 = O1.ValueType
public typealias T2 = O2.ValueType
public typealias ValueType = (T1, T2)
public private(set) var beforeChange = EventReference<ValueChange<(T1, T2)>>()
public private(set) var afterChange = EventReference<ValueChange<(T1, T2)>>()
internal var first : T1
internal var second : T2
public var value : (T1, T2) { return (first, second) }
private let _base1 : O1
private let _base2 : O2
public init (_ o1: O1, _ o2: O2) {
_base1 = o1
_base2 = o2
first = o1.value
second = o2.value
o1.beforeChange.add(owner: self) { [weak self] c1 in
let oldV = (c1.oldValue, self!.second)
let newV = (c1.newValue, self!.second)
let change = ValueChange(oldV, newV)
self!.beforeChange.notify(change)
}
o1.afterChange.add(owner: self) { [weak self] c1 in
let nV1 = c1.newValue
self!.first = nV1
let oldV = (c1.oldValue, self!.second)
let newV = (c1.newValue, self!.second)
let change = ValueChange(oldV, newV)
self!.afterChange.notify(change)
}
o2.beforeChange.add(owner: self) { [weak self] c2 in
let oldV = (self!.first, c2.oldValue)
let newV = (self!.first, c2.newValue)
let change = ValueChange(oldV, newV)
self!.beforeChange.notify(change)
}
o2.afterChange.add(owner: self) { [weak self] c2 in
let nV2 = c2.newValue
self!.second = nV2
let oldV = (self!.first, c2.oldValue)
let newV = (self!.first, c2.newValue)
let change = ValueChange(oldV, newV)
self!.afterChange.notify(change)
}
}
}
public func & <O1 : AnyObservable, O2: AnyObservable> (x: O1, y: O2) -> PairObservable<O1, O2> {
return PairObservable(x, y)
}
Copyright (c) 2018 igiu1988 <wangyang@wanmeizhensuo.com>
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.
# GM-Swift-Observable
[![CI Status](http://img.shields.io/travis/igiu1988/GM-Swift-Observable.svg?style=flat)](https://travis-ci.org/igiu1988/GM-Swift-Observable)
[![Version](https://img.shields.io/cocoapods/v/GM-Swift-Observable.svg?style=flat)](http://cocoapods.org/pods/GM-Swift-Observable)
[![License](https://img.shields.io/cocoapods/l/GM-Swift-Observable.svg?style=flat)](http://cocoapods.org/pods/GM-Swift-Observable)
[![Platform](https://img.shields.io/cocoapods/p/GM-Swift-Observable.svg?style=flat)](http://cocoapods.org/pods/GM-Swift-Observable)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
GM-Swift-Observable is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'GM-Swift-Observable'
```
## Author
igiu1988, wangyang@wanmeizhensuo.com
## License
GM-Swift-Observable is available under the MIT license. See the LICENSE file for more info.
//
// GMBaseController.swift
// Gengmei
//
// Created by wangyang on 16/3/21.
// Copyright © 2016年 更美互动信息科技有限公司. All rights reserved.
//
import UIKit
import GMPhobos
import GMKit
private var navigationBarAssociationKey: UInt8 = 0
open class GMBaseController: UIViewController, GMNavigationBarDelegate, GMEmptyViewDelegate {
public var emptyView = GMEmptyView(frame:CGRect(x: 0, y: 0, width: Constant.screenWidth, height: Constant.screenHeight))
/**
默认值为false,表示导航栏自动被WMBaseViewController管理,永远在所有的view最上方。
设置为true时表示因为特殊需求,开发人员需要自己控制导航,并且需要在viewDidLoad中确定navigationBar的位置
*/
public var controlNavigationByYou = false
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
initController()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initController()
}
open func initController() {
// 在 initController 中初始化自定义导航栏有很大好处。至少可以保证视图被push之前就可以访问navigationBar,以配置title等属性
customNavigationBar()
edgesForExtendedLayout = UIRectEdge()
automaticallyAdjustsScrollViewInsets = false
}
open override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.background
if responds(to: #selector(setter: UIViewController.edgesForExtendedLayout)) {
edgesForExtendedLayout = UIRectEdge()
}
addNavigationBar()
hideLeftButtonForRootController()
initReferer()
initRefererLink()
initReferrerTabName()
setupEmptyView()
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 保证即使在loading的时候,仍然可以后退
if !controlNavigationByYou {
view.bringSubview(toFront: navigationBar)
}
}
open override func didMove(toParentViewController parent: UIViewController?) {
// 作为childController使用,但不是被push到 UINavigationController时,隐藏导航栏
super.didMove(toParentViewController: parent)
if parent != nil && !(parent is UINavigationController) {
navigationBar.isHidden = true
}
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 导航栏隐藏在 viewWillAppear 里控制的原因是在viewDidLoad时,有可能 navigationController 与 self 并没有关系
if navigationController != nil {
navigationController!.isNavigationBarHidden = true
}
Phobos.sharedClient().onPVStart(self)
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if !pageName.isEmpty {
Phobos.sharedClient().onPVEnd(self)
}
}
open override var preferredStatusBarStyle: UIStatusBarStyle {
if #available(iOS 13.0, *) {
return UIStatusBarStyle.darkContent
} else {
return UIStatusBarStyle.default
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: - 导航相关
public var navigationBar: GMNavigationBar {
get {
return objc_getAssociatedObject(self, &navigationBarAssociationKey) as! GMNavigationBar
}
set(newValue) {
objc_setAssociatedObject(self, &navigationBarAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
public func customNavigationBar() {
navigationBar = GMNavigationBar()
}
public func addNavigationBar() {
navigationBar.delegate = self
view.addSubview(navigationBar)
}
public func hideLeftButtonForRootController() {
if navigationController != nil && navigationController!.viewControllers.count == 1 {
navigationBar.leftButton.isHidden = true
}
}
/// 对于没有导航栏,但是有backButton的controller,可以使用这个方法创建backButton。坐标需要指定
open func createBackButton(_ imageName: String) -> UIButton {
let button = GMBaseNavigationButton(type: .custom)
button.setImage(UIImage(named: imageName), for: .normal)
button.sizeToFit()
button.adaptiveHotAreaWidth = 70
button.addTarget(self, action: #selector(GMBaseController.backAction(_:)), for: .touchUpInside)
return button
}
open func pushViewController(_ controller: UIViewController) {
navigationController?.pushViewController(controller, animated: true)
}
open func rightButtonClicked(_ button: UIButton) {
}
open func backAction(_ button: UIButton) {
// parentViewController 是 UINavigationController 说明是极有可能是 push 进来的,需要进一步判断
if (parent?.isKind(of: UINavigationController.classForCoder())) != nil {
let navigation = (parent as! UINavigationController)
// 如果 presentingViewController 存在,表示有人 present 自己,再并上条件已经在导航器上rootViewController时,直接 dismiss 就好了
if presentingViewController != nil && navigation.viewControllers.count == 1 {
dismiss(animated: true, completion: nil)
} else {
_ = navigationController?.popViewController(animated: true)
}
} else {
dismiss(animated: true, completion: nil)
}
}
}
@objc extension GMBaseController {
// MARK: - GMEmptyView
@objc open func setupEmptyView() {
emptyView.isHidden = true
emptyView.delegate = self
view.addSubview(emptyView)
emptyView.snp.makeConstraints { (make) in
if let _ = self.parent as? UINavigationController {
make.top.equalTo(UIApplication.shared.statusBarFrame.height)
} else {
make.top.equalTo(0)
}
make.left.right.bottom.equalTo(0)
}
}
@objc open func showEmptyView(_ type: GMEmptyViewType) {
emptyView.type = type
emptyView.isHidden = false
view.bringSubview(toFront: emptyView)
}
@objc open func hideEmptyView() {
emptyView.isHidden = true
}
@objc open func emptyViewDidClickReload() {
if let me = self as? Refreshable {
me.refreshList()
}
}
}
//
// GMBaseNavigationButton.swift
// Pods
//
// Created by wangyang on 2016/10/21.
//
//
import Foundation
public class GMBaseNavigationButton: UIButton {
// 常常导航栏上的切图资源都比较小,所以需要代码来扩大其响应范围
var adaptiveHotAreaWidth: CGFloat = 0
var adaptiveHotAreaHeight: CGFloat = 0
override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let widthDelta = max(adaptiveHotAreaWidth - bounds.size.width, 0)
let heightDelta = max(adaptiveHotAreaHeight - bounds.size.height, 0)
bounds = bounds.insetBy(dx: -0.5 * widthDelta, dy: -0.5 * heightDelta)
return bounds.contains(point)
}
}
//
// GMBaseObject.swift
// GengmeiDoctor
//
// Created by Thierry on 16/4/21.
// Copyright © 2016年 wanmeizhensuo. All rights reserved.
//
import Foundation
import EVReflection
open class GMBaseObject: EVObject {
/**
Override super setValue method, ignore warning
- parameter value:
- parameter key:
*/
override open func setValue(_ value: Any!, forUndefinedKey key: String) {}
override open func propertyMapping() -> [(keyInObject: String?, keyInResource: String?)] {
return [("desc", "description")]
}
}
//
// GMBaseUtil.swift
// Pods
//
// Created by wangyang on 2016/10/21.
//
//
import Foundation
import SnapKit
public extension UIView {
public var snp_safeBottom: ConstraintItem {
if #available(iOS 11.0, *) {
return safeAreaLayoutGuide.snp.bottom
} else {
return snp.bottom
}
}
}
public extension ConstraintMaker {
public func gm_bottom(to superView: UIView, height: CGFloat) {
top.equalTo(superView.snp_safeBottom).offset(-height)
bottom.equalTo(0)
}
}
Copyright (c) 2016 wangyang <wangyang@wanmeizhensuo.com>
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.
# GMBaseSwift
[![CI Status](http://img.shields.io/travis/wangyang/GMBaseSwift.svg?style=flat)](https://travis-ci.org/wangyang/GMBaseSwift)
[![Version](https://img.shields.io/cocoapods/v/GMBaseSwift.svg?style=flat)](http://cocoapods.org/pods/GMBaseSwift)
[![License](https://img.shields.io/cocoapods/l/GMBaseSwift.svg?style=flat)](http://cocoapods.org/pods/GMBaseSwift)
[![Platform](https://img.shields.io/cocoapods/p/GMBaseSwift.svg?style=flat)](http://cocoapods.org/pods/GMBaseSwift)
## Example
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
GMBaseSwift is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod "GMBaseSwift"
```
## Author
wangyang, wangyang@wanmeizhensuo.com
## License
GMBaseSwift is available under the MIT license. See the LICENSE file for more info.
This diff is collapsed.
This diff is collapsed.
//
// NSFileManager+FolderSize.h
// ZhengXing
//
// Created by wangyang on 3/19/15.
// Copyright (c) 2015 Wanmei Creative. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSFileManager (FolderSize)
- (BOOL)getAllocatedSize:(unsigned long long *)size ofDirectoryAtURL:(NSURL *)directoryURL error:(NSError * __autoreleasing *)error;
@end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment