For the following code, how can I check if a member "b" or "f" exist for myArray?
struct example {
var a: String!
var b: Bool!
var c: Bool!
var d: String!
}
var myArray = [example]!
For example, if I check if member "f" exists, I would like something to return "false" or "nil"; and if I check if "b" exists, I would like to receive "true".
Thanks!
Unlike Objective-C, Swift does not have the dynamic mechanisms to do things like this. So the answer is that no, you cannot check for members by name in this way, unless you are working with members of an NSObject subclass which are marked with the #objc attribute.
Using Mirror.
let example = Example()
let containsB = Mirror(reflecting: example).children.contains { $0.0 == "b" } // true
let containsF = Mirror(reflecting: example).children.contains { $0.0 == "f" } // false
let examples = [Example(), Example(), Example()]
let containsA = examples.filter {
Mirror(reflecting: $0).children.contains { $0.0 == "a" }
}.isEmpty == false // true
As others have commented, there are other problems with your example, but assuming you know and are just throwing out a quick and dirty sample to illustrate your question, you could do something a bit like this:
if let bExists = myArray.b {
return true
} else if let fExists = myArray.f {
return false // or return nil, or whatever you want to do if `f exists.
}
Related
I have created enum with associated value and I want to be able to dynamically update associated value. As far as I know Swift doesn't support that at the moment.
Because of that I used following approach:
enum PersonInfo {
class EnumValue<T> {
var value: T
init(_ value: T) {
self.value = value
}
}
// Instead of using String or Bool or any other type directly, use EnumValue wrapper
case firstName(EnumValue<String>)
case lastName(EnumValue<String>)
case isAdult(EnumValue<Bool>)
}
I want to add function that would update EnumValue.value property in following way:
func updateAssociatedValue<V>(_ updateValue: V) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<V> else {
continue
}
value.value = updateValue
}
}
Problem is that this guard statement always fails (guard let value = associatedValue.value as? EnumValue<V>) and I can't figure it out why.
On the other hand, when I write updateAssociatedValue with typed type then things work properly:
// This works
func updateAssociatedValue(_ updateValue: String) {
let mirror = Mirror(reflecting: self)
for associatedValue in mirror.children {
guard let value = associatedValue.value as? EnumValue<String> else {
continue
}
value.value = updateValue
}
}
Things compile normally but during the runtime guard statement always fails. Am I using generic value in some incorrect way? Should I use somehow updateValue.Type or updateValue.self (I tried but it didn't work).
Example of usage:
var array: [PersonInfo] = [
.firstName(PersonInfo.EnumValue("John")),
.lastName(PersonInfo.EnumValue("Doe")),
.isAdult(PersonInfo.EnumValue(false))
]
print(array)
// John, Doe, false
array.first?.updateAssociatedValue("Mike")
print(array)
// Mike, Doe, false
I can always reassign enum value in array but if possible I want to avoid that. That's the reason for asking this question.
To search for a string included in a struct I use:
let results = myArray.filter( {$0.model.localizedCaseInsensitiveContains("bu")} )
But say the struct has several properties that I'd like to search - or maybe I'd even like to search all of them at one time. I can only filter primitive types so leaving 'model' out won't work.
Solution -------------------------
While I really liked the idea of using key paths as Matt suggested below, I ended up adding a function to my struct that made my view controller code much cleaner:
struct QuoteItem {
var itemIdentifier: UUID
var quoteNumber: String
var customerName: String
var address1: String
func quoteItemContains(_ searchString: String) -> Bool {
if self.address1.localizedCaseInsensitiveContains(searchString) ||
self.customerName.localizedCaseInsensitiveContains(searchString) ||
self.quoteNumber.localizedCaseInsensitiveContains(searchString)
{
return true
}
return false
}
Then, in my controller, quotes is an array of QuoteItem that I can search by simply writing:
searchQuoteArray = quotes.filter({ $0.quoteItemContains(searchString) })
This sounds like a job for Swift key paths. Just supply the key paths for the String properties you want to search.
struct MyStruct {
let manny = "Hi"
let moe = "Hey"
let jack = "Howdy"
}
let paths = [\MyStruct.manny, \MyStruct.moe, \MyStruct.jack]
let s = MyStruct()
let target = "y"
let results = paths.map { s[keyPath:$0].localizedCaseInsensitiveContains(target) }
// [false, true, true]
I hope i understood you correct. I think with this piece of code you can achieve what you want:
struct ExampleStruct {
let firstSearchString: String
let secondSearchString: String
}
let exampleOne = ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
let exampleTwo = ExampleStruct(firstSearchString: "Bye", secondSearchString: "Boy")
let exampleArray = [exampleOne, exampleTwo]
let searchString = "Hello"
let filteredArray = exampleArray.filter { (example) -> Bool in
// check here the properties you want to check
if (example.firstSearchString.localizedCaseInsensitiveContains(searchString) || example.secondSearchString.localizedCaseInsensitiveContains(searchString)) {
return true
}
return false
}
for example in filteredArray {
print(example)
}
This prints the following in Playgrounds:
ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
Let me know if it helps.
Let's say we have an Array, assigned to a variable with the type Any
let something: Any = ["one", "two", "three"]
Let's also assume we don't know if it's an array or something entirely else. And we also don't know what kind of Array.Element we are dealing with exactly.
Now we want to find out if it's an array.
let isArray = something is Array // compiler error
let isArray = (something as? [Any?] != nil) // does not work (array is [String] and not [Any?])
Is there any elegant solution to tickle the following information out of the swift type system:
Is the given object an Array
What's the count of the array
Give me the elements of the array
(bridging to NSArray is not a solution for me, because my array could also be of type [Any?] and contain nil-values)
I love #stefreak's question and his solution. Bearing in mind #dfri's excellent answer about Swift's runtime introspection, however, we can simplify and generalise #stefreak's "type tagging" approach to some extent:
protocol AnySequenceType {
var anyElements: [Any?] { get }
}
extension AnySequenceType where Self : SequenceType {
var anyElements: [Any?] {
return map{
$0 is NilLiteralConvertible ? Mirror(reflecting: $0).children.first?.value : $0
}
}
}
extension Array : AnySequenceType {}
extension Set : AnySequenceType {}
// ... Dictionary, etc.
Use:
let things: Any = [1, 2]
let maybies: Any = [1, nil] as [Int?]
(things as? AnySequenceType)?.anyElements // [{Some 1}, {Some 2}]
(maybies as? AnySequenceType)?.anyElements // [{Some 1}, nil]
See Swift Evolution mailing list discussion on the possibility of allowing protocol extensions along the lines of:
extension<T> Sequence where Element == T?
In current practice, however, the more common and somewhat anticlimactic solution would be to:
things as? AnyObject as? [AnyObject] // [1, 2]
// ... which at present (Swift 2.2) passes through `NSArray`, i.e. as if we:
import Foundation
things as? NSArray // [1, 2]
// ... which is also why this fails for `mabyies`
maybies as? NSArray // nil
At any rate, what all this drives home for me is that once you loose type information there is no going back. Even if you reflect on the Mirror you still end up with a dynamicType which you must switch through to an expected type so you can cast the value and use it as such... all at runtime, all forever outside the compile time checks and sanity.
As an alternative to #milos and OP:s protocol conformance check, I'll add a method using runtime introspection of something (foo and bar in examples below).
/* returns an array if argument is an array, otherwise, nil */
func getAsCleanArray(something: Any) -> [Any]? {
let mirr = Mirror(reflecting: something)
var somethingAsArray : [Any] = []
guard let disp = mirr.displayStyle where disp == .Collection else {
return nil // not array
}
/* OK, is array: add element into a mutable that
the compiler actually treats as an array */
for (_, val) in Mirror(reflecting: something).children {
somethingAsArray.append(val)
}
return somethingAsArray
}
Example usage:
/* example usage */
let foo: Any = ["one", 2, "three"]
let bar: [Any?] = ["one", 2, "three", nil, "five"]
if let foobar = getAsCleanArray(foo) {
print("Count: \(foobar.count)\n--------")
foobar.forEach { print($0) }
} /* Count: 3
--------
one
2
three */
if let foobar = getAsCleanArray(bar) {
print("Count: \(foobar.count)\n-------------")
foobar.forEach { print($0) }
} /* Count: 5
-------------
Optional("one")
Optional(2)
Optional("three")
nil
Optional("five") */
The only solution I came up with is the following, but I don't know if it's the most elegant one :)
protocol AnyOptional {
var anyOptionalValue: Optional<Any> { get }
}
extension Optional: AnyOptional {
var anyOptionalValue: Optional<Any> {
return self
}
}
protocol AnyArray {
var count: Int { get }
var allElementsAsOptional: [Any?] { get }
}
extension Array: AnyArray {
var allElementsAsOptional: [Any?] {
return self.map {
if let optional = $0 as? AnyOptional {
return optional.anyOptionalValue
}
return $0 as Any?
}
}
}
Now you can just say
if let array = something as? AnyArray {
print(array.count)
print(array.allElementsAsOptional)
}
This works for me on a playground:
// Generate fake data of random stuff
let array: [Any?] = ["one", "two", "three", nil, 1]
// Cast to Any to simulate unknown object received
let something: Any = array as Any
// Use if let to see if we can cast that object into an array
if let newArray = something as? [Any?] {
// You now know that newArray is your received object cast as an
// array and can get the count or the elements
} else {
// Your object is not an array, handle however you need.
}
I found that casting to AnyObject works for an array of objects. Still working on a solution for value types.
let something: Any = ["one", "two", "three"]
if let aThing = something as? [Any] {
print(aThing.dynamicType) // doesn't enter
}
if let aThing = something as? AnyObject {
if let theThing = aThing as? [AnyObject] {
print(theThing.dynamicType) // Array<AnyObject>
}
}
I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.
In my playground, I tried the following:
import UIKit
class myClass
{
var fieldOne:String?
var fieldTwo:Int?
var fieldThree:Float?
}
var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5
var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3
let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)
for thing in aMirror.children
{
for thing2 in bMirror.children
{
if thing.label! == thing2.label!
{
print("property: \(thing.label!)")
print("before: \(thing.value)")
print("after: \(thing2.value)")
print("")
//let myTest = thing.value == nil ? "nil" : "not nil"
}
}
}
And it generates the following output:
property: fieldOne
before: Optional("blah")
after: Optional("stuff")
property: fieldTwo
before: nil
after: Optional(3)
property: fieldThree
before: Optional(3.5)
after: nil
As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:
playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed
And yet, we know from the output that it IS nil. How can this be and what can I do about it?
Based on this answer, I recommend using if case Optional<Any>.some(_).
For example:
aMirror.children.forEach {
guard let propertyName = $0.label else { return }
if case Optional<Any>.some(_) = $0.value {
print("property: \(propertyName) is not nil")
} else {
print("property: \(propertyName) is nil")
}
}
Thats look like some sort of bug. Look at that
let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.
let y = { (value:Any?) in
return value == nil ? "Nil" : "Not Nil"
}
let z = y(childMirror.value) //compile, but doesn't evaluate.
I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:
func getValue(unknownValue:Any) -> Any {
let value = Mirror(reflecting: unknownValue)
if value.displayStyle != .Optional || value.children.count != 0 {
return "Not Nil"
} else {
return "Nil"
}
}
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;
}
}