Swift: Casting [UInt32] to AnyObject - iphone

I have a UInt32 array of Ints defined as: var myArr : [UInt32] = [1, 2, 3] how can I convert it to AnyObject type ? I've tried the forced downcast as! AnyObject but the compiler gives this warning: treating a forced downcast to AnyObject as optional will never produce nil

A UInt32 is not like an Int. You cannot cast a UInt32 to an AnyObject, as they are not bridged. You will have to wrap every UInt32 in the array in an NSNumber explicitly, using map.

Related

Double primitive type is "AnyObject" however it cannot conform to protocols that require an AnyObject type [duplicate]

I read inline documentation of Swift and I am bit confused.
1) Any is a protocol that all types implicitly conform.
2) AnyObject is a protocol to which all classes implicitly conform.
3) Int, Float, Double are structs
Here is a sample code:
import UIKit
func passAnyObject(param: AnyObject) {
print(param)
}
class MyClass {}
struct MyStruct {}
let a: Int = 1
let b = 2.0
let c = NSObject()
let d = MyClass()
let e = MyStruct()
passAnyObject(a)
passAnyObject(b)
passAnyObject(c)
passAnyObject(d)
//passAnyObject(e) // Argument type 'MyStruct' does not conform to expected type 'AnyObject'
if a is AnyObject { // b, d, e is also AnyObject
print("\(a.dynamicType) is AnyObject")
}
What I don't understand is why Int, Double, Float are AnyObjects? Why compiler doesn't say anything? Those types are declared as structs. Struct MyStruct cannot be passed to the method on the top because it does not conform to AnyObject.
Could you help me understand why Int, Double and Float are AnyObject or why compiler thinks they are?
Because you have Foundation imported, Int, Double, and Float get converted to NSNumber when passed to a function taking an AnyObject. Type String gets converted to NSString. This is done to make life easier when calling Cocoa and Cocoa Touch based interfaces. If you remove import UIKit (or import Cocoa for OS X), you will see:
error: argument type 'Int' does not conform to expected type 'AnyObject'
when you call
passAnyObject(a)
This implicit conversion of value types to objects is described here.
Update for Swift 3 (Xcode 8 beta 6):
Passing an Int, Double, String, or Bool to a parameter of type AnyObject now results in an error such as Argument of type 'Int' does not conform to expected type 'AnyObject'.
With Swift 3, implicit type conversion has been removed. It is now necessary to cast Int, Double, String and Bool with as AnyObject in order to pass it to a parameter of type AnyObject:
let a = 1
passAnyObject(a as AnyObject)
Good find! UIKit actually converts them to NSNumber - also mentioned by #vacawama. The reason for this is, sometimes you're working with code that returns or uses AnyObject, this object could then be cast (as!) as an Int or other "structs".
class Test {
static func test() {
let anyObjectsValues: [AnyObject] = [1, "Two", 3, "Four"] as [AnyObject]
anyObjectsValues.forEach { (value) in
switch value {
case is Int:
print("\(value) is an Int!")
case is String:
print("\(value) is a String!")
default:
print("\(value) is some other type!")
}
}
}
}
I have not imported UIKit or Foundation frameworks. Why compiler is not giving any error? Even it printing the result.
Output:
1 is an Int!
Two is a String!
3 is an Int!
Four is a String!
Does anybody have an idea?

Swift: mutate cast parameter (Error: Cannot use mutating member on immutable value of type 'MyObjectType' (aka 'Dictionary<Int, Int>')

I have a functions where I passed in Dictionary [Int : Int] as inout T and would need to write into the Dictionary. This function is included as a function of a protocol.
However, after casting T into MyObjectType (Dictionary), it becomes immutable.
Question: How can I make a generic parameter (it can be passed in as struct, class, array or dictionary) mutable after casting?
typealias MyObjectType = [Int : Int] // Key is a enum with Int as RawValue
static func myGenericFunction<T>(_ object: inout T) {
(object as! MyObjectType).updateValue(0, forKey: 1)
}
I have tried casting it to NSMutableDictionary as below which compile successfully but crash when running with simulator.
(object as! NSMutableDictionary)[Int(pMetric.selectedUnit)] = Int(pMetric.metricType)
Thanks for any good solution!

Why casting function type as AnyObject works

Question : Difference between Any vs. AnyObject
Answer :
Any can represent an instance of any type at all, including function types and optional types.
AnyObject can represent an instance of any class type.
I tried to store a function type in a Any and a AnyObject variables
func add(a: Int, b: Int) -> Int {
return a + b
}
let funcType = add
let test1: Any = funcType
let test2: AnyObject = funcType//Value of type '(Int, Int) -> Int' does not conform to specified type 'AnyObject' Insert ' as AnyObject'
When I use the fix option
let test2: AnyObject = funcType as AnyObject
It works without any error. How am I able to store a function type in a AnyObject?
Behind the scenes as AnyObject converts the casted value to an Objective-C compatible one: Int's become NSNumber, array's become NSArray, and so on. Swift-only values get wrapped within opaque SwiftValue instances.
print(type(of: test2)) // __SwiftValue
This is why adding as AnyObject makes your code compile, as the right-hand of the operator is now an object.

Swift 3: Cannot convert value of type 'NSMutableDictionary' to expected argument type '[AnyHashable : Any]!'

This code worked before Swift 3. (Curse you Swift 3!)
Now it's showing this error against the Flurry.logEvent(eventName, withParameters: userData!) line:
Cannot convert value of type 'NSMutableDictionary' to expected
argument type '[AnyHashable : Any]!'
Casting userData! to [AnyHashable : Any] produces this error:
Cannot convert value of type 'NSMutableDictionary' to type
'[AnyHashable : Any]' in coercion
func logEvent(_ eventName: String, userData: NSMutableDictionary?) {
// Use <userData> or create new one?
var userData = userData
if userData == nil {
userData = NSMutableDictionary()
}
// Set base properties
userData!.setObject(gUser.tofus.count, forKey: "Num Tofus" as NSCopying)
userData!.setObject(gUser.getLifetimeTofus(), forKey: "Num Lifetime Tofus" as NSCopying)
// Call Flurry
DispatchQueue.main.async {
Flurry.logEvent(eventName, withParameters: userData! as [AnyHashable:Any])
}
}
What's the right syntax for Swift 3?
If that Flurry.logEvent(_:withParameters:) takes [AnyHashable: Any], why don't you use it as your local userData?
func logEvent(_ eventName: String, userData: NSMutableDictionary?) {
// Use <userData> or create new one?
var userData = userData as NSDictionary? as? [AnyHashable: Any] ?? [:]
// Set base properties
userData["Num Tofus"] = gUser.tofus.count
userData["Num Lifetime Tofus"] = gUser.getLifetimeTofus()
// Call Flurry
DispatchQueue.main.async {
Flurry.logEvent(eventName, withParameters: userData)
}
}
UPDATE
Xcode 8.1 GM seed including SE-0139 and SE-0140 is out, so the list below is updated.
These are the Objective-C safe types, when set to a [AnyHashable: Any] dictionary (or set in a [Any] array, or simply passed to Any which is a non-null id in Objective-C) in which is passed to Objective-C world:
Swift 3.0.1/Xcode 8.1
Optional values including nil
nil is converted to NSNull, all non-nil Optionals are unwrapped.
(NSNull may not be what you want. Still be careful about nil-checking.)
All numeric types and Bool
Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, as well as
Int, UInt, Double, Float, CGFloat and Bool. These are converted to NSNumber.
String
Converted to NSString.
Array, where Element is Objective-C safe
Converted to NSArray.
Dictionary, where Key and Value are Objective-C safe
Converted to NSDictionary.
Set, where Element is Objective-C safe
Converted to NSSet
NSObject descendent types
Not converted, used as is.
Value types which have counter-part reference types
See the list here.
Value types where NSValue has an initializer for
NSRange,
CGPoint,
CGVector,
CGSize,
CGRect,
CGAffineTransform,
UIEdgeInsets,
UIOffset,
CATransform3D,
CMTime,
CMTimeRange,
CMTimeMapping,
CLLocationCoordinate2D,
MKCoordinateSpan,
SCNVector3,
SCNVector4,
SCNMatrix4.
These types are converted to NSValue. (NSRange was already convertible to NSValue in older Swifts, but not well-documented.)
Bad things (example)
Still some values may be converted to _SwiftValue even in Swift 3.0.1.
Swift only types such as (Swift-only)enum, struct, tuple...
(See this list.)
I haven't checked all wrapper enums and structs, but some of them (for example, Notification.Name to NSString) seem to be safely converted.
Swift 3.0.0/Xcode 8.0
Non-Optional numeric types and Bool
Int, UInt, Double, Float, CGFloat and Bool. These are converted to NSNumber.
Non-Optional String
Converted to NSString.
Non-Optional Array, where Element is Objective-C safe
Converted to NSArray.
Non-Optional Dictionary, where Key and Value are Objective-C safe
Converted to NSDictionary.
Non-Optional Set, where Element is Objective-C safe
Converted to NSSet
Non-Optional NSObject descendent types
Not converted, used as is.
Non-Optional value types which have counter-part reference types
See the list here. (The linked article is updated for Swift 3.0.1.)
Bad things (example)
These may be converted to _SwiftValue, which is completely useless and disastrous in Objective-C world.
Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64
Any Optional values including nil
Swift only types such as (Swift-only)enum, struct, tuple...

Swift [1,2] conforms to AnyObject but [Enum.a, Enum.b] does not

I'm in AppDelegate, trying to pass a reply to a WatchKit Extension Request. I cannot use an array of enums as the value in a Dictionary whose values are typed as AnyObject. Experimenting in a Playground shows this:
enum E : Int {
case a = 0
case b
}
var x : AnyObject = [0, 1] // OK
var y : AnyObject = [E.a, E.b] // [E] is not convertible to AnyObject
Of course I can work around this by converting my enums to strings or numbers, but why is this a type error in Swift?
AnyObject exists for compatibility with Objective-C. You can only put objects into an [AnyObject] array that Objective-C can interpret. Swift enums are not compatible with Objective-C, so you have to convert them to something that is.
var x: AnyObject = [0, 1] works because Swift automatically handles the translation of Int into the type NSNumber which Objective-C can handle. Unfortunately, there is no such automatic conversion for Swift enums, so you are left to do something like:
var y: AnyObject = [E.a.rawValue, E.b.rawValue]
This assumes that your enum has an underlying type that Objective-C can handle, like String or Int.
Another example of something that doesn't work is an optional.
var a: Int? = 17
var b: AnyObject = [a] // '[Int?]' is not convertible to 'AnyObject'
See Working with Cocoa Data Types for more information.