more idiomatic swift test for optionset? - swift

Still getting used to the use of OptionSetType in Swift.
In good ol' C, if I had something like
typedef enum {
CHAR_PROP_BROADCAST =0x01,
CHAR_PROP_READ =0x02,
CHAR_PROP_WRITE_WITHOUT_RESP =0x04,
CHAR_PROP_WRITE =0x08,
CHAR_PROP_NOTIFY =0x10,
CHAR_PROP_INDICATE =0x20,
CHAR_PROP_SIGNED_WRITE =0x40,
CHAR_PROP_EXT =0x80
} CharacteristicProperty;
I could test a set of flags with something simple like:
if ((propertiesMask & (CHAR_PROP_NOTIFY | CHAR_PROP_INDICATE)) != 0) ...
The Swift alternative might look like
let properties:CBCharacteristicProperties = [.Write, .Read, .Indicate]
!properties.intersect([.Indicate, .Notify]).isEmpty
Is there a more idiomatic way to do this test? Not a fan of the ! out front. But otherwise, seems straightforward enough, except I'm really interested in when there IS an intersection. This led me to want to add my own.
extension OptionSetType {
func hasIntersection(other:Self) -> Bool {
return !self.intersect(other).isEmpty
}
}
Which then allows me to write
properties.hasIntersection([.Indicate, .Notify])
Is there a better/more idiomatic way to do this? Did I roll my own and miss something?

There's this method from the protocol SetAlgebraType which OptionSetType implements:
isDisjointWith(_: Self) -> Bool
Returns true iff self.intersect(other).isEmpty.
So you could shorten your test to:
!properties.isDisjointWith([.Indicate, .Notify])
or
properties.isDisjointWith([.Indicate, .Notify]) == false
You can also compare the raw values with bitwise operators, just as you would do in C:
(properties.rawValue & (CharacteristicProperties.Notify.rawValue | CharacteristicProperties.Indicate.rawValue)) != 0
Full example code (in a playground):
struct CBCharacteristicProperties : OptionSetType {
let rawValue: UInt
init(rawValue: UInt) { self.rawValue = rawValue }
static let Broadcast = CBCharacteristicProperties(rawValue:0x01)
static let Read = CBCharacteristicProperties(rawValue:0x02)
static let WriteWithoutResp = CBCharacteristicProperties(rawValue:0x04)
static let Write = CBCharacteristicProperties(rawValue:0x08)
static let Notify = CBCharacteristicProperties(rawValue:0x10)
static let Indicate = CBCharacteristicProperties(rawValue:0x20)
static let SignedWrite = CBCharacteristicProperties(rawValue:0x40)
static let Ext = CBCharacteristicProperties(rawValue:0x80)
}
let properties = CBCharacteristicProperties([.Write, .Read, .Indicate])
print(!properties.intersect([.Indicate, .Notify]).isEmpty)
print(!properties.isDisjointWith([.Indicate, .Notify]))
print(properties.isDisjointWith([.Indicate, .Notify]) == false)
print((properties.rawValue & (CBCharacteristicProperties.Notify.rawValue | CBCharacteristicProperties.Indicate.rawValue)) != 0)
result:
"true"
"true"
"true"
"true"

The actual solution I found most appealing in the end was to simply add the following extension:
extension SetAlgebraType {
var notEmpty:Bool {
return self.isEmpty.NOT
}
}
This allowed me to write code:
if properties.intersect([.Indicate, .Notify]).notEmpty {
...
}

Related

Perform OR operation to all elements in array

I want to extend Arrays of Enums: Numeric with a function OR() that ORs all element in the array.
This is what I came up:
extension Array where Element: RawRepresentable, Element.RawValue: Numeric {
func OR(_: Array) -> Element.RawValue {
return self.map{ $0.rawValue }.reduce(0x00000000){ $0|$1 }
}
}
and this is the error thrown by the compiler:
Cannot invoke 'reduce' with an argument list of type '(Int, (_, _) ->
_)'
I want use it in situation like:
enum RendererFlags: CUnsignedInt {
case software = 0x00000001 // The renderer is a software fallback
case accelerated = 0x00000002 // The renderer uses hardware acceleration
case presentVSync = 0x00000004 // Present is synchronized with the refresh rate
case targetTexture = 0x00000008 // The renderer supports rendering to texture
}
or enums with other numeric rawValues, and then
let flags = [.software, .accelerated]OR()
Where is my error? Why the compiler is not happy with this?
Numeric is not enough for your needs, you need BinaryInteger:
extension Array where Element: RawRepresentable, Element.RawValue: BinaryInteger {
func OR() -> Element.RawValue {
return self.map{ $0.rawValue }.reduce(0) { $0 | $1 }
}
}
Also OR should be without parameters and you need to specify the type somewhere:
let flags = ([.software, .accelerated] as [RendererFlags]).OR()
However, this can be implemented easier using OptionSet:
struct RendererFlags: OptionSet {
let rawValue: CUnsignedInt
static let software = RendererFlags(rawValue: 1 << 0)
static let accelerated = RendererFlags(rawValue: 1 << 1)
static let presentVSync = RendererFlags(rawValue: 1 << 2)
static let targetTexture = RendererFlags(rawValue: 1 << 3)
}
let flags: RendererFlags = [.software, .accelerated]
The OR operation is already implemented for you and the options behave like an array therefore you don't have to worry about mask operations.

How to find if a string contains all the characters of a string for live searching in Swift?

I'm trying to implement search inside my app that I'm making. I have an array that I'm trying to search and I find this code online:
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter({( candy : Candies) -> Bool in
if candy.name.lowercaseString.containsString(searchText.lowercaseString) == true {
return true
} else {
return false
}
})
tableView.reloadData()
}
The issue is that the database that I'm trying to implement search on has text that is all scrambled because it was supposed to shortened. How can I make it so that the search will check if all the letters are there instead of searching exactly the right name. Example of object from database (USDA): CRAB, DUNGINESS, RAW
If you have an answer, please make it fast enough for live searching. Non live searching makes searching terrible (at least for me)!
I'm using Swift 2.2 and Xcode 7
As an improvement to #appzYourLife's solution, you could do this with a native Swift Set, as a counted set isn't necessarily needed in this case. This will save having to map(_:) over the characters of each name and bridging them to Objective-C. You can now just use a set of Characters, as they're Hashable.
For example:
struct Candy {
let name: String
}
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = candies.filter {Set($0.name.lowercaseString.characters).isSupersetOf(searchCharacters)}
tableView.reloadData()
}
filterContentForSearchText("RA")
print(filteredCandies) // [Candy(name: "CRAB"), Candy(name: "RAW")]
filterContentForSearchText("ED")
print(filteredCandies) // Candy(name: "DUNGINESS")]
Also depending on whether you can identify this as a performance bottleneck (you should do some profiling first) – you could potentially optimise the above further by caching the sets containing the characters of your 'candy' names, saving from having to recreate them at each search (although you'll have to ensure that they're updated if you update your candies data).
When you come to search, you can then use zip(_:_:) and flatMap(_:) in order to filter out the corresponding candies.
let candies = [Candy(name: "CRAB"), Candy(name: "DUNGINESS"), Candy(name: "RAW")]
// cached sets of (lowercased) candy name characters
let candyNameCharacterSets = candies.map {Set($0.name.lowercaseString.characters)}
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let searchCharacters = Set(searchText.lowercaseString.characters)
filteredCandies = zip(candyNameCharacterSets, candies).flatMap {$0.isSupersetOf(searchCharacters) ? $1 : nil}
tableView.reloadData()
}
First of all a block of code like this
if someCondition == true {
return true
} else {
return false
}
can also be written this ways
return someCondition
right? :)
Refactoring
So your original code would look like this
func filterContentForSearchText(searchText: String) {
filteredCandies = candies.filter { $0.name.lowercaseString.containsString(searchText.lowercaseString) }
tableView.reloadData()
}
Scrambled search
Now, given a string A, your want to know if another string B contains all the character of A right?
For this we need CountedSet which is available from Swift 3. Since you are using Swift 2.2 we'll use the old NSCountedSet but some bridging to Objective-C is needed.
Here's the code.
struct Candy {
let name: String
}
let candies = [Candy]()
var filteredCandies = [Candy]()
func filterContentForSearchText(searchText: String) {
let keywordChars = NSCountedSet(array:Array(searchText.lowercaseString.characters).map { String($0) })
filteredCandies = candies.filter {
let candyChars = NSCountedSet(array:Array($0.name.lowercaseString.characters).map { String($0) }) as Set<NSObject>
return keywordChars.isSubsetOfSet(candyChars)
}
tableView.reloadData()
}
Swift 3 code update :
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredCandies = candies.filter { candy in
return candy.name.localizedLowercase.contains(searchText.lowercased())
}
tableView.reloadData()
}

Comparing array of force unwrapped optionals

I'm writing a test:
func test_arrayFromShufflingArray() {
var videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
var shuffledVideos = tuple.0
let shuffleIndexMap = tuple.1
// -- test order is different
XCTAssert(videos != shuffledVideos, "test_arrayFromShufflingArray fail")
}
But on the last line I get the last line:
Binary operator '!=' cannot be applied to two '[VMVideo!]' operands
Arrays can be compared with == if the element type is Equatable:
/// Returns true if these arrays contain the same elements.
public func ==<Element : Equatable>(lhs: [Element], rhs: [Element]) -> Bool
But neither ImplicitlyUnwrappedOptional<Wrapped> nor Optional<Wrapped> conform to Equatable, even if the
underlying type Wrapped does.
Possible options are (assuming that VMVideo conforms to Equatable):
Change your code so that videos and shuffledVideos are
[VMVideo] arrays instead of [VMVideo!].
Compare the arrays elementwise:
XCTAssert(videos.count == shuffledVideos.count
&& !zip(videos, shuffledVideos).contains {$0 != $1 })
Define a == operator for arrays of implicitly unwrapped equatable
elements:
func ==<Element : Equatable> (lhs: [Element!], rhs: [Element!]) -> Bool {
return lhs.count == rhs.count && !zip(lhs, rhs).contains {$0 != $1 }
}
Swift can't tell how to compare two arrays to see if their contents are identical unless it knows how to compare individual elements. So you need to implement the == function on your class and adopt Equatable:
extension VMVideo: Equatable {
// nothing goes here, == function has to be at global scope
}
func ==(lhs: VMVideo, rhs: VMVideo) -> Bool {
// Up to you to determine what equality means for your object, e.g.:
return lhs.essentialProperty1 == rhs.essentialProperty1 &&
lhs.essentialProperty2 == rhs.essentialProperty2
}
EDIT To clarify how it interacts with NSObject and to troubleshoot your environment, please confirm the following:
class UnderstandsEqual: NSObject {}
let ok1: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
let ok2: [UnderstandsEqual] = [UnderstandsEqual(), UnderstandsEqual()]
ok1 == ok2 // no problem, evaluates to true
class DoesntUnderstand {}
let bad1: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
let bad2: [DoesntUnderstand] = [DoesntUnderstand(), DoesntUnderstand()]
bad1 == bad2 // produces a compile-time error
Martin R's answer was the better answer, but for this specific purpose I just converted to NSArray's and was able to use the == operator in Swift:
func test_arrayFromShufflingArray() {
let videos = [MockObjects.mockVMVideo_1(), MockObjects.mockVMVideo_2(), MockObjects.mockVMVideo_3()]
let videosNSArray: NSArray = videos.map { $0 }
let tuple = ShuffleHelper.arrayFromShufflingArray(videos, currentIndex:1)
let shuffledVideos = tuple.0
let shuffledVideosNSArray: NSArray = shuffledVideos.map { $0 }
// -- test order is different
XCTAssert(videosNSArray != shuffledVideosNSArray, "test_arrayFromShufflingArray fail")
// -- test elements are the same
let set = NSSet(array: videos)
let shuffledSet = NSSet(array: shuffledVideos)
XCTAssert(set == shuffledSet, "test_arrayFromShufflingArray fail")
}

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

Get a Swift Variable's Actual Name as String

So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}