Hashing problems using a wrapper class around NSUUID as the key - swift

** REWRITE **
OK, it turns out I'm really asking a different question. I understand about hashValue and ==, so that's not relevant.
I would like my wrapper class BUUID to "do the right thing" and act just like NSUUID's act in a Dictionary.
See below, where they don't.
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
Results. Note that I can look up using NSUUID nB and get back what I put under nA. Not so with my BUUID.
a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil
nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false
nA == nB: true
Optional("Hi") Optional("Hi")

Inheriting from NSObject also assumes isEqual(object: AnyObject?) -> Bool method overloading:
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override func isEqual(object: AnyObject?) -> Bool {
guard let buuid = object as? BUUID else {
return false
}
return buuid._realUUID == _realUUID
}
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")

So the answer is to not make BUUID inherit from NSObject, which undercuts the Swiftiness of overriding ==.
So:
extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
// take away all 'override' keywords, nothing to override
// otherwise same as above
}
Interesting!

This answer is relevant to initially asked question: Why that's possible to get two key-value pairs with identical key's hashes in a dictionary
This example illustrates that keys in Dictionary can have identical hashes, but equality operation should return false for different keys:
func ==(lhs: FooKey, rhs: FooKey) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
class FooKey: Hashable, Equatable {
var hashValue: Int {
get {
return 123
}
}
}
var d = Dictionary<FooKey, String>()
let key1 = FooKey()
let key2 = FooKey()
d[key1] = "value1"
d[key2] = "value2"
Output
[FooKey: "value1", FooKey: "value2"]
That's definitely not good to have all keys with the same hash. In this case we are getting that worst case when search element complexity fells down to O(n) (exhaustive search). But it will work.

Related

Storing Generic Objects in a Heterogeneous Array and retrieving object parameter as the correct type

Ahoy everyone,
I have recently been trying to implement a Node based graph system that passes data between nodes using plugs. Similar to many of the 3D applications like houdini and maya.
I have written a similar system before using Python, and wanted to try this with Swift as my first learning excersise. Boy did I jump into the deep end on this one.
I am stuck now with Swifts Arrays, as I would like to store a list of Generic plugs.
Each plug can have its own value type float, int, color, string, Vector Matrix.
I have read up about Type Erasers and opaque types, but still cant seem to get my values our of a list in a way that I can perform some arithmetic on them.
All and any help that might put me in the direction would be greatly appreciated :D
import Foundation
import MetalKit
protocol genericPlug {
associatedtype T
func GetValue() -> T
}
class Plug<T>:genericPlug{
var _value:T?
var value:T {
get{GetValue()}
set(val){
value = val
}
}
func GetValue() -> T{
return _value!
}
init(_ newValue:T){
_value=newValue
}
}
class Node{
var plugs:[genericPlug] = []
init(){
var p1 = Plug<Int>(0)
var p2 = Plug(vector2(1.2, 3.1))
var p3 = Plug([0.0, 3.1, 0.6, 1])
plugs.append(p1)
plugs.append(p2)
plugs.append(p3)
}
func execute(){
// will access the plugs in the array and perform some sort of calculations on them.
plugs[0].value + 1 // should equal 1
plugs[1].value.x + 0.8 // should have x=2.0 y=3.1
plugs[2].value[1] - 0.1 // should equal 3.0
}
}
Thanks everyone
Use a generic something to extract what you need. Your options are methods and subscripts.
protocol PlugValue {
init()
}
extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
struct Plug<Value: PlugValue> {
var value: Value
init(_ value: Value) {
self.value = value
}
}
protocol AnyPlug {
var anyValue: PlugValue { get }
}
extension AnyPlug {
subscript<Value: PlugValue>(type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
func callAsFunction<Value: PlugValue>(_ type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
}
extension Plug: AnyPlug {
var anyValue: PlugValue { value }
}
let plugs: [AnyPlug] = [
Plug(1),
Plug(2.3 as Float),
Plug(4.5),
Plug([6, 7, 8] as SIMD3)
]
plugs[0][Int.self] // 1
plugs[1][Double.self] // 0
plugs[1][] as Float // 2.3
let double: Double = plugs[2]() // 4.5
plugs[3](SIMD3.self).y // 7
With the array of protocols, do you have to down cast them into their Plug when retrieving them every time?
Essentially. This is true of all heterogenous sequences. Here are your options:
extension Array: PlugValue where Element: PlugValue { }
let plug: AnyPlug = Plug([0.1, 1.1, 2.1])
(plug as? Plug<[Double]>)?.value[1]
(plug.anyValue as? [Double])?[1]
extension Plug {
enum Error: Swift.Error {
case typeMismatch
}
}
extension AnyPlug {
func callAsFunction<Value: PlugValue, Return>(_ closure: (Value) -> Return) throws {
guard let value = anyValue as? Value
else { throw Plug<Value>.Error.typeMismatch }
closure(value)
}
}
try plug { (doubles: [Double]) in doubles[1] } // 1.1
try plug { ($0 as [Double])[1] } // 1.1
try plug { $0 as Int } // <Swift.Int>.Error.typeMismatch
I managed to find a solution that worked for my needs.
I am still looking at finding a better way to handle getting the data and their correct type.
import Foundation
import MetalKit
// Creating the PlugType Enum
enum PlugType{
case Integer(Int?)
case Float_(Float?)
case Double_(Double?)
case Vector3(simd_int3)
// default types
static func IntegerType() -> PlugType{ return PlugType.Integer(nil)}
static func FloatType() -> PlugType{ return PlugType.Float_(nil)}
static func DoubleType() -> PlugType{ return PlugType.Double_(nil)}
}
// Implements a way to retrieve the correct value type
extension PlugType{
var IntegerValue: Int{
switch self{
case .Integer(let value):
return value ?? 0
default:
return 0
}
}
var FloatValue: Float{
switch self
{
case .Float_(let value):
return value ?? 0
default:
return 0
}
}
var DoubleValue: Double{
switch self
{
case .Double_(let value):
return value ?? 0
default:
return 0
}
}
}
// Get the string representation of the PlugType
extension PlugType {
var typeName: String{
switch self {
case .Integer: return "Integer"
case .Float_: return "Float"
case .Double_: return "Double"
case .Vector3: return "Vector3"
}
}
var swiftType: Any.Type {
switch self {
case .Integer: return Int.self
case .Float_: return Float.self
case .Double_: return Double.self
case .Vector3: return simd_int3.self
}
}
}
class Plug{
var _value:PlugType?
var type:String? { get{ return _value?.typeName } }
init(_ newValue:PlugType){
_value = newValue
}
func geee<T>(_ input:T) -> T{
switch type {
case "Integer":
return getVal(_value!.IntegerValue) as! T
case "Double":
return getVal(_value!.DoubleValue) as! T
default:
return 0 as! T
}
}
func getVal(_ val:Int) -> Int {
return val
}
func getVal(_ val:Float) -> Float {
return val
}
func getVal(_ val:Double) -> Double {
return val
}
}
var plugs:[Plug] = []
var p1 = Plug(PlugType.Integer(2))

Get index of enum with extension of String,

I have an Enum that looks like this:
enum Status: String {
case online = "online"
case offline = "offline"
case na = "na"
}
I need the String value and I know how to get it, but my question is if it´s possible to get the index value too for each case in the enum.
0 for online, 1 for offline and 2 for na.
I will add more statues in the future.
-------- UPDATE -------
Since swift 4.2 you can do following:
enum Status: String, CaseIterable {
case online
case offline
case na
}
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.index { self == $0 }
}
}
or as I wrote earlier:
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
return String(describing: self)
}
}
-------- ORIGIN ANSWER -------
I'm using this extension:
protocol EnumIterable: RawRepresentable {
static var allValues: [Self] { get }
var index: Int? { get }
}
extension EnumIterable {
static var count: Int {
return allValues.count
}
}
extension EnumIterable where Self.RawValue: Equatable {
var next: Self? {
if let index = Self.allValues.index(where: { rawValue == $0.rawValue }) {
return Self.allValues[safe: index + 1]
}
return nil
}
var index: Int? {
return Self.allValues.index { rawValue == $0.rawValue }
}
}
But you would define allValues variable:
enum Status: String, EnumIterable {
case online = "online"
case offline = "offline"
case na = "na"
static var allValues: [Status] {
return [
.online,
.offline,
.na,
]
}
}
Something similar was solved here (count of enumerations):
How do I get the count of a Swift enum?
Next possibility is to define enum like this:
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
return String(describing: self)
}
}
print (Status.online.value) // online
print (Status.online.index) // 0
or
enum Status: Int {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var value: String {
switch self {
case .online:
return "online"
case .offline:
return "offline"
case .na:
return "na"
}
}
}
print (Status.online.value) // online
print (Status.online.index) // 0
Note: for defining string value, you can use CustomStringConvertible protocol.
Eg:
enum Status: Int, CustomStringConvertible {
case online = 0
case offline
case na
var index: Int {
return rawValue
}
var description: String {
switch self {
case .online:
return "online"
case .offline:
return "offline"
case .na:
return "na"
}
}
}
Great answer of #JMI in Swift 5.
enum MyEnum: CaseIterable {
case one
case two
}
extension CaseIterable where Self: Equatable {
var index: Self.AllCases.Index? {
return Self.allCases.firstIndex { self == $0 }
}
}
How to use:
guard let enumCaseIndex = MyEnum.one.index else { return }
print("Enum case index: ", \(enumCaseIndex)) // prints: 0
As enum in Swift does not have index of its values (please read the post in Martin R's comment), you have to create your self some 'index' function or to map all values to an Array to have the index.
You can implement as in this post or another way to do:
enum Status: String {
case online = "online"
case offline = "offline"
case na = "na"
static func index(of aStatus: Status) -> Int {
let elements = [Status.online, Status.offline, Status.na]
return elements.index(of: aStatus)!
}
static func element(at index: Int) -> Status? {
let elements = [Status.online, Status.offline, Status.na]
if index >= 0 && index < elements.count {
return elements[index]
} else {
return nil
}
}
}
let a = Status.na
//return 2
let index = Status.index(of: a)
//return Status.offline
let element2 = Status.element(at: 1)
//return nil
let element3 = Status.element(at: 3)
I did use a solution witch is almost the same than santhosh-shettigar:
func toInt() -> Int {
let allValues: NSArray = MyEnum.allCases as NSArray
let result: Int = allValues.index(of: self)
return result
}
Simple! I use the Swift built-in MyEnum.allCases, ...but I'm not sure that in Swift Specification, we have the guaranty that the Array return by MyEnum.allCases is always in the same order, the one used at the MyEnum definition???
How about this.
enum Status: Int {
case online = 0
case offline = 1
case na = 2
}
You can get the string value and the integer index both.
// enum as string
let enumName = "\(Status.online)" // `online`
// enum as int value
let enumValue = Status.online.rawValue // 0
// enum from int
let enumm = Status.init(rawValue: 1)
Hope it helps. Thanks.
Possible work around may to associate custom functions with enum
enum ToolbarType : String{
case Case = "Case", View="View", Information="Information"
static let allValues = [Case, View, Information]
func ordinal() -> Int{
return ToolbarType.allValues.index(of: self)!
}
}
Can be used as
for item in ToolbarType.allValues {
print("\(item.rawValue): \(item.ordinal())")
}
Output
Case: 0
View: 1
Information: 2

Swift 3 Equatable Struct optional function

This will be a little long winded so please bear with me. I am also a bit of a swift beginner as well. I have an array with a defined struct.
var modelOriginalArray = [model]()
struct model: Equatable {
var modelID = String()
var modelName = String()
var modelNumber = String()
var manufacturer = String()
var manufShort = String()
var phiTypeCode = String()
var phiTypeDesc = String()
init(modelID: String, modelName: String, modelNumber: String, manufacturer: String, manufShort: String, phiTypeCode: String, phiTypeDesc: String) {
self.modelID = modelID
self.modelName = modelName
self.modelNumber = modelNumber
self.manufacturer = manufacturer
self.manufShort = manufShort
self.phiTypeCode = phiTypeCode
self.phiTypeDesc = phiTypeDesc
}
static func == (lhs: model, rhs: model) -> Bool {
return lhs.manufShort == rhs.manufShort && lhs.modelName == rhs.modelName && lhs.modelNumber == rhs.modelNumber
}
}
I load about 5000 records into this array. I then have a need to filter this array based on search criteria. Let's say I am looking for a manufacturer "Sony". There is the possibility of multiple models for Sony so I need to separate all Sony records from the greater 5000.
srchval = "SONY"
var filteredArray = [model]()
var uniqueFilteredArray = [model]()
filteredArray = self.modelOriginalArray.filter { $0.manufShort.range(of: srchval, options: .caseInsensitive) != nil }
This will give me an array with only "Sony" records. However there is a possibility that some of those "Sony" records have duplicate manufShort, modelName, modelNumber values under different modelID's. I need to separate those and only have unique records.
// Find Uniquic options by removing duplicate Model Names
uniqueFilteredArray = unique(models: filteredArray)
func unique(models: [model]) -> [model] {
var uniqueModels = [model]()
for model in models {
if !uniqueModels.contains(model) {
uniqueModels.append(model)
}
}
return uniqueModels
}
This all works ver well. The problem I have is in the filter there are situations where I have to make sure the record is matching on:
static func == (lhs: model, rhs: model) -> Bool {
return lhs.manufShort == rhs.manufShort && lhs.modelName == rhs.modelName && lhs.modelNumber == rhs.modelNumber
}
And in a different situation in the same class I need to match only on the manufShort:
static func == (lhs: model2, rhs: model2) -> Bool {
return lhs.manufShort == rhs.manufShort
}
I have tried creating a separate model i.e.. model2 with this different static function, but I have difficulties moving data from one array to another with a different struct.
Any thoughts or a better way to accomplish this?
Thanks
Since you use two different approaches for defining the "equality" of two models, you should probably consider not using the == operator, as you don't really test equality if the equality predicate is different from case to case. Rather, you have two different custom predicates (which applies to two model instances) that you would like to use in different contexts. Why not use two custom type (static) methods for this, with descriptive names, semantically describing their different meanings?
Thanks! That makes a great deal of sense, how do I call the different (static) methods so that it will hit the right function based on what I am after. An example?
Example setup:
struct Foo {
let id: String
let bar: Int
let baz: Int
let bax: Int
init(_ id: String, _ bar: Int, _ baz: Int, _ bax: Int)
{
self.id = id
self.bar = bar
self.baz = baz
self.bax = bax
}
static func byBarEqualityPredicate(lhs: Foo, rhs: Foo) -> Bool {
return lhs.bar == rhs.bar
}
static func byBazAndBaxEqualityPredicate(lhs: Foo, rhs: Foo) -> Bool {
return lhs.baz == rhs.baz && lhs.bax == rhs.bax
}
}
let fooArr = [Foo("Foo A", 1, 2, 3),
Foo("Foo B", 1, 1, 2),
Foo("Foo C", 3, 1, 2)]
A slightly modified version of your unique method, now supplying a (Foo, Foo) -> Bool predicate among with the foos array:
func unique(foos: [Foo], predicate: (Foo, Foo) -> Bool) -> [Foo] {
var uniqueFoos = [Foo]()
for foo in foos {
if !uniqueFoos.contains(where: { predicate($0, foo) }) {
uniqueFoos.append(foo)
}
}
return uniqueFoos
}
Testing with the two different Foo predicates:
// by 'bar' "equality": Foo A and Foo B will be considered "equal",
// and only Foo A, among these two , will be added to the "unique" array.
let fooWithBarPredicate = unique(foos: fooArr, predicate: Foo.byBarEqualityPredicate)
fooWithBarPredicate.forEach { print($0.id) } // Foo A, Foo C
// by 'baz' && 'bax' "equality": Foo A and Foo C will be considered "equal",
// and only Foo A, among these two, will be added to the "unique" array.
let fooWithBazBaxPredicate = unique(foos: fooArr, predicate: Foo.byBazAndBaxEqualityPredicate)
fooWithBazBaxPredicate.forEach { print($0.id) } // Foo A, Foo B
You can use the following extension on Collection. Not tested.
extension Collection where Iterator.Element: Equatable {
func uniques(by equals: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {
var uniqueElems: [Iterator.Element] = []
for elem in self {
if uniqueElems.index(where: { equals($0, elem) }) == nil {
uniqueElems.append(elem)
}
}
return uniqueElems
}
}
Then you can use
filteredArray.uniques { $0.manufShort == $1.manufShort }
filteredArray.uniques { $0.manufShort == $1.manufShort && $0.modelName == $1.modelName && $0.modelNumber == $1.modelNumber }

Swift 2 issue with a closure for groupBy collection

The code below is written in order to group journal publications by year of publication.
But I got the error "Cannot convert value of type '(Journal) -> Int' to expected argument type '(_) -> _'"
Here's the playground with a stripped down version of the real code for you to play around http://swiftlang.ng.bluemix.net/#/repl/1de81132cb2430962b248d2d6ff64922e2fe912b1480db6a7276c6a03047dd89
class Journal {
var releaseDate: Int = 0
static var journals = [Journal]()
class func groupedReduce<S: SequenceType, K: Hashable, U> (
source: S,
initial: U,
combine: (U, S.Generator.Element) -> U,
groupBy: (S.Generator.Element) -> K
)
-> [K:U]
{
var result: [K:U] = [:]
for element in source {
let key = groupBy(element)
result[key] = combine(result[key] ?? initial, element)
}
return result
}
class func groupBy() {
let byYear = { (journal: Journal) in
journal.releaseDate
}
let groupedJournals = groupedReduce(journals, initial: 0, combine:+, groupBy: byYear)
print("Grouped journals = \(groupedJournals)")
}
}
Journal.journals = [Journal(), Journal(), Journal(), Journal()]
for j in Journal.journals {
j.releaseDate = 1
}
Journal.groupBy()
Your code is overly complicated. Below is a groupBy function that group elements of an array according to criteria of your choice. Playground
import Foundation
class Journal {
var releaseDate: Int = 0
init(_ releaseDate: Int) {
self.releaseDate = releaseDate
}
}
extension Array {
func groupBy<T: Hashable>(f: Element -> T) -> [T: [Element]] {
var results = [T: [Element]]()
for element in self {
let key = f(element)
if results[key] == nil {
results[key] = [Element]()
}
results[key]!.append(element)
}
return results
}
func groupBy2<T: Hashable>(f: Element -> T) -> [T: [Element]] {
return self.reduce([T: [Element]]()) { (var aggregate, element) in
let key = f(element)
if aggregate[key] == nil {
aggregate[key] = [Element]()
}
aggregate[key]!.append(element)
return aggregate
}
}
}
let journals = [Journal(2015), Journal(2016), Journal(2015), Journal(2014)]
let groupedJournals = journals.groupBy {
$0.releaseDate
}
print(groupedJournals)

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
}