Swift - switch between Core ML Model - swift

I'm trying to compare predictions from different MLModels in SwiftUI. To do that I have to switch between them, but can't because every ML variable has its own class, so I get the error:
Cannot assign value of type 'ModelOne' to type 'ModelTwo'
Here's an example code:
import Foundation
import CoreML
import SwiftUI
let modelone = { //declaration model 1
do {
let config = MLModelConfiguration()
return try ModelOne(configuration: config)
} catch {
/*...*/
}
}()
let modeltwo = { //declaration model 2
do {
let config = MLModelConfiguration()
return try ModelTwo(configuration: config)
} catch {
/*...*/
}
}()
var imageused : UIImage! //image to classify
var modelstring = "" //string of model user chosen
var modelchosen = modelone
Button(action: { //button user decide to use model two
modelstring = "Model Two"
}) {/*...*/}
/*...*/
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
if modelstring == "Model Two" { //if the user chosen model two, use ModelTwo
modelchosen = modeltwo // Error: Cannot assign value of type 'ModelOne' to type 'ModelTwo'
} else {
modelchosen = modelone}
let output = try? modelchosen.prediction(image: imagebuffer) //prediction with model chosen
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/
}
}
}
Thank you!

The issue is that the two model classes do not have a common class or common inherited class. There are several ways to implement what you want. I think this is the best way based on your example.
class MyModel {
var model: MLModel? = nil
init(modelName: String) {
let bundle = Bundle.main
if let modelURL = bundle.url(forResource: modelName, withExtension:"mlmodelc") {
do {
self.model = try MLModel(contentsOf: modelURL)
}
catch {
print("Unable to open MLModel: \(error)")
}
}
}
}
class TestModel {
class func testModels() {
let modelOne = MyModel(modelName: "ModelOne")
let modelTwo = MyModel(modelName: "ModelTwo")
var selectedModel = modelOne
selectedModel = modelTwo
}
}

Swift is a statically typed language which means that in the general case you cannot assign a variable of one type to a variable of another type:
var int: Int = 42
int = "Hello, world!" // Not allowed: cannot assign String to Int
The problem is that modelchosen is of type ModelOne since it is initialized with modelone, thus, you cannot later assign modeltwo to it as you are trying to do.
To make that working, you have first to identify the common capabilities of ModelOne and ModelTwo. Take a look at their definition. For instance, do their .predict(image:) method return the same type? It looks like you are trying to do image classification, so a common capability could be the capability to return a String describing the image (or a list of potential objects, etc.).
When you'll have identified the common capability, you'll be able to define the common interface of your different types. This common interface can be expressed in many ways:
Using a base class
Using a protocol
Using an enum with payloads (union)
The following examples suppose that the common capabilities are:
The two networks can both be initialized with a MLModelConfiuration
They are used for image classification, i.e. they predict label (a String) describing a given image
Using a base class
The base class definition expresses those requirements like this:
class MLClassifier {
init(from config: MLModelConfig) {
fatalError("not implemented")
}
func classify(image: ImageBuffer) -> String {
fatalError("not implemented")
}
}
You then derive this base class for the two models (example with the first one:
final class ModelOne: MLClassifier {
init(from config: MLModelConfig) {
// the specific implementation for `ModelOne`...
}
func classify(image: ImageBuffer) -> String {
// the specific implementation for `ModelOne`..
}
}
Finally, you can make the variable modelchosen to be of type MLClassifier to erase the underlying concrete type of the model:
var modelchosen: MLClassifier = ModelOne(from: config1)
As MLClassifier is a common base class for both ModelOne and ModelTwo you can dynamically change the type of modelchosen whenever you need:
// Later...
modelchosen = ModelTwo(from: config2)
The variable modelchosen being of type MLClassifier ensures that you can call the .classify(image:) method whatever the concrete model type is:
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
let output = modelchosen.classify(image: imageBuffer)
// Update the UI...
}
Using protocols
Protocols are the modern and preferred way of expressing common interfaces in Swift, they should be used over classes when possible:
protocol MLClassifier {
init(from config: MLModelConfig)
func classify(image: ImageBuffer) -> String
}
// Implement the protocol for your models
struct ModelOne: MLClassifier {
init(from config: MLModelConfig) { ... }
func classify(image: ImageBuffer) -> String { ... }
}
// Store an instance of any `MLClassfier` using an existential
var classifier: any MLClassifier = ModelOne(from: config1)
// Later...
classifier = ModelTwo(from: config2)
To sum up, the key is to identify the common capabilities of the different types you are trying to unify. For instance, if the two models output at some point a classLabelProbs of the same type, then you could use this as the common abstraction.
As a last resort, you could wrap everything in a big if-else statement, event though it is not recommended since it is not very readable, is not a good way to encapsulate common behavior and leads to a lot of code repetition:
func classifyphoto() {
guard let image = imageused as UIImage?,
let imagebuffer = image.convertToBuffer() else {
return
}
if modelstring == "Model Two" {
// Use modeltwo
let output = try? modeltwo.prediction(image: imagebuffer)
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/ }
} else {
// Use modelone
let output = try? modelone.prediction(image: imagebuffer)
if let output = output {
let results = output.classLabelProbs.sorted { $0.1 > $1.1 }
_ = results.map { /*...*/ }
}
}

Related

RxMVVM using Inputs / Outputs and issues with complex mapping

Given the design pattern as described by this post, here is an example view model:
final class SayHelloViewModel: ViewModelType {
let input: Input
let output: Output
struct Input {
let name: AnyObserver<String>
let validate: AnyObserver<Void>
}
struct Output {
let greeting: Driver<String>
}
private let nameSubject = ReplaySubject<String>.create(bufferSize: 1)
private let validateSubject = PublishSubject<Void>()
init() {
let greeting = validateSubject
.withLatestFrom(nameSubject)
.map { name in
return "Hello \(name)!"
}
.asDriver(onErrorJustReturn: ":-(")
self.output = Output(greeting: greeting)
self.input = Input(name: nameSubject.asObserver(), validate: validateSubject.asObserver())
}
}
The above seems like a perfectly good design pattern. My only issue is, what happens when your mapping function from nameSubject -> greeting is more complex than what is shown here and instead needs to be abstracted into it's own function?
In the below scenario, i've abstracted the mapping functionality into its own function called sayHello. Of course, the issue now is that we're referencing self before self is initialised. How is it possible to maintain this design pattern across non-trivial examples?
final class SayHelloViewModel {
let input: Input
let output: Output
struct Input {
let name: AnyObserver<String>
let validate: AnyObserver<Void>
}
struct Output {
let greeting: Driver<String>
}
private let nameSubject = ReplaySubject<String>.create(bufferSize: 1)
private let validateSubject = PublishSubject<Void>()
init() {
let greeting = validateSubject
.withLatestFrom(nameSubject)
.map(sayHello)
.asDriver(onErrorJustReturn: ":-(")
self.output = Output(greeting: greeting)
self.input = Input(name: nameSubject.asObserver(), validate: validateSubject.asObserver())
}
private func sayHello(name: String) -> String {
return "Hello \(name)!"
}
}
Just make your mapping function a private free function instead of a class member. It only needs to be a member itself if it needs access to members, which in this pattern is highly unlikely.
Edit: Also you could clean this up a lot by avoiding subjects and operate on the inputs/outputs directly like so:
final struct SayHelloViewModel {
struct Input {
let name: Observable<String>
let validate: Observable<Void>
}
// An output
let greeting: Driver<String>
init(inputs: Input) {
let greeting = input.validate
.withLatestFrom(input.name)
.map(sayHello)
.asDriver(onErrorJustReturn: ":-(")
}
}
private func sayHello(name: String) -> String {
return "Hello \(name)!"
}
You could take it even further and not use a struct/class at all and make it purely a function that returns a tuple/struct of outputs.

Can you simultaneously define and instantiate implicit types in Swift?

Just messing around with the language thinking of how I want to structure some UserDefaults that automatically generate keys based on the hierarchy. That got me wondering... Is it possible to simultaneously define, and instantiate a type, like this?
let myUserSettings = {
let formatting = {
var lastUsedFormat:String
}
}
let lastUsedFormat = myUserSettings.formatting.lastUsedFormat
Note: I can't use statics because I specifically need instancing so nested structs/classes with static members will not work for my case.
Here's the closest thing I could come up with, but I hate that I have to create initializers to set the members. I'm hoping for something a little less verbose.
class DefaultsScope {
init(_ userDefaults:UserDefaults){
self.userDefaults = userDefaults
}
let userDefaults:UserDefaults
func keyForSelf(property:String = #function) -> String {
return "\(String(reflecting: self)).\(property)"
}
}
let sharedDefaults = SharedDefaults(UserDefaults(suiteName: "A")!)
class SharedDefaults : DefaultsScope {
override init(_ userDefaults:UserDefaults){
formatting = Formatting(userDefaults)
misc = Misc(userDefaults)
super.init(userDefaults)
}
let formatting:Formatting
class Formatting:DefaultsScope {
let maxLastUsedFormats = 5
fileprivate(set) var lastUsedFormats:[String]{
get { return userDefaults.stringArray(forKey:keyForSelf()) ?? [] }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
func appendFormat(_ format:String) -> [String] {
var updatedListOfFormats = Array<String>(lastUsedFormats.suffix(maxLastUsedFormats - 1))
updatedListOfFormats.append(format)
lastUsedFormats = updatedListOfFormats
return updatedListOfFormats
}
}
let misc:Misc
class Misc:DefaultsScope {
var someBool:Bool{
get { return userDefaults.bool(forKey:keyForSelf()) }
set { userDefaults.set(newValue, forKey:keyForSelf()) }
}
}
}
So is there a simpler way?
Disclaimer: this is, probably, just an abstract solution that should not be used in real life :)
enum x {
enum y {
static func success() {
print("Success")
}
}
}
x.y.success()
Update: Sorry, folks, I can't stop experimenting. This one looks pretty awful :)
let x2= [
"y2": [
"success": {
print("Success")
}
]
]
x2["y2"]?["success"]?()
Update 2: One more try, this time with tuples. And since tuples must have at least two values, I had to add some dummies in there. Also, tuples cannot have mutating functions.
let x3 = (
y3: (
success: {
print("Success")
},
failure: {
print("Failure")
}
),
z3: 0
)
x3.y3.success()
How about you try nesting some swift structs?
struct x {
struct y {
static func success() {
print("success")
}
}
}
x.y.success()
You cannot have that kind of structure but you cant access y from inside x, since y is only visible inside the scope of x and so is success inside the scope of y. There is no way that you can access them from outside
One other alternative is to have higher order function like so, which return closure which is callable.
let x = {
{
{
print("Success")
}
}
}
let y = x()
let success = y()
success()
or
x()()()
The real world usage of higher order function for userdefaults could be something like this,
typealias StringType = (String) -> ((String) -> Void)
typealias IntType = (String) -> ((Int) -> Void)
typealias BoolType = (String) -> ((Bool) -> Void)
typealias StringValue = (String) -> String?
typealias IntValue = (String) -> Int?
typealias BoolValue = (String) -> Bool?
func userDefaults<T>(_ defaults: UserDefaults) -> (String) -> ((T) -> Void) {
return { key in
return { value in
defaults.setValue(value, forKey: key)
}
}
}
func getDefaultsValue<T>(_ defaults: UserDefaults) -> (String) -> T? {
return { key in
return defaults.value(forKey: key) as? T
}
}
let setStringDefaults: StringType = userDefaults(.standard)
setStringDefaults("Name")("Jack Jones")
setStringDefaults("Address")("Australia")
let setIntDefaults: IntType = userDefaults(.standard)
setIntDefaults("Age")(35)
setIntDefaults("Salary")(2000)
let setBoolDefaults: BoolType = userDefaults(.standard)
setBoolDefaults("Married")(false)
setBoolDefaults("Employed")(true)
let getStringValue: StringValue = getDefaultsValue(.standard)
let name = getStringValue("Name")
let address = getStringValue("Address")
let getIntValue: IntValue = getDefaultsValue(.standard)
let age = getIntValue("Age")
let salary = getIntValue("Salary")
let getBoolValue: BoolValue = getDefaultsValue(.standard)
let married = getBoolValue("Married")
let employed = getBoolValue("Employed")
I am not sure if you like the pattern, but it has some good use cases as you can see from below, setStringDefaults you can set strings value to string key and all of them are typesafe.
You can extend this for your use case. But, you could use struct as well and use imperative code, which could be easier to understand. I see beauty in this as well.
Ok, I think I've figured it out. This first class can go in some common library that you use for all your apps.
class SettingsScopeBase {
private init(){}
static func getKey(setting:String = #function) -> String {
return "\(String(reflecting:self)).\(setting)"
}
}
The next part is a pair of classes:
The 'Scoping' class where you define which user defaults instance to use (along with anything else you may want to specify for this particular settings instance)
The actual hierarchy that defines your settings
Here's the first. I'm setting this up for my shared settings between my application and it's extension:
class SharedSettingsScope : SettingsScopeBase{
static let defaults = UserDefaults(suiteName: "group.com.myco.myappgroup")!
}
And finally, here's how you 'set up' your hierarchy as well as how you implement the properties' bodies.
class SharedSettings:SharedSettingsScope{
class Formatting:SharedSettingsScope{
static var groupsOnWhitespaceOnlyLines:Bool{
get { return defaults.bool(forKey: getKey()) }
set { defaults.set(newValue, forKey: getKey()) }
}
}
}
And here's how you use them...
let x = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// x = false
SharedSettings.Formatting.groupsOnWhitespaceOnlyLines = true
let y = SharedSettings.Formatting.groupsOnWhitespaceOnlyLines
// y = true
I'm going to see if I can refine/optimize it a little more, but this is pretty close to where I want to be. No hard-coded strings, keys defined by the hierarchy where they're used, and only setting the specific UserDefaults instance in one place.

Get all key paths from a struct in Swift 4

Let's say I have that struct:
struct MyStruct {
let x: Bool
let y: Bool
}
In Swift 4 we can now access it's properties with the myStruct[keyPath: \MyStruct.x] interface.
What I need is a way to access all it's key paths, something like:
extension MyStruct {
static func getAllKeyPaths() -> [WritableKeyPath<MyStruct, Bool>] {
return [
\MyStruct.x,
\MyStruct.y
]
}
}
But, obviously, without me having to manually declare every property in an array.
How can I achieve that?
DISCLAIMER:
Please note that the following code is for educational purpose only and it should not be used in a real application, and might contains a lot of bugs/strange behaviors if KeyPath are used this way.
Answer:
I don't know if your question is still relevant today, but the challenge was fun :)
This is actually possible using the mirroring API.
The KeyPath API currently doesn't allow us to initialize a new KeyPath from a string, but it does support dictionary "parsing".
The idea here is to build a dictionary that will describe the struct using the mirroring API, then iterate over the key to build the KeyPath array.
Swift 4.2 playground:
protocol KeyPathListable {
// require empty init as the implementation use the mirroring API, which require
// to be used on an instance. So we need to be able to create a new instance of the
// type.
init()
var _keyPathReadableFormat: [String: Any] { get }
static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}
extension KeyPathListable {
var _keyPathReadableFormat: [String: Any] {
let mirror = Mirror(reflecting: self)
var description: [String: Any] = [:]
for case let (label?, value) in mirror.children {
description[label] = value
}
return description
}
static var allKeyPaths: [KeyPath<Self, Any?>] {
var keyPaths: [KeyPath<Self, Any?>] = []
let instance = Self()
for (key, _) in instance._keyPathReadableFormat {
keyPaths.append(\Self._keyPathReadableFormat[key])
}
return keyPaths
}
}
struct Foo: KeyPathListable {
var x: Int
var y: Int
}
extension Foo {
// Custom init inside an extension to keep auto generated `init(x:, y:)`
init() {
x = 0
y = 0
}
}
let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]
var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!
print(x)
print(y)
Note that the printed output is not always in the same order (probably because of the mirroring API, but not so sure about that).
After modifying rraphael's answer I asked about this on the Swift forums.
It is possible, discussion here:
Getting KeyPaths to members automatically using Mirror
Also, the Swift for TensorFlow team has this already built in to Swift for TensorFlow, which may make its way to pure swift:
Dynamic property iteration using key paths
I propose my solution. It has the advantage of dealing correctly with #Published values when using the Combine framework.
For the sake of clarity, it is a simplified version of what I have really. In the full version, I pass some options to the Mirror.allKeyPaths() function to change behaviour ( To enumerate structs and/or classes properties in sub-dictionaries for example ).
The first Mirror extension propose some functions to simplify properties enumeration.
The second extension implements the keyPaths dictionaries creation, replacing
#Published properties by correct name and value
The last part is the KeyPathIterable protocol, that add enumeration
capability to associated object
swift
// MARK: - Convenience extensions
extension String {
/// Returns string without first character
var byRemovingFirstCharacter: String {
guard count > 1 else { return "" }
return String(suffix(count-1))
}
}
// MARK: - Mirror convenience extension
extension Mirror {
/// Iterates through all children
static func forEachProperty(of object: Any, doClosure: (String, Any)->Void) {
for (property, value) in Mirror(reflecting: object).children where property != nil {
doClosure(property!, value)
}
}
/// Executes closure if property named 'property' is found
///
/// Returns true if property was found
#discardableResult static func withProperty(_ property: String, of object: Any, doClosure: (String, Any)->Void) -> Bool {
for (property, value) in Mirror(reflecting: object).children where property == property {
doClosure(property!, value)
return true
}
return false
}
/// Utility function to determine if a value is marked #Published
static func isValuePublished(_ value: Any) -> Bool {
let valueTypeAsString = String(describing: type(of: value))
let prefix = valueTypeAsString.prefix { $0 != "<" }
return prefix == "Published"
}
}
// MARK: - Mirror extension to return any object properties as [Property, Value] dictionary
extension Mirror {
/// Returns objects properties as a dictionary [property: value]
static func allKeyPaths(for object: Any) -> [String: Any] {
var out = [String: Any]()
Mirror.forEachProperty(of: object) { property, value in
// If value is of type Published<Some>, we transform to 'regular' property label and value
if Self.isValuePublished(value) {
Mirror.withProperty("value", of: value) { _, subValue in
out[property.byRemovingFirstCharacter] = subValue
}
} else {
out[property] = value
}
}
return out
}
}
// MARK: - KeyPathIterable protocol
protocol KeyPathIterable {
}
extension KeyPathIterable {
/// Returns all object properties
var allKeyPaths: [String: Any] {
return Mirror.allKeyPaths(for: self)
}
}

Using reflection to set object properties without using setValue forKey

In Swift it's not possible use .setValue(..., forKey: ...)
nullable type fields like Int?
properties that have an enum as it's type
an Array of nullable objects like [MyObject?]
There is one workaround for this and that is by overriding the setValue forUndefinedKey method in the object itself.
Since I'm writing a general object mapper based on reflection. See EVReflection I would like to minimize this kind of manual mapping as much as possible.
Is there an other way to set those properties automatically?
The workaround can be found in a unit test in my library here
This is the code:
class WorkaroundsTests: XCTestCase {
func testWorkarounds() {
let json:String = "{\"nullableType\": 1,\"status\": 0, \"list\": [ {\"nullableType\": 2}, {\"nullableType\": 3}] }"
let status = Testobject(json: json)
XCTAssertTrue(status.nullableType == 1, "the nullableType should be 1")
XCTAssertTrue(status.status == .NotOK, "the status should be NotOK")
XCTAssertTrue(status.list.count == 2, "the list should have 2 items")
if status.list.count == 2 {
XCTAssertTrue(status.list[0]?.nullableType == 2, "the first item in the list should have nullableType 2")
XCTAssertTrue(status.list[1]?.nullableType == 3, "the second item in the list should have nullableType 3")
}
}
}
class Testobject: EVObject {
enum StatusType: Int {
case NotOK = 0
case OK
}
var nullableType: Int?
var status: StatusType = .OK
var list: [Testobject?] = []
override func setValue(value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "nullableType":
nullableType = value as? Int
case "status":
if let rawValue = value as? Int {
status = StatusType(rawValue: rawValue)!
}
case "list":
if let list = value as? NSArray {
self.list = []
for item in list {
self.list.append(item as? Testobject)
}
}
default:
NSLog("---> setValue for key '\(key)' should be handled.")
}
}
}
I found a way around this when I was looking to solve a similar problem - that KVO can't set the value of a pure Swift protocol field. The protocol has to be marked #objc, which caused too much pain in my code base.
The workaround is to look up the Ivar using the objective C runtime, get the field offset, and set the value using a pointer.
This code works in a playground in Swift 2.2:
import Foundation
class MyClass
{
var myInt: Int?
}
let instance = MyClass()
// Look up the ivar, and it's offset
let ivar: Ivar = class_getInstanceVariable(instance.dynamicType, "myInt")
let fieldOffset = ivar_getOffset(ivar)
// Pointer arithmetic to get a pointer to the field
let pointerToInstance = unsafeAddressOf(instance)
let pointerToField = UnsafeMutablePointer<Int?>(pointerToInstance + fieldOffset)
// Set the value using the pointer
pointerToField.memory = 42
assert(instance.myInt == 42)
Notes:
This is probably pretty fragile, you really shouldn't use this.
But maybe it could live in a thoroughly tested and updated reflection library until Swift gets a proper reflection API.
It's not that far away from what Mirror does internally, see the code in Reflection.mm, around here: https://github.com/apple/swift/blob/swift-2.2-branch/stdlib/public/runtime/Reflection.mm#L719
The same technique applies to the other types that KVO rejects, but you need to be careful to use the right UnsafeMutablePointer type. Particularly with protocol vars, which are 40 or 16 bytes, unlike a simple class optional which is 8 bytes (64 bit). See Mike Ash on the topic of Swift memory layout: https://mikeash.com/pyblog/friday-qa-2014-08-01-exploring-swift-memory-layout-part-ii.html
Edit: There is now a framework called Runtime at https://github.com/wickwirew/Runtime which provides a pure Swift model of the Swift 4+ memory layout, allowing it to safely calculate the equivalent of ivar_getOffset without invoking the Obj C runtime. This allows setting properties like this:
let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")
try property.set(value: "newUsername", on: &user)
This is probably a good way forward until the equivalent capability becomes part of Swift itself.
Swift 5
To set and get properties values with pure swift types you can use internal ReflectionMirror.swift approach with shared functions:
swift_reflectionMirror_recursiveCount
swift_reflectionMirror_recursiveChildMetadata
swift_reflectionMirror_recursiveChildOffset
The idea is to gain info about an each property of an object and then set a value to a needed one by its pointer offset.
There is example code with KeyValueCoding protocol for Swift that implements setValue(_ value: Any?, forKey key: String) method:
typealias NameFreeFunc = #convention(c) (UnsafePointer<CChar>?) -> Void
struct FieldReflectionMetadata {
let name: UnsafePointer<CChar>? = nil
let freeFunc: NameFreeFunc? = nil
let isStrong: Bool = false
let isVar: Bool = false
}
#_silgen_name("swift_reflectionMirror_recursiveCount")
fileprivate func swift_reflectionMirror_recursiveCount(_: Any.Type) -> Int
#_silgen_name("swift_reflectionMirror_recursiveChildMetadata")
fileprivate func swift_reflectionMirror_recursiveChildMetadata(
_: Any.Type
, index: Int
, fieldMetadata: UnsafeMutablePointer<FieldReflectionMetadata>
) -> Any.Type
#_silgen_name("swift_reflectionMirror_recursiveChildOffset")
fileprivate func swift_reflectionMirror_recursiveChildOffset(_: Any.Type, index: Int) -> Int
protocol Accessors {}
extension Accessors {
static func set(value: Any?, pointer: UnsafeMutableRawPointer) {
if let value = value as? Self {
pointer.assumingMemoryBound(to: self).pointee = value
}
}
}
struct ProtocolTypeContainer {
let type: Any.Type
let witnessTable = 0
var accessors: Accessors.Type {
unsafeBitCast(self, to: Accessors.Type.self)
}
}
protocol KeyValueCoding {
}
extension KeyValueCoding {
private mutating func withPointer<Result>(displayStyle: Mirror.DisplayStyle, _ body: (UnsafeMutableRawPointer) throws -> Result) throws -> Result {
switch displayStyle {
case .struct:
return try withUnsafePointer(to: &self) {
let pointer = UnsafeMutableRawPointer(mutating: $0)
return try body(pointer)
}
case .class:
return try withUnsafePointer(to: &self) {
try $0.withMemoryRebound(to: UnsafeMutableRawPointer.self, capacity: 1) {
try body($0.pointee)
}
}
default:
fatalError("Unsupported type")
}
}
public mutating func setValue(_ value: Any?, forKey key: String) {
let mirror = Mirror(reflecting: self)
guard let displayStyle = mirror.displayStyle
, displayStyle == .class || displayStyle == .struct
else {
return
}
let type = type(of: self)
let count = swift_reflectionMirror_recursiveCount(type)
for i in 0..<count {
var field = FieldReflectionMetadata()
let childType = swift_reflectionMirror_recursiveChildMetadata(type, index: i, fieldMetadata: &field)
defer { field.freeFunc?(field.name) }
guard let name = field.name.flatMap({ String(validatingUTF8: $0) }),
name == key
else {
continue
}
let clildOffset = swift_reflectionMirror_recursiveChildOffset(type, index: i)
try? withPointer(displayStyle: displayStyle) { pointer in
let valuePointer = pointer.advanced(by: clildOffset)
let container = ProtocolTypeContainer(type: childType)
container.accessors.set(value: value, pointer: valuePointer)
}
break
}
}
}
This approach works with both class and struct and supports optional, enum and inherited(for classes) properties:
// Class
enum UserType {
case admin
case guest
case none
}
class User: KeyValueCoding {
let id = 0
let name = "John"
let birthday: Date? = nil
let type: UserType = .none
}
var user = User()
user.setValue(12345, forKey: "id")
user.setValue("Bob", forKey: "name")
user.setValue(Date(), forKey: "birthday")
user.setValue(UserType.admin, forKey: "type")
print(user.id, user.name, user.birthday!, user.type)
// Outputs: 12345 Bob 2022-04-22 10:41:10 +0000 admin
// Struct
struct Book: KeyValueCoding {
let id = 0
let title = "Swift"
let info: String? = nil
}
var book = Book()
book.setValue(56789, forKey: "id")
book.setValue("ObjC", forKey: "title")
book.setValue("Development", forKey: "info")
print(book.id, book.title, book.info!)
// Outputs: 56789 ObjC Development
if you are afraid to use #_silgen_name for shared functions you can access to it dynamically with dlsym e.g.: dlsym(RTLD_DEFAULT, "swift_reflectionMirror_recursiveCount") etc.
UPDATE
There is a swift package (https://github.com/ikhvorost/KeyValueCoding) with full implementation of KeyValueCoding protocol for pure Swift and it supports: get/set values to any property by a key, subscript, get a metadata type, list of properties and more.
Unfortunately, this is impossible to do in Swift.
KVC is an Objective-C thing. Pure Swift optionals (combination of Int and Optional) do not work with KVC. The best thing to do with Int? would be to replace with NSNumber? and KVC will work. This is because NSNumber is still an Objective-C class. This is a sad limitation of the type system.
For your enums though, there is still hope. This will not, however, reduce the amount of coding that you would have to do, but it is much cleaner and at its best, mimics the KVC.
Create a protocol called Settable
protocol Settable {
mutating func setValue(value:String)
}
Have your enum confirm to the protocol
enum Types : Settable {
case FirstType, SecondType, ThirdType
mutating func setValue(value: String) {
if value == ".FirstType" {
self = .FirstType
} else if value == ".SecondType" {
self = .SecondType
} else if value == ".ThirdType" {
self = .ThirdType
} else {
fatalError("The value \(value) is not settable to this enum")
}
}
}
Create a method: setEnumValue(value:value, forKey key:Any)
setEnumValue(value:String forKey key:Any) {
if key == "types" {
self.types.setValue(value)
} else {
fatalError("No variable found with name \(key)")
}
}
You can now call self.setEnumValue(".FirstType",forKey:"types")

get the type/class of a property from its name in swift

Lets say I have this class:
class Node {
var value: String
var children: [Node]?
}
If I have the name of one of its properties (for example "children") how can I get its type? (In this case [Node]?)
I imagine having a global function like below will solve my needs:
func typeOfPropertyWithName(name: String, ofClass: AnyClass) -> AnyClass? {
//???
}
// Example usage:
var arrayOfNodesClass = typeOfPropertyWithName("children", Node.self)
Swift 2 (Note: Reflection changed):
import Foundation
enum PropertyTypes:String
{
case OptionalInt = "Optional<Int>"
case Int = "Int"
case OptionalString = "Optional<String>"
case String = "String"
//...
}
extension NSObject{
//returns the property type
func getTypeOfProperty(name:String)->String?
{
let type: Mirror = Mirror(reflecting:self)
for child in type.children {
if child.label! == name
{
return String(child.value.dynamicType)
}
}
return nil
}
//Property Type Comparison
func propertyIsOfType(propertyName:String, type:PropertyTypes)->Bool
{
if getTypeOfProperty(propertyName) == type.rawValue
{
return true
}
return false
}
}
custom class:
class Person : NSObject {
var id:Int?
var name : String?
var email : String?
var password : String?
var child:Person?
}
get the type of the "child" property:
let person = Person()
let type = person.getTypeOfProperty("child")
print(type!) //-> Optional<Person>
property type checking:
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalInt) ) //--> false
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalString) //--> true
or
if person.propertyIsOfType("email", type: PropertyTypes.OptionalString)
{
//true -> do something
}
else
{
//false -> do something
}
Reflection is achieved in Swift using the global reflect() function. When passing an instance of some type to reflect() it returns a MirrorType, which has a range of properties allowing you to analyze your instance:
var value: Any { get }
var valueType: Any.Type { get }
var objectIdentifier: ObjectIdentifier? { get }
var count: Int { get }
var summary: String { get }
var quickLookObject: QuickLookObject? { get }
var disposition: MirrorDisposition { get }
subscript(i: Int) -> (String, MirrorType) { get }
This seems to work:
func getTypeOfVariableWithName(name: String, inInstance instance: Any) -> String? {
let mirror = reflect(instance)
var variableCollection = [String: MirrorType]()
for item in 0..<mirror.count {
variableCollection[mirror[item].0] = mirror[item].1
}
if let type = variableCollection[name] {
let longName = _stdlib_getDemangledTypeName(type.value)
let shortName = split(longName, { $0 == "."}).last
return shortName ?? longName
}
return nil
}
Here's some example code on SwiftStub.
Edit:
The result for optional values is only "Optional".
The result for arrays is only "Array".
The result for dictionaries is only "Dictionary".
I'm not sure if it is possible to extract what kind of optional/array/dictionary it is. But I guess this would also be the case for custom data structures using generics.
Building on #PeterKreinz answer I needed to be able to check types of inherited properties as well so added a little to his above code:
extension NSObject {
// Returns the property type
func getTypeOfProperty (name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
while let parent = type.superclassMirror() {
for child in parent.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
type = parent
}
return nil
}
}
Hope this may help someone.
Swift 3 update:
// Extends NSObject to add a function which returns property type
extension NSObject {
// Returns the property type
func getTypeOfProperty (_ name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
while let parent = type.superclassMirror {
for child in parent.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
type = parent
}
return nil
}
}
The solution provided by #peter-kreinz using Swift's class Mirror works beautifully when you have an instance of a class, and want to know the types of the properties. However if you want to inspect the properties of a class without having an instance of it you might be interested in my solution.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}