I have this line of Swift which use to compile without issue
let layoutSettings = try LayoutSettings.deserialize(params: layoutSettingsParams as Dictionary<String, AnyObject?>)
As of Xcode 8.3.2 I get the error
Swift Compiler Error
Command failed due to signal:
Segmentation fault: 11
If I remove the ? at the end of the line of code it compiles without issue.
How do I use the nullable type without crashing the compiler?
AnyObject and AnyObject? are not the same thing. AnyObject? is actually syntactic sugar for Optional< AnyObject >. The fact that it compiles with Dictionary< String, AnyObject > but not Dictionary< String, AnyObject? > means that Dictionary< String, AnyObject > or [String: AnyObject] is the correct type, or convertible to the correct type. In general the swift compiler occasionally has problems with type inference which either causes it to crash or to compile very slowly. I notice this most frequently with dictionaries and with returns from .map(). In both cases if you explicitly state the type (even if according to the rules of the language it should be inferred) it usually helps the compiler go faster and/or not crash. In these instances I think it also helps the reader, because if the compiler can't infer the type easily its often the case that a human can't do it without some thinking as well.
Related
I came across a heterogeneous dictionary definition like this on a tutorial online:
var mixedMap4 = [AnyHashable(0): "Zero" as Any,
AnyHashable(1): 1.0 as Any,
AnyHashable("pi"): 3.14 as Any]
I was wondering why the author chose to write
AnyHashable(0) instead of 0 as AnyHashable. When I tried this on Swift playground, it also worked. However when I turned "Zero" as Any into Any(0) it gives the following
error: error: The Dictionary.xcplaygroundpage:41:34: error: protocol
type 'Any' cannot be instantiated var mixedMap4 = [AnyHashable(0):
Any("Zero") ,
Thank you for the answer
The clue is in the error message.
AnyHashable is a struct that type-erases the underlying hashable type, and so can be directly instantiated as an object
Any is a protocol and therefore can't be directly instantiated, although all other types can be complied with it, thus a String such as "Zero" can be cast as Any but Any(String) is meaningless.
To me it all just feels like a bucket load of trouble waiting to happen!
Using ReactiveSwift, I've used the following code:
let alertMessageProperty = MutableProperty<String?>(nil)
...
.on(failed: { self.alertMessageProperty.value = $0.localizedDescription })
...
which means:
self.alertMessageProperty.value is of type String?
in .on(failed:), failed is of type ((Error) -> Void)? so $0 is of type Error and $0.localizedDescription is of type String.
in a simple Playground, assigning a String to a String? variable works fine, as expected.
It does not compile and I get this error:
'String' is not convertible to 'String?'
I think String is SO convertible to String?. What's going on here?
Versions: Swift 3.2 - ReactiveSwift 2.0.1
There is a lot missing in your example so I can not reproduce the exact problem.
Your code that you left out in ... is probably a lot more complicated, maybe a chain of multiple reactive operations, and my guess is that the actual error is something completely different somewhere in that code. The error message you're getting is just misleading due to some bug i the Swift compiler.
You can try to break up your code into smaller pieces and see if the compiler now fails at a different place and try to narrow it down that way.
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?
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.)
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
SE-0072: Fully eliminate implicit bridging conversions from Swift
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:
AnyObject not working in Xcode8 beta6?
mapView.rac_valuesForKeyPath("userTrackingMode", observer: self).subscribeNextAs {
// block handling
I get an error 'String' is not convertible to 'String!'. Any suggestions what this may mean?
I used to think, that String! is same as String, so it is unwrapped String?...
Xcode 7.3.1
Swift 2.2
ReactiveCocoa 4.1.0
I think the compiler is reporting a wrong error.
You can simplify the expression using
let key: String! = "userTrackingMode"
and then use key instead of the literal.
That will simplify the expression and will help the compiler to print the real error.
Type inferring is complicated and when the compiler doesn't find a valid type combination, it can show you a wrong error.
Swift changed a lot with Xcode 6.3. I had to replace dozens of places in each of my app as -> as!. Why, what are the rules now?
Prior to Swift 1.2, the as operator could be used to carry out two different kinds of conversion, depending on the type of expression being converted and the type it was being converted to:
Guaranteed conversion of a value of one type to another, whose success can be verified by the Swift compiler. For example, upcasting (i.e., converting from a class to one of its superclasses) or specifying the type of a literal expression, (e.g., 1 as Float).
Forced conversion of one value to another, whose safety cannot be guaranteed by the Swift compiler and which may cause a runtime trap. For example downcasting, converting from a class to one of its subclasses.
Swift 1.2 separates the notions of guaranteed conversion and forced conversion into two distinct operators. Guaranteed conversion is still performed with the as operator, but forced conversion now uses the as! operator. The ! is meant to indicate that the conversion may fail. This way, you know at a glance which conversions may cause the program to crash.
Source: https://developer.apple.com/swift/blog/?id=23
The practical difference is this:
var optionalString = dict.objectForKey("SomeKey") as? String
optionalString will be a variable of type String?. If the underlying type is something other than a String this will harmlessly just assign nil to the optional.
var optionalString = dict.objectForKey("SomeKey") as! String?
This says, I know this thing is a String?. This too will result in optionalString being of type String?, but it will crash if the underlying type is something else.
The first style is then used with if let to safely unwrap the optional:
if let string = dict.objectForKey("SomeKey") as? String {
// If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
// identified the type as String, and the value is now unwrapped and ready to use. In
// this case "string" has the type "String".
println(string)
}
According to the release notes:
The notions of guaranteed conversion and “forced failable” conversion
are now separated into two operators. Forced failable conversion now
uses the as! operator. The ! makes it clear to readers of code that
the cast may fail and produce a runtime error. The “as” operator
remains for upcasts (e.g. “someDerivedValue as Base”) and type
annotations (“0 as Int8”) which are guaranteed to never fail.