I'm attempting to implement a KVC equivalent is Swift (inspired by David Owens) using reflection.
valueForKey is fairly trivial, using reflection to get all the children names and retrieve the appropriate value. setValueForKey has proved to be quite tricky however, as Swift reflection appears to be read-only (since readwrite would break reflections dogma)
protocol KVC {
var codeables: [String: Any.Type] { get }
mutating func setValue<T>(value: T, forKey key: String)
func getValue<T>(key: String) -> T?
}
extension KVC {
var codeables: [String: Any.Type] {
var dictionary: [String: Any.Type] = [:]
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let label = child.label {
dictionary[label] = Mirror(reflecting: child.value).subjectType
}
}
return dictionary
}
mutating func setValue<T>(value: T, forKey key: String) {
if let valueType = self.codeables[key] where valueType == value.dynamicType {
}
}
func getValue<T>(key: String) -> T? {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let label = child.label, value = child.value as? T where label == key {
return value
}
}
return nil
}
}
Is there anyway in Swift at all to set a dynamic keypath value without using the Objective-C runtime or enforcing that the conformer is a subclass of NSObject? It seems like the answer is no but there are some clever workarounds such as ObjectMapper, although I'm not a fan of the responsibility its on the conformer.
Related
I'm trying to convert a `struct1 to Realm objects right now.
Realm object has same keypath with original struct.
so If I can get all writable keypaths from original struct, it is possible to convert with general method.
public protocol KeyPathListable {
var allKeyPaths:[WritableKeyPath<Self, Any>] { get }
}
extension KeyPathListable {
private subscript(checkedMirrorDescendant key: String) -> Any {
return Mirror(reflecting: self).descendant(key)!
}
var allKeyPaths:[WritableKeyPath<Self, Any>] {
var membersTokeyPaths = [WritableKeyPath<Self,Any>]()
let mirror = Mirror(reflecting: self)
for case (let key?, _) in mirror.children {
if let keyPath = \Self.[checkedMirrorDescendant: key] as? WritableKeyPath<Self, Any> {
membersTokeyPaths.append(keyPath)
}
}
return membersTokeyPaths
}
}
Just found the code snippet above but it returns KeyPath(not WritableKeyPath). I tried to typecast in this case, but it returns nil. Maybe mirror function has problem. Is there any solution for that?
I have some generic code that allows me to read and write various types to the defaults system, e.g. value getters and setters:
var value : T {
get {
if T.self == Int.self {
return UserDefaults.standard.integer(forKey: storageKey) as! T
} else if T.self == Double.self {
return UserDefaults.standard.double(forKey: storageKey) as! T
} else if T.self == Float.self {
return UserDefaults.standard.float(forKey: storageKey) as! T
} else if T.self == Bool.self {
return UserDefaults.standard.bool(forKey: storageKey) as! T
} else if T.self == String.self {
return UserDefaults.standard.string(forKey: storageKey) as! T
} else {
return UserDefaults.standard.value(forKey: self.storageKey) as! T
}
}
set(value) {
UserDefaults.standard.set(value, forKey: storageKey)
UserDefaults.standard.synchronize()
}
}
Now I want to add my own enum types to this mechanism by making them RawRepresentable<Int>, e.g.
enum Direction : Int, RawRepresentable {
case left = 0
case right = 1
}
Unfortunately, I can neither find the magic incantation to test whether T conforms to the RawRepresentable protocol, nor can I cast T to the RawRepresentable protocol, because no matter what I try, I always end up with a Protocol 'RawRepresentable' can only be used as a generic constraint because it has Self or associated type requirements.
I have tried every where and as incantation until I have started doubting that it can be done at all!?
I'm in Swift 5 and the goal is to create new instance by invoking CustomType(rawValue:) and getting the Int value by calling myValue.rawValue.
As #vadian said, all those type checks can be replaced be a single call to UserDefaults.standard.object() and conditional casting. Also the type of the value property needs to be an optional to handle the case where the property is not set (or not of the correct type):
struct DefaultKey<T> {
let storageKey: String
var value: T? {
get {
return UserDefaults.standard.object(forKey: storageKey) as? T
}
nonmutating set {
UserDefaults.standard.set(newValue, forKey: storageKey)
}
}
}
And then you can define a constrained extension method where you specialize the computed property for the case of RawRepresentable types:
extension DefaultKey where T: RawRepresentable {
var value: T? {
get {
if let rawValue = UserDefaults.standard.object(forKey: storageKey) as? T.RawValue {
return T(rawValue: rawValue)
}
return nil
}
nonmutating set {
UserDefaults.standard.set(newValue?.rawValue, forKey: storageKey)
}
}
}
Example usage:
enum Direction : Int {
case left = 0
case right = 1
}
let key1 = DefaultKey<Int>(storageKey: "foo")
key1.value = 123
let key2 = DefaultKey<Direction>(storageKey: "bar")
key2.value = .right
print(key1.value as Any) // Optional(123)
print(key2.value as Any) // Optional(Direction.right)
Note that this can still crash if used with non-property-list types. To be on the safe side, you would have to restrict the extensions to types which are known to be user defaults storable (integers, floats, strings, ...):
protocol UserDefaultsStorable {}
extension Int: UserDefaultsStorable {}
extension Float: UserDefaultsStorable {}
// ...
struct DefaultKey<T> {
let storageKey: String
}
extension DefaultKey where T: UserDefaultsStorable { .. }
extension DefaultKey where T: RawRepresentable, T.RawValue: UserDefaultsStorable { ... }
I'm trying to get all the members of a generic class T, I can get the properties based on a specific class.
But, how I can do it using Mirror ?
let mirrored_object = Mirror(reflecting: user)
for (index, attr) in mirrored_object.children.enumerated() {
if let propertyName = attr.label as String! {
print("Attr \(index): \(propertyName) = \(attr.value)")
}
}
I added this as extension
extension NSObject {
public func GetAsJson() -> [[String:Any?]] {
var result:[[String: Any?]] = [[String: Any?]]()
for item in self {
var dict: [String: Any?] = [:]
for property in Mirror(reflecting: self).children {
dict[property.label!] = property.value
}
result.append(dict)
}
return result
}
}
How can you check if a type is Optional in Swift?
Say I have a variable of type PartialKeyPath where:
struct Foo {
let bar: String
let baz: String?
}
typealias Property<Root> = (key: PartialKeyPath<Root>, value: Any?)
typealias Properties<Root> = [Property<Root>]
Now say I iterate thru an instance of Properties:
properties.forEach { prop in
let valueType1 = type(of: prop.key).valueType
let valueType2 = type(of: value)
...
How can I check here whether valueType1 is Optional<valueType2>, or whether it is Optional of any other flavor for that matter?
So far the only way I’ve found is really ugly...
Using a similar approach to Optional field type doesn't conform protocol in Swift 3, you could define a 'dummy protocol' for Optional and use this to get the wrapped metatype:
protocol OptionalProtocol {
// the metatype value for the wrapped type.
static var wrappedType: Any.Type { get }
}
extension Optional : OptionalProtocol {
static var wrappedType: Any.Type { return Wrapped.self }
}
If you just want to know a type is an optional:
func isOptionalType(_ type: Any.Type) -> Bool {
return type is OptionalProtocol.Type
}
print(isOptionalType(String.self)) // false
print(isOptionalType(String?.self)) // true
If you want to check if one metatype is the 'optional version' of another metatype:
struct Foo {
let bar: String
let baz: String?
}
struct Property<Root> {
var key: PartialKeyPath<Root>
var value: Any
}
let properties = [Property(key: \Foo.baz, value: "hello")]
/// Attempt to get the `Wrapped` metatype from a metatype of an
/// `Optional<Wrapped>`. If not an `Optional`, will return `nil`.
func wrappedTypeFromOptionalType(_ type: Any.Type) -> Any.Type? {
return (type as? OptionalProtocol.Type)?.wrappedType
}
for property in properties {
let valueType1 = type(of: property.key).valueType
let valueType2 = type(of: property.value)
if wrappedTypeFromOptionalType(valueType1) == valueType2 {
print("\(valueType1) == Optional<\(valueType2)>")
}
}
// Optional<String> == Optional<String>
However there's almost certainly a better way to do whatever you're trying to do here with the key paths.
could you use a mirror reflecting Any and check displayStyle is optional?.
func isOptional(any:Any) -> Bool {
let mirror = Mirror(reflecting: any)
if mirror.displayStyle == .Optional {
return true
} else {
return false
}
}
More on mirror display style:
https://developer.apple.com/documentation/swift/mirror.displaystyle
This is a hacky but working solution:
func isOptional(_ type: Any.Type) -> Bool {
let typeName = String(describing: type)
return typeName.hasPrefix("Optional<")
}
Test:
let t1 = Int?.self
let t2 = Bool.self
print(isOptional(t1))
// true
print(isOptional(t2))
// false
A tweak of #kelin’s answer:
postfix operator ...?!
postfix func ...?!<T>(_ instance: T) -> Bool {
let subject = "\(Mirror(reflecting: instance).subjectType)"
return !subject.hasPrefix("Optional")
}
And in the vein of #Ercell0’s answer is this superior method:
func isOptional<T>(_ instance: T) -> Bool {
guard let displayStyle = Mirror(reflecting: instance).displayStyle
else { return false }
return displayStyle == .optional
}
Basically the issue is the RawRepresentable part of the code, I need to be able to get the value of it, but because I need to cast to a protocol it doesn't allow me to use the rawValue. Any workaround for this?
public protocol Serializable {
func dictionary() -> [String: Any]
}
extension Serializable {
func dictionary() -> [String: Any] {
var result = [String: Any]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
guard let label = child.label else { continue }
switch child.value {
case let serializable as Serializable:
result[label] = serializable.dictionary()
// Compile error here
case let rawRepresentable as RawRepresentable:
result[label] = rawRepresentable.rawValue
default:
result[label] = child.value
}
}
return result
}
}
I think this comes down to issues trying to use an associatedType outside of the enum.
I fixed it like this:
public protocol Serializable {
func dictionary() -> [String: Any]
}
extension Serializable {
func dictionary() -> [String: Any] {
var result = [String: Any]()
let mirror = Mirror(reflecting: self)
for child in mirror.children {
guard let label = child.label else { continue }
switch child.value {
case let serializable as Serializable:
result[label] = serializable.dictionary()
case let rawRepresentable as RawRepresentable:
let value = rawRepresentable.getValueAsAny()
result[label] = value
default:
result[label] = child.value
}
}
return result
}
}
extension RawRepresentable {
func getValueAsAny() -> Any {
return rawValue as Any
}
}