I was able to retrieve a binding Swift Array from a Kotlin MutableList source,but I can’t find the way to retrieve a binding Swift Array from a Kotlin MutableMap of MutableLists source, given the map key.
=====
Kotlin MutableList to binding Swift Array
Kotlin MutableList definition:
var path : MutableList<ScreenIdentifier> = mutableListOf()
retrieving Binding Swift Array => SUCCESSFUL!
extension Binding where Value == NSMutableArray {
public func cast() -> Binding<[ScreenIdentifier]> {
return Binding<[ScreenIdentifier]>(
get:{ self.wrappedValue as NSArray as! [ScreenIdentifier] },
set: { self.wrappedValue = NSMutableArray(array: $0) }
)
}
}
===
NavigationStack(path: $path.cast()) {
...
}
===
=====
Kotlin MutableMap of MutableList to binding Swift Array
Kotlin MutableMap of MutableLists definition:
var paths : MutableMap<String, MutableList<ScreenIdentifier>> = mutableMapOf()
retrieving Binding Swift Array => NOT WORKING
extension Binding where Value == NSMutableDictionary {
public func getPath(level1URI: String) -> Binding<[ScreenIdentifier]> {
return Binding<[ScreenIdentifier]>(
get:{
let dict = self.wrappedValue as NSDictionary as! [String:[ScreenIdentifier]]
return dict[level1URI] as! NSMutableArray as NSArray as! [ScreenIdentifier]
},
set: {
var modifiedDict = self.wrappedValue as! [NSString:NSMutableArray]
modifiedDict[level1URI as NSString] = NSMutableArray(array: $0)
self.wrappedValue = NSMutableDictionary(dictionary: modifiedDict as! KotlinMutableDictionary<NSString,NSMutableArray>)
}
)
}
}
===
NavigationStack(path: $paths.getPath(level1URI: myString)) {
...
}
===
this is the error given by XCode:
Referencing instance method 'getPath(level1URI:)' on 'Binding'
requires the types 'KotlinMutableDictionary<NSString, NSMutableArray>'
and 'NSMutableDictionary' be equivalent
I found it!!!
On the Kotlin documentation, it's explained that Kotlin's MutableMap cannot cast directly to NSMutableDictionary, unlike MutableList which can instead cast directly to NSMutableArray.
So, the solution is to ignore NSMutableDictionary and to deal directly with KotlinMutableDictionary.
Here is the correct extension code:
extension Binding where Value == KotlinMutableDictionary<NSString,NSMutableArray> {
public func getPath(level1URI: String) -> Binding<[ScreenIdentifier]> {
return Binding<[ScreenIdentifier]>(
get:{
let dict = self.wrappedValue as! [String:[ScreenIdentifier]]
return dict[level1URI]!
},
set: {
var modifiedDict = self.wrappedValue as! [NSString:NSMutableArray]
modifiedDict[level1URI as NSString] = NSMutableArray(array: $0)
self.wrappedValue = KotlinMutableDictionary<NSString, NSMutableArray>.init(dictionary: modifiedDict)
}
)
}
}
Related
I'm trying to convert a `struct1 to Realm objects right now.
Realm object has same keypath with original struct.
so If I can get all writable keypaths from original struct, it is possible to convert with general method.
public protocol KeyPathListable {
var allKeyPaths:[WritableKeyPath<Self, Any>] { get }
}
extension KeyPathListable {
private subscript(checkedMirrorDescendant key: String) -> Any {
return Mirror(reflecting: self).descendant(key)!
}
var allKeyPaths:[WritableKeyPath<Self, Any>] {
var membersTokeyPaths = [WritableKeyPath<Self,Any>]()
let mirror = Mirror(reflecting: self)
for case (let key?, _) in mirror.children {
if let keyPath = \Self.[checkedMirrorDescendant: key] as? WritableKeyPath<Self, Any> {
membersTokeyPaths.append(keyPath)
}
}
return membersTokeyPaths
}
}
Just found the code snippet above but it returns KeyPath(not WritableKeyPath). I tried to typecast in this case, but it returns nil. Maybe mirror function has problem. Is there any solution for that?
I'm trying to get all the members of a generic class T, I can get the properties based on a specific class.
But, how I can do it using Mirror ?
let mirrored_object = Mirror(reflecting: user)
for (index, attr) in mirrored_object.children.enumerated() {
if let propertyName = attr.label as String! {
print("Attr \(index): \(propertyName) = \(attr.value)")
}
}
I added this as extension
extension NSObject {
public func GetAsJson() -> [[String:Any?]] {
var result:[[String: Any?]] = [[String: Any?]]()
for item in self {
var dict: [String: Any?] = [:]
for property in Mirror(reflecting: self).children {
dict[property.label!] = property.value
}
result.append(dict)
}
return result
}
}
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>
I need to convert a Dictionary with mixed case keys in the same exact Dictionary but with only lowercase keys.
Here is my attempt (It works but I found this implementation extremely rough)
extension Dictionary {
func lowercaseKeys()->Dictionary<String, AnyObject>{
var newDictionary = Dictionary<String,AnyObject>()
for k in keys{
if let k_string = k as? String{
newDictionary[k_string.lowercaseString] = self[k] as? AnyObject
}
}
return newDictionary
}
}
Can you suggest a more elegant way to solve this problem?
Changes its own keys without the need of a temporary dictionary ;)
var dict = ["HEJ":"DÅ", "NeJ":"tack"]
for key in dict.keys {
dict[key.lowercaseString] = dict.removeValueForKey(key)
}
print(dict)
[hej: DÅ, nej: tack]
EDIT
I made this extension, its a little dirty for now but I will update it again.
extension Dictionary {
mutating func lowercaseKeys() {
for key in self.keys {
let str = (key as! String).lowercaseString
self[str as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["HeJ":"Då", "nEJ":"taCK!"]
dict.lowercaseKeys()
print(dict)
["hej": "Då", "nej": "taCK!"]
EDIT 2
extension Dictionary where Key: StringLiteralConvertible {
mutating func lowercaseKeys() {
for key in self.keys {
self[String(key).lowercaseString as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["NamE":"David", "LAST_NAME":"Göransson"]
dict.lowercaseKeys() // Will compile
var dict2 = [0:"David", 0:"Göransson"]
dict2.lowercaseKeys() // Won't compile because Key isn't StringLiteralConvertible
Smth like that?
var dict = ["KEY": "value"]
var newDict = [String: AnyObject]()
for (key, value) in dict {
newDict[key.lowercaseString] = value
}
Arbitur's answer, updated for Swift 3:
extension Dictionary where Key: ExpressibleByStringLiteral {
public mutating func lowercaseKeys() {
for key in self.keys {
self[String(describing: key).lowercased() as! Key] = self.removeValue(forKey: key)
}
}
}
Here is my solution, no force casting, totally safe.
protocol LowercaseConvertible {
var lowercaseString: Self { get }
}
extension String: LowercaseConvertible {}
extension Dictionary where Key: LowercaseConvertible {
func lowercaseKeys() -> Dictionary {
var newDictionary = Dictionary()
for k in keys {
newDictionary[k.lowercaseString] = self[k]
}
return newDictionary
}
}
Just as an alternative to looping, you could use the map function:
func lowercaseKeys () -> Dictionary<String, AnyObject> {
var newDictionary = Dictionary<String,AnyObject>()
Array(keys).map { key in
newDictionary[(key as! String).lowercaseString] = self[key] as? AnyObject
}
return newDictionary
}
How can I check in Swift if a value is an Array. The problem is that an array of type Int can apparently not be casted to an array of type Any. Suppose I have an array myArray of type Int and execute the following:
if let array = myArray as? [Any] { return true }
it doesn't return true (which surprises me actually). The same thing appears with dictionaries. I want a dictionary of type String, Any (meaning that Any can be any type). How can I check if it is?
Thanks in advance.
Got it working like this, although it's not as beautiful as I would've hoped:
protocol ArrayType {}
extension Array : ArrayType {}
let intArray : Any = [1, 2, 3]
let stringArray : Any = ["hi", "hello", "world"]
intArray is ArrayType // true
stringArray is ArrayType // true
EDIT: I think I misunderstood your question before, now I got it though:
let intArray = [1, 2, 3]
let anyArray = intArray.map{ $0 as Any }
This is the only way to my knowledge.
You can simply
array is Array<Any>
Got it now, with the idea of #Kametrixom. It's extraordinary ugly but at least it works.
private protocol CJSONArrayType {
func toAny() -> [Any]
}
extension Array: CJSONArrayType {
private func toAny() -> [Any] {
return self.map { $0 as Any }
}
}
extension NSMutableArray: CJSONArrayType { }
extension NSArray: CJSONArrayType {
private func toAny() -> [Any] {
var result = [Any]()
for item in self {
result.append(item as Any)
}
return result
}
}
private protocol CJSONDictionaryType {
func toStringAny() -> [String: Any]
}
extension Dictionary: CJSONDictionaryType {
private func toStringAny() -> [String : Any] {
var result = [String: Any]()
for item in self {
result[item.0 as! String] = item.1 as Any
}
return result
}
}
extension NSMutableDictionary: CJSONDictionaryType { }
extension NSDictionary: CJSONDictionaryType {
private func toStringAny() -> [String : Any] {
var result = [String: Any]()
for item in self {
result[item.0 as! String] = item.1 as Any
}
return result
}
}
If you want to parse JSON, there are only a few supported types which are at least AnyObject rather than Any.
Then it's very easy to check for Array
func checkArray(item : AnyObject) -> Bool {
return item is Array<AnyObject>
}
let integer = 1
let string = "one"
let array = ["one", "two", "three"]
let dictionary = ["one" : 1, "two" : 2]
checkArray(integer) // false
checkArray(string) // false
checkArray(array) // true
checkArray(dictionary) // false
Apple highly recommends to constrain the types at compile time as much as possible