2

I understand the difference between String! type and String? type. But what about String type? How does it differs from String! and String? in swift? Does String! type identical to String type?

Say, I have a class like this:

class Person { private var _name: String! var name: String { return _name } init(name: String) { _name = name } } 

There is no compiler error, looks like String type is identical to String! type. But I am not sure...

5 Answers 5

4

String and String! are not identical. There is just happens to be enough sugar in the language to convert between them. Similarly there is sugar in the language to convert between String and String? (but not in reverse).

Start with the basics. There is String. Unless there is some strong reason, you should use String when you mean a string of characters. Everything else is "more stuff" and you shouldn't add it unless you need it.

There is Optional<String>. This is just an enum with two cases, one with a value, and one without a value:

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible { case None case Some(Wrapped) // ... } 

There is a postfix operator ! for Optional which will return Wrapped if it's available, and crash if it is not. So far, no magic. This is stuff you could build yourself.

There are are few pieces of magic around Optional. First, the type Wrapped? is magically converted to Optional<Wrapped> by the compiler (for any Wrapped). This is just syntactic sugar. The two notations are identical. Second, there is optional-chaining with the ?. "operator" (it's not really an operator; it's part of the language and you couldn't build it yourself). And then there's optional promotion. Any type Wrapped can be automatically and implicitly converted to Wrapped? if needed. There are are few other pieces of magic around Optional like if-let syntax, and there's nil which is a synonym for Optional.None (I believe they're actually identical). But Optional really is just a generic type, implemented as an enum. It's just a type the compiler knows special things about.

Then there is ImplicitlyUnwrappedOptional<Wrapped>. This is also just an enum (in Swift 2.2; this will change in Swift 3).

public enum ImplicitlyUnwrappedOptional<Wrapped> : _Reflectable, NilLiteralConvertible { case None case Some(Wrapped) // ... } 

This is not the same as Optional and it is not the same as Wrapped. It's a completely different type. But it also has some magic associated with it. First, the type Wrapped! is syntactic sugar for ImplicitlyUnwrappedOptional<Wrapped>. Again, it's just sugar. The two are the same (in Swift 2.2, not in Swift 3). Next, if IUO<Wrapped> is found in a place that Wrapped is expected, it will automatically be converted to Wrapped or crash if there is no value. If it is found in a place that Wrapped? is expected, it will automatically be converted to Wrapped?. Those are magical, and it's why sometimes String and String! seem to be the same type. That's just the compiler magically "making it work" for you by adding an invisible conversion step. It doesn't mean they're really the same type.

IUO is mostly useful in bridging to certain Objective-C patterns, especially involving Storyboards, and should be avoided outside of those situations. Even in those situations, IUO is just there for convenience. You could do all the same things using regular Optionals, you'd just have to check them with if-let more often. Using Optionals is much safer than using IUO. It's easy to think "I know for certain this value will always be set before it is used." And just this week I chased down a crasher due to being wrong about that. There's a difference between "it should be" and "it must be." But, being totally safe with Optionals in Storyboards could be very inconvenient and might mask some bugs (by doing nothing rather than crashing), so that's the most common place for IUO.

IUO properties used to be valuable for dealing with failable init methods. That's no longer a problem in Swift 2.2, so that use has gone away. The last "pure Swift" use that I run into is when you must pass self to the initializer of something you store as a property (you can't pass self at that point because all your properties have been initialized). That's an unfortunate use-case and very messy, and I hope we come up with a fix for it. Outside of these kinds of cases, you should avoid Implicitly Unwrapped Optionals.

Sign up to request clarification or add additional context in comments.

Comments

1

String and String! are same and different at the same time. If you declare let or var as String, you will have to set some value at init method. If you declare it as String! you can set value when you want but you must do it before reading this value.

So, if your _name will be nil, your app will crash.

If you declare it as String, compiler guarantees that _name != nil before the first reading. If you declare it as String!, you should guarantee it.

This things have same type because of this:

var test1: String = "test" var test2: String! = "test" if test1 is String { // True print("test1 is String") } if test2 is String { // True print("test2 is String") } 

This types are equal but this things are different

6 Comments

String and String! are not equal, per se, and they have some subtle differences. The latter is an ImplicitlyUnwrappedOptional wrapping a String, and even if it is "implicitly unwrapped", you may use e.g. a String! instance in a conditional binding block (if let foo = bar { } for bar being of type String!), whereas you may not for "pure" String instance. They do, however, in practice have many similarities, however e.g. safety not being one of them.
@Artyom, Based on your explaination, is it so that it is better to declare _name with type String instead of String!, so that compiler will always remind me to set a value in init & my app will have less chance to crash, otherwise if like what I do now, _name is of type String!, I could get a nil value & crash my app accidentally?
@Leem.fin You can't initialize your example class without name, but you can set nil to name after that and it can cause crash if your variable will be accessed
It is better to declare variables as String but you can't do that everywhere. For example, your @IBOutlet always will be ImplicitlyUnwrappedOptional (UIView! for example) because they values can't be set at initialization
The fact that is String passes does not mean that String! and String are the same type. It just means that the compiler is willing to implicitly convert between them. Compare test1.dynamicType and test2.dynamicType to see the difference. It's also worth noting that while you can use IUOs for @IBOutlet, you don't have to. You can also use regular Optionals. IUOs are a convenience (and not always a major one), not a requirement there.
|
1

Note the difference between these two functions:

func f() { let x: String? = nil guard let y: String = x else { print("failed") return } } func g() { let x: String? = nil guard let y: String! = x else { print("failed") return } print(y.dynamicType, y) } f() # failed g() # Optional<String> nil 

2 Comments

This is a very interesting corner case, but may confuse more than it enlightens (it might even be a compiler bug, though if it is, it'll be irrelevant in Swift 3). The output is suggesting that String! and String? are the same type, but that's not what's happening. I'm certain that the guard-let is promoting x to String?? which is why it succeeds. The promoted String?? has a value; that value is .Some(nil) (which is completely different than nil). Compare your example to this simpler one: let x: String? = nil; let z: String! = x; print(z.dynamicType).
@RobNapier I do believe this is a compiler bug limited to explicitly declaring the type binded to as ImplicitlyUnwrappedOptional<T> in if let and guard let clauses`, as I previously pondered over in this thread. Bug report has been filed here (SR-1123), but as you say, it'll should irrelevant (and already implicitly fixed :) in Swift 3.
0

You are better to read swift documentation than any answer here. Properly undestandig swift's type system is very important, so do it first.

Comments

-1

both String! and String? are optional types.

Using String? requires you to test whether the value is nil (.None) or a value (.Some). For example using guard statements or if-let clauses.

String! is an optional that is assumed to be non-nil and is unwrapped automatically. This allows you to write cleaner-looking code and avoid unwrapping wherever the value is used. BUT: if your value is nil a fatal error will occur.

4 Comments

This will be true in Swift 3, but is not true in 2.2. String! is ImplicitlyUnwrappedOptional<String> which is a different type than Optional<String>.
is ImplicitlyUnwrappedOptional a subtype of Optional?
No. You can't have a subtype of an enum.
I changed my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.