Why do integers not conform to the AnyObject protocol?
There are two kinds of anything types in Swift – Any
, which can truly hold anything – a struct, enum or class, and AnyObject
, which can only hold classes.
The reason why it seems like AnyObject
can hold structs sometimes is that some specific types are implicitly converted to their NSEquivalent for you as needed, to make Objective-C interop less painful.
When you write let ao: AnyObject = Int(1)
, it isn’t really putting an Int
into an AnyObject
. Instead, it’s implicitly converting your Int
into an NSNumber
, which is a class, and then putting that in.
But only some types have this implicit conversion. Int
does, but Int32
doesn’t, hence this behaviour:
// fine
let aoInt: AnyObject = Int(1) as NSNumber
// also fine: implicit conversion
let aoImplicitInt: AnyObject = Int(1)
// not fine: error: 'Int32' is not convertible to 'NSNumber'
let aoInt32: AnyObject = Int32(1) as NSNumber
// but the implicit error is less, ahem, explicit
// error: type 'Int32' does not conform to protocol 'AnyObject'
let aoImplicitInt32: AnyObject = Int32(1)
It could be argued there should be more implicit conversions to grease the wheels, but then again, these implicit conversions are the source of much confusion already and the direction in the latest beta is to have fewer of them rather than more.
Type 'OSType' does not conform to protocol 'AnyObject' in Swift 2.0
This is the kCVPixelFormatType_32BGRA
definition in Swift 1.2:
var kCVPixelFormatType_32BGRA: Int { get } /* 32 bit BGRA */
This is its definition in Swift 2.0:
var kCVPixelFormatType_32BGRA: OSType { get } /* 32 bit BGRA */
Actually the OSType
is a UInt32
which can't implicit convert to a NSNumber
:
When you write
let ao: AnyObject = Int(1)
, it isn’t really putting an Int into an AnyObject. Instead, it’s implicitly converting your Int into an NSNumber, which is a class, and then putting that in.https://stackoverflow.com/a/28920350/907422
So try this:
videoDataOutput?.videoSettings = [kCVPixelBufferPixelFormatTypeKey: Int(kCVPixelFormatType_32BGRA)]
or
videoDataOutput?.videoSettings = [kCVPixelBufferPixelFormatTypeKey: NSNumber(unsignedInt: kCVPixelFormatType_32BGRA)
Is Float, Double, Int an AnyObject?
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)
When is a variable a AnyObject but not a NSObject
On platforms with Objective-C compatibility (which means all of Apple's platforms and no others), every class type is (secretly) a subclass of the SwiftObject
class, which provides NSObject
protocol conformance.
On other platforms, NSObject
is “just another class”, implemented in Swift, so only a class that explicitly has NSObject
as a superclass has instances that are NSObject
s.
Swift issue: About Type Casting for AnyObject
This is a little confusing because of the bridging that Swift does to Objective-C, where most things are classes and there are fewer value-types. To see what Swift does without the bridge, create a new playground and delete the import ...
statement at the top, then try casting an Int
to Any
(no problem) and AnyObject
:
let num = 23 // 23
let anyNum: Any = num // 23
let anyObjectNum: AnyObject = num
// error: type 'Int' does not conform to protocol 'AnyObject'
Now add import Foundation
at the top of your playground:
import Foundation
let num = 23 // 23
let anyNum: Any = num // 23
let anyObjectNum: AnyObject = num // 23
The error goes away - why? When it sees the attempt to cast an Int
to AnyObject
, Swift first bridges num
to be an instance of the Objective-C NSNumber
class, found in Foundation
, then casts it to the desired AnyObject
. The same thing is happening in your array.
You can more or less prove this is the case using the is
keyword - since NSNumber
bridges to all the numeric types in Swift, it returns true
in some funny cases:
anyNum is Int // true
anyNum is Double // false
anyObjectNum is Int // true
anyObjectNum is UInt // true
anyObjectNum is Double // true
anyObjectNum is Float // true
Argument type 'Int' does not conform to expected type 'NSCoding & NSCopying & NSObjectProtocol'
As ORKTextChoice
's initialiser has an abstract parameter type for value:
, Swift will fallback on interpreting the integer literals passed to it as Int
– which does not conform to NSCoding
, NSCopying
or NSObjectProtocol
. It's Objective-C counterpart, NSNumber
, however does.
Although, rather than casting to NSCoding & NSCopying & NSObjectProtocol
, which would result in a bridge to NSNumber
(albeit an indirect and unclear one), you can simply make this bridge directly:
let textChoices = [
ORKTextChoice(text: "Create a ResearchKit app", value: 0 as NSNumber),
ORKTextChoice(text: "Seek the Holy grail", value: 1 as NSNumber),
ORKTextChoice(text: "Find a shrubbery", value: 2 as NSNumber)
]
Your original code would have worked prior to Swift 3, as Swift types were able to be implicitly bridged to their Objective-C counterparts. However, as per SE-0072: Fully eliminate implicit bridging conversions from Swift, this is no longer the case. You need to make the bridge explicit with as
.
((Any) throws - Bool) throws - Optional Any ') cannot conform to 'BinaryInteger'
With help from @Rob in the comments, I was able to unwrap the message properly and remove unnecessary guard let statements to run my delegate function. Below is the updated piece of code.
- Changed
message.values.first?
asInt
as the rate was in an integer format. - Removed guard let statements in converting the
heartRate
into the double format, as it was non-optional.
extension WatchKitConnection: WCSessionDelegate {
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("didReceiveMessage from watch")
print(message)
// delegate?.updateLatestHeartRate(Double(message.values.first))
guard let heartRate = message.values.first as? Int else {
return
}
let heartRateDouble = Double(heartRate)
print("printing heartRate double from message\(heartRateDouble)")
delegate?.updateLatestHeartRate(heartRateDouble)
print("updateLatestHeartRate")
}
}
Related Topics
Why Does Swiftui Uihostingcontroller Have Extra Spacing
Instance Member Cannot Be Used on Type Class
How to Demonstrate a Zombie Object in Swift
Gcd with Static Functions of a Struct
Compare Three Values for Equality
How to Use a Variadic Closure in Swift
Uibarbuttonitem Doesn't Work When Created as a Property, But Does When Created in a Function
When Calling a Function in Swift, What Does Each Part Mean
Swift Protocol Implements Equatable
How to Refactor Swift in Xcode
Swiftui: Is There Exist Modifier to Highlight Substring of Text() View
Why Does Int(Float(Int.Max)) Give Me an Error
How to Mimic 'Uitableviewcontroller' Showing of the Large Titles in 'Navigationbar' on iOS 11
Swift Mutable Set: Duplicate Element Found
Binding in a Foreach in Swiftui