The following code works:
nil as! String?
"muffin" as! String?
Therefore I also expect this to work:
var magicArray: [Any?] = ["Muffin", nil, 3]
class Box<T> {
var index: Int
init(index: Int){
self.index = index
}
func get() -> T {
return magicArray[index] as! T //crash
}
}
But neither
let box = Box<String?>(index: 0)
box.get()
nor
let box = Box<String?>(index: 1)
box.get()
works as expected. The program crashes at the cast in get(). This however works:
let box = Box<Int>(index: 2)
box.get()
I need to be able to cast a Any? value to T inside my class where T can be any type, including optionals. The Any? actually comes from an array of Any? so there is no other way of verifying it’s of the correct type T.
Is this possible?
If you need a generic wrapper, this should work for you:
final class Wrapper<T> {
final let wrappedValue: T
init(theValue: T) {
wrappedValue = theValue
}
}
You can replace let with var to make it mutable.
Edit:
This is the code I used to test it on a playground.
var s : Any?
s = "some string"
let w = Wrapper(theValue: s)
w.wrappedValue
let int = 3
let w2 = Wrapper(theValue: int)
w2.wrappedValue
w2.wrappedValue = 3
let array : [Any?] = [s, int, nil, "abc", NSObject()]
for obj in array {
let wrapper = Wrapper(theValue: obj)
}
Related
I'm very new in Swift so i might be missing some basics.
I have struct:
struct MyStruct {
var a: Int
var b: String
var c: Bool
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
}
and function, that should iterate through given struct properties and return their types in json:
func structProps(){
let elm = MyStruct()
let mirror = Mirror(reflecting: elm)
var exampleDict: [String: String] = [:]
for child in mirror.children {
exampleDict[child.label!] = String(describing:type(of: child.value)) as String
}
if let theJSONData = try? JSONSerialization.data(
withJSONObject: exampleDict,
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
}
}
it kinda return what i need:
JSON string = {"a":"Int","b":"String","c":"Bool"}
Because i'm having a lot of structs and i want to export json from all of them, i'm wondering if there is a way to have generic initializer. Without passing default values.
It means without
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
If I understand correctly , you can create a base protocol and add as an extension to your structures like
protocol BaseFunction {
func getElements() -> [String : String]
func getDict() -> String
}
extension BaseFunction {
func getElements() -> [String : String] {
let mirror = Mirror(reflecting: self)
let propertiesRemoveNil = mirror.children.filter({!(($0.value as AnyObject) is NSNull)})
let properties = propertiesRemoveNil.compactMap({$0.label})
var types = [String]()
_ = mirror.children.forEach({
types.append(String(describing:type(of: $0.value)))
})
return Dictionary(uniqueKeysWithValues: zip(properties, types))
}
func getDict() -> String{
if let theJSONData = try? JSONSerialization.data(
withJSONObject: getElements(),
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
return theJSONText ?? ""
}
return ""
}
}
And usage :
func structProps(){
let elm = MyStruct()
print(elm.getDict())
}
OUTPUT :
{"a":"Int","b":"String","c":"Bool"}
I was going to write a comment about using a protocol but I thought it would be easier to understand as an answer with some code.
To make the usage more generic so you don't need specific code for each type of struct you should use a protocol. Instead of having an init that might clash with already existing init in the struct I prefer a static method that returns a new object, also known as a factory method
protocol PropertyExtract {
static func createEmpty() -> PropertyExtract
}
Then we can make use of the default init for the struct or any supplied to create an object with some initial values by letting the struct conform to the protocol in an extension
extension MyStruct: PropertyExtract {
static func createEmpty() -> PropertyExtract {
MyStruct(a: 0, b: "", c: false)
}
}
And instead of hardcoding or passing a specific type of object to the encoding function we pass the type of it
func structProps(for structType: PropertyExtract.Type)
and use the protocol method to get an instance of the type
let object = structType.createEmpty()
The whole function (with some additional changes)
func structProps(for structType: PropertyExtract.Type) -> String? {
let object = structType.createEmpty()
let mirror = Mirror(reflecting: object)
let exampleDict = mirror.children.reduce(into: [String:String]()) {
guard let label = $1.label else { return }
$0[label] = String(describing:type(of: $1.value))
}
if let data = try? JSONEncoder().encode(exampleDict) {
return String(data: data, encoding: .utf8)
}
return nil
}
And this is then called with the type of the struct
let jsonString = structProps(for: MyStruct.self)
According to Creating a Swift Runtime Library, there is a way to access meta data types without initializer.
Solution for what i was asking for is possible with Runtime library.
Mirror(reflecting:) expects an instance of a type and not a type itself, ref.
One idea is to have a generic function that works with all types conforming to a protocol that provides an empty init. Something like:
protocol EmptyInitializable {
init()
}
struct StructOne {
let a: Bool
let b: String
}
struct StructTwo {
let c: Int
let d: Float
}
extension StructOne: EmptyInitializable {
init() {
a = false
b = ""
}
}
extension StructTwo: EmptyInitializable {
init() {
c = 1
d = 1.0
}
}
func print(subject: EmptyInitializable) -> String? {
let dictionary = Dictionary(uniqueKeysWithValues:
Mirror(reflecting: subject).children.map {
($0.label!, String(describing: type(of: $0.value)))
}
)
return (try? JSONSerialization.data(withJSONObject: dictionary)).flatMap {
String(data: $0, encoding: .utf8)
}
}
print(subject: StructOne()) // "{"a":"Bool","b":"String"}"
print(subject: StructTwo()) // "{"d":"Float","c":"Int"}"
You still have to implement the empty init and assign some values and I'm afraid there's no way currently to avoid that.
I was doing some research on AnyXXX struct like AnyHashable, then I write a similar container named MyAnyHashable.
There's some issues
when I using the original AnyHashalbe is Swift, it works fine:
func usingAppleAnyHashable() {
var dict = Dictionary<AnyHashable, Any>()
// using literal as key always works
dict[1] = 1
dict["efg"] = "efg_value"
// using some var as key still works
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
print(dict)
}
it can automatically convert Int's var into AnyHashable, what a Magic!
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
Then, I using MyAnyHashable as the Key's Type
func usingMyAnyHashable() {
var dict = Dictionary<MyAnyHashable, Any>()
// using literal as key always works
dict[1] = 1
dict["efg"] = "efg_value"
// it show errors: Cannot convert value of type 'Int' to expected argument type 'MyAnyHashable'...
let any1 = 100
let any2 = "abc"
dict[any1] = 100
dict[any2] = "abc_value"
print(dict)
}
I must write like this to manually create a MyAnyHashable instance
dict[MyAnyHashable(any1)] = 100
What is the Magic in Apple's AnyHashable?
How to make it automatically convert non-Literal var into MyAnyHashable?
Here is MyAnyHashable implementation, I even wrote a ExpressibleByXXXXLiteral which can automatically transform literalType to MyAnyHashable.
struct MyAnyHashable : Hashable, Equatable {
private let baseHash: Int
private let base: Any
init<H: Hashable>(_ base: H) {
self.base = base
self.baseHash = base.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.baseHash)
}
static func == (lhs: MyAnyHashable, rhs: MyAnyHashable) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
extension MyAnyHashable : ExpressibleByStringLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
typealias StringLiteralType = String
init(stringLiteral value: StringLiteralType) {
self.init(value)
}
typealias IntegerLiteralType = Int
init(integerLiteral value: IntegerLiteralType) {
self.init(value)
}
typealias FloatLiteralType = Float
init(floatLiteral value: FloatLiteralType) {
self.init(value)
}
}
screenshots
I'm using swift in my project. I have 2 object from same class(for example object A and object B from Class MyClass):
class MyClass: NSObject {
var someString: String = ""
var someInt: Int = 0
}
...
let A = MyClass()
A.someString = "A object"
A.someInt = 1
let B = MyClass()
B.someString = "B object"
B.someInt = 2
how I can check if same properties have same value, and if not return value and key of the property?
I think we can do this with using Mirror with 2 for loop inside each other, am I write?
I think you're looking for something like this pretty much:
import Foundation
class MyClass {
var someString: String = ""
var someInt: Int = 0
}
let a = MyClass()
a.someString = "A object"
a.someInt = 1
let b = MyClass()
b.someString = "B object"
b.someInt = 2
func compare<T: MyClass>(_ instance: T, with instance2: T) -> [String: AnyHashable] {
let sourceMirror = Mirror(reflecting: instance)
let targetMirror = Mirror(reflecting: instance2)
var output = [String: AnyHashable]()
for sourceChild in sourceMirror.children {
guard let label = sourceChild.label else { continue }
guard let targetChild = (targetMirror.children.first { $0.label! == label }) else {
fatalError("Failed to find target child, since types are same this fatal error should not be fired")
}
guard
let firstValue = sourceChild.value as? AnyHashable,
let secondValue = targetChild.value as? AnyHashable
else {
continue
}
guard firstValue != secondValue else { continue }
output[label] = secondValue
}
return output
}
for result in compare(a, with: b) {
print("label: \(result.key), value: \(result.value)")
}
The downside of this method is all of your fields must be conforming to Hashable protocol if you want to see the difference between these.
The output is:
label: someInt, value: 2
label: someString, value: B object
I have a generic dictionary, and built a generic function to access that dictionary (to prevent concurrency access problems).
My (singleton) data class looks like:
class AppData {
static let genericDict: Dictionary<String, Any> = [:]
static func genericGet<T: Any>(_ objID: String) -> T {
let retVal: T
mySemaphore.wait()
retVal = genericDict[objID] as! T
mySemaphore.signal()
return retVal
}
}
However, when I call my function like so:
class SomeClass {
let product: SomeObj = AppData.genericGet(objID) as! SomeObj
}
I get the error:
Generic parameter 'T' could not be inferred
I both explicitly and implicitly cast the type to my desired value. Not sure what else I can do to fix this issue.
I've tried restarting XCode, does not help.
The real code:
public class AppData: ObservableObject {
static private var dataDirectory: Dictionary<String, Any> = Dictionary(minimumCapacity: 10000)
static let dataGetLock = DispatchSemaphore(value: 1)
static func get<T: Any>(_ objID: String) -> T? {
let retVal: T
dataGetLock.wait()
retVal = dataDirectory[objID] as! T
dataGetLock.signal()
return retVal
}
}
I get the error on the following two lines:
class StoreViewEnvironment: ObservableObject {
func initProductLoad(storeID: String) {
...
let liveStore: Store = AppData.get(storeID) as! Store
let liveMenu: Menu = AppData.get(liveStore.menuID) as! Menu
...
}
}
I don't approve of the way you're doing this (on the general grounds that Any is just about always a bad smell), but here's a version that compiles (just delete the Any constraint):
class AppData {
static let genericDict: Dictionary<String, Any> = [:]
static func get<T>(_ objID: String) -> T {
let retVal: T
retVal = genericDict[objID] as! T
return retVal
}
}
class SomeClass {
let product : String = AppData.get("yoho")
}
Let say I have following code
class Foo {
}
var fooArray : Array<Foo> = Array<Foo>()
// This is important because in my code I will get Any (vs Array<Foo)
var fooArrayAny : Any = foo
I want to be able to get a Type Foo out of variable fooArrayAny.
If I had fooArray, I would do something like that:
let type = fooArray.dynamicType.Element().dynamicType
However, this doesn't work with fooArrayAny. It says that it has no member Element()
If you set NSObject as the base class of Foo, then you could use the following code:
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let arr = fooArray as? Array {
let i = arr.getArrayTypeInstance(arr)
print("i = \(i)")
}
}
}
class Foo: NSObject {
}
extension Array {
public func getArrayTypeInstance<T>(arr:Array<T>) -> T {
return arr.getTypeInstance()
}
public func getTypeInstance<T>() -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
This code is a snippet of my library EVReflection
Update:
I noticed a mistake in the code above. I used fooArray instead of fooArrayAny. When changing that to fooArrayAny I get the same error as you that the compiler does not have the Element. After playing around with this, I found out a solution that does work. Again it has parts of code of my EVReflection library.
class EVReflectionTests: XCTestCase {
func testArrayInstance() {
let fooArray : Array<Foo> = Array<Foo>()
let fooArrayAny : Any = fooArray
if let _ = fooArrayAny as? NSArray {
var subtype: String = "\(Mirror(reflecting: fooArrayAny))"
subtype = subtype.substringFromIndex((subtype.componentsSeparatedByString("<") [0] + "<").endIndex)
subtype = subtype.substringToIndex(subtype.endIndex.predecessor())
print("The type of the array elements = \(subtype)")
if let instance = swiftClassFromString(subtype) {
print("An instance of the array element = \(instance)")
let type = instance.dynamicType
print("An instance of the array element = \(type)")
}
}
}
// All code below is a copy from the EVReflection library.
func swiftClassFromString(className: String) -> NSObject? {
var result: NSObject? = nil
if className == "NSObject" {
return NSObject()
}
if let anyobjectype : AnyObject.Type = swiftClassTypeFromString(className) {
if let nsobjectype : NSObject.Type = anyobjectype as? NSObject.Type {
let nsobject: NSObject = nsobjectype.init()
result = nsobject
}
}
return result
}
func swiftClassTypeFromString(className: String) -> AnyClass! {
if className.hasPrefix("_Tt") {
return NSClassFromString(className)
}
var classStringName = className
if className.rangeOfString(".", options: NSStringCompareOptions.CaseInsensitiveSearch) == nil {
let appName = getCleanAppName()
classStringName = "\(appName).\(className)"
}
return NSClassFromString(classStringName)
}
func getCleanAppName(forObject: NSObject? = nil)-> String {
var bundle = NSBundle.mainBundle()
if forObject != nil {
bundle = NSBundle(forClass: forObject!.dynamicType)
}
var appName = bundle.infoDictionary?["CFBundleName"] as? String ?? ""
if appName == "" {
if bundle.bundleIdentifier == nil {
bundle = NSBundle(forClass: EVReflection().dynamicType)
}
appName = (bundle.bundleIdentifier!).characters.split(isSeparator: {$0 == "."}).map({ String($0) }).last ?? ""
}
let cleanAppName = appName
.stringByReplacingOccurrencesOfString(" ", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
.stringByReplacingOccurrencesOfString("-", withString: "_", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
return cleanAppName
}
}
class Foo: NSObject {
}
The output of this code will be:
The type of the array elements = Foo
An instance of the array element = <EVReflection_iOS_Tests.Foo: 0x7fd6c20173d0>
An instance of the array element = Foo
Swift 5
Its old but I want to share my version if someone needs it.
I use ModelProtocol and I suggests you use protocol so we can do operation to model via protocol (ex: static instantiating).
protocol ModelProtocol {}
class Foo: ModelProtocol {
}
Since I can't check type is Array, I use CollectionProtocol and create Array extension to get Element via protocol.
protocol CollectionProtocol {
static func getElement() -> Any.Type
}
extension Array: CollectionProtocol {
static func getElement() -> Any.Type {
return Element.self
}
}
Testing.
var fooArray: Array<Foo> = Array<Foo>()
var fooArrayAny: Any = fooArray
let arrayMirrorType = type(of: fooArrayAny)
String(describing: "arrayMirrorType: \(arrayMirrorType)")
if arrayMirrorType is CollectionProtocol.Type {
let collectionType = arrayMirrorType as! CollectionProtocol.Type
String(describing: "collectionType: \(collectionType)")
let elementType = collectionType.getElement()
String(describing: "elementType: \(elementType)")
let modelType = elementType as! ModelProtocol.Type
String(describing: "modelType: \(modelType)")
// ... now you can do operation to modelType via ModelProtocol
}
Printing.
arrayMirrorType: Array<Foo>
collectionType: Array<Foo>
elementType: Foo
modelType: Foo
class Foo {
var foo: Int = 1
}
struct Boo {
var boo: String = "alfa"
}
func f(array: Any) {
let mirror = Mirror(reflecting: array)
let arraytype = mirror.subjectType
switch arraytype {
case is Array<Foo>.Type:
let fooArray = array as! Array<Foo>
print(fooArray)
case is Array<Boo>.Type:
let booArray = array as! Array<Boo>
print(booArray)
default:
print("array is not Array<Foo> nor Array<Boo>")
break
}
}
var fooArray : Array<Foo> = []
fooArray.append(Foo())
var anyArray : Any = fooArray // cast as Any
f(anyArray) // [Foo]
var booArray : Array<Boo> = []
booArray.append(Boo())
anyArray = booArray // cast as Any
f(anyArray) // [Boo(boo: "alfa")]
var intArray : Array<Int> = []
anyArray = intArray // cast as Any
f(anyArray) // array is not Array<Foo> nor Array<Boo>