32

A fairly simple piece of code

var dict: [String: AnyObject] = [:]
dict["key"] = "value"

generates the following compile-time error

Cannot assign value of type 'String' to type 'AnyObject?'

Simple type checks tell me that String is AnyObject

"value" is AnyObject // returns true

I could change AnyObject to Any and everything would work

var dict: [String: Any] = [:]
dict["key"] = "value"

but I want to understand why do I get the error? Is String no longer AnyObject? Or is this a bug?

Wain
  • 118,658
  • 15
  • 128
  • 151
xinatanil
  • 1,085
  • 2
  • 13
  • 23

2 Answers2

41

In b6, String no longer magically bridges to NSString. String is not a class; it's a struct. You need to do the bridging by hand:

dict["key"] = "value" as AnyObject

The fact that is still seems to be bridging is likely a bug and should be reported.

It goes without saying that [String: AnyObject] and [String: Any] should be used as little as possible in your code.

(Make sure to follow the link Hamish provides in the comments below.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks for the answer, everything was just as I suspected. That false-positive type check is the only reason why I asked this question. – xinatanil Aug 19 '16 at 18:21
  • 2
    [According to Joe Groff](https://bugs.swift.org/browse/SR-2420?focusedCommentId=17394&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-17394), `is/as AnyObject` should always succeed, as everything bridges to `AnyObject` now. I believe this is due to that because `id` is now bridged to Swift as `Any`, `Any` can now be bridged to `AnyObject` (weird, I know). – Hamish Aug 19 '16 at 18:22
  • 1
    And yes, looks like swift is experiencing AnyObject type check troubles http://stackoverflow.com/questions/39033194/anyobject-not-working-in-xcode8-beta6 – xinatanil Aug 19 '16 at 18:25
  • Can I ask you why [String:AnyObject] shouldn't be used in code? – romero-ios Oct 05 '16 at 17:10
  • @danielx0328 it is weakly typed, which creates a lot of opportunities for bugs and a lot of headaches for working in Swift (which is strongly typed). Most usage of Any or AnyObject bumps quickly into headaches in Swift. – Rob Napier Oct 05 '16 at 17:34
  • @RobNapier thanks for the response. So I use this simply since it is what Apple said was the preferred usage since it is shorthand. What is the way I should use it to avoid bugs then? – romero-ios Oct 05 '16 at 17:44
  • @danielx0328 I'm not aware of anywhere that Apple says AnyObject or Any are preferred usage. There are bridges into ObjC where it is currently unavoidable for temporary usage, but you should immediately convert these dictionaries to specific structs or classes. – Rob Napier Oct 05 '16 at 17:51
  • @RobNapier "The type of a Swift dictionary is written in full as Dictionary, where Key is the type of value that can be used as a dictionary key, and Value is the type of value that the dictionary stores for those keys. You can also write the type of a dictionary in shorthand form as [Key: Value]. Although the two forms are functionally identical, the shorthand form is preferred and is used throughout this guide when referring to the type of a dictionary." Straight from swift 3 documentation on their site. That's why I've used it that way :/ – romero-ios Oct 05 '16 at 17:54
  • @danielx0328 I didn't mean the syntax "[Key:Value]". That absolutely is the correct way to write dictionaries. I am discussing `AnyObject` and `Any`. You should avoid `Value` being of type `AnyObject`. If you have a known set of keys (i.e. for a string-keyed Dictionary, there are only certain legal strings), then you shouldn't use Dictionary at all in nearly all cases; you should use a struct. The fact that Cocoa uses Dictionaries in some cases is a historic artifact, not a sign of good Swift. – Rob Napier Oct 05 '16 at 17:59
  • 1
    @RobNapier okay now I understand! Okay thanks for that clarification! :) – romero-ios Oct 05 '16 at 18:02
  • [String:Any] is VERY useful when working with structured JSON/XML. Its only weak if you store unstructured data within it. – matthew Feb 11 '17 at 05:18
  • Only during parsing or serializing. You should as quickly as possible convert that `[String:Any]` to structs/classes for manipulation. It is weak even for JSON (in that it is capable of holding many values that are not valid values; which is fundamentally what it means to be "a weak type"). It is only necessary because of weakness in Foundation's JSON parser and serializer. It is completely possible to have a strongly typed JSON parser (such as the standard one in Go); Foundation just doesn't provide one, and it's difficult to build in Swift due to weakness of reflection. – Rob Napier Feb 11 '17 at 18:47
  • This change does not result in equivalent code. It changes the meaning of the code in Swift 2 from Swift 3. In Swift 2 (without the `as`), assigning a `nil` results in the key not being added. In Swift 3 (with the `as`), the the value gets assigned. – Dan Rosenstark Sep 18 '17 at 14:57
  • The only way to get equivalence, AFAIK, is by adding an optional bind. This is useful if you happen to be converting a huge codebase that you didn't author to Swift 3 ;) – Dan Rosenstark Sep 18 '17 at 15:07
4

I'll complement to @RobNapier's answer with some official sources.


The removal of the implicit bridging mechanisms was accepted in the following Swift evolution proposal, to be implemented for Swift 3

Previously, implicit conversions were available from some Swift native types to associated Objective-C types (Swift types conforming to private protocol _ObjectiveCBridgeable, e.g. natively Int, String, )

For this reason, we decided to make a compromise. We would require explicit bridging casts when converting from a bridged Objective-C type to its associated Swift value type (E.g., NSString -> String), but not the other way around.

... [From SE-0072]

With Swift 3, such implicit conversion mechanisms will no longer be available.

With the introduction of Objective-C generics last year, along with all of the awesome improvements to API importing happening for Swift 3, I think it’s time that we take another look at completing this work.

...

I propose that we fully eliminate implicit bridging conversions in Swift 3. This would mean that some users might have to introduce more explicit casts in their code, but we would remove another special case from Swift's type system and be able to further simplify the compiler.

...

Code that previously relied on implicit conversions between Swift value types and their associated bridged Objective-C type will now require a manual coercion via an as cast.

Finally, the release notes for Xcode 8 beta 6 (login required) states that this proposal has now been implemented for beta 6:

New in Xcode 8 beta 6 - Swift Compiler: Swift Language

...

  • Bridging conversions are no longer implicit. The conversion from a Swift value type to its corresponding object can be forced with as. For example: string as NSString. Any Swift value can also be converted to its boxed id representation with as AnyObject. (SE-0072)

W.r.t. new "boxed id" allowing explicit conversion for any Swift value to AnyObject, see e.g. the following thread:

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192