I have a string var oneString: String! and later on in a method, when I want to concatenate a string to oneString I have to do this:
oneString! += anyString
If I don't add ! I get an error 'String!' is not identical to 'CGFloat'
If I initialize my string with var oneString = "" I don't have this problem. Why? Why do I need to unwrap oneString while I explicitly said it would not be nil when I declared it?
Why do I need to unwrap oneString while I explicitly said it would not be nil when I declared it?
You’ve misunderstood what var oneString: String! means. It does not mean oneString will not be nil. If you declare a type as var oneString: String, then you are declaring a type that cannot be nil.
The type String! is an “implicitly-unwrapped optional”. That is, it’s an optional, much like String?, but one that pretends to be a non-optional sometimes. Mostly for the purposes of reading it – you don’t have to explicitly unwrap it to get the value out. The downside being, if it is ever nil (and it totally can be), your code will trap and abort.
But this pretending-to-not-be-optional only goes so far. You cannot pass a String! to a function as inout when that argument is not an optional. Hence your problem.
Anton’s answer is completely correct in why it won’t work, and his suggested operator overload will make your code compile. But it isn’t the right solution – you should instead avoid using implicitly-unwrapped optionals as they are spring-loaded deathtraps and only to be used in specific circumstances (the most common being with Cocoa UI controls). 999 times out of 1,000 you would be better off with a regular optional or non-optional
Reason is that Foo, Foo? and Foo! are different types in Swift.
There are certain operators pre-defined for you "out-of-the-box" which allow great deal of transparency between Foo and Foo!, but still these types are not the same.
When it comes to strings, operator
func += (inout left: String!, right: String)
... is simply not defined.
If you declare it like:
func += (inout left: String!, right: String) {
left = left + right
}
... then your code should compile the way you like it, that is:
oneString! += anyString
Related
I have a custom operator ?= that is used to simply assign optional values to non-optionals.
var a: String
let b: String?
a = b ?? a
// is simplified to
a ?= b
This works well, but I don't want people to be able to use this operator when b is not optional. It's not going to cause any problems but it looks bad and I don't like it.
My function signature is currently:
func ?=<T> (originalValue: inout T, optionalNewValue: T?)
How could I make this cause a compiler warning or error when optionalNewValue is not actually an optional?
As other have said in the comments, the ?= operator might not be the best solution to your problems. Now if you're keen to that operator, or adds value to your code base, then there is a workaround that would allow you to reject non optional values on the right hand side of the operator.
You can use an OptionalCompatible protocol for the right hand side of the operator:
protocol OptionalCompatible {
associatedtype Wrapped
var optionalValue: Wrapped? { get }
}
extension Optional: OptionalCompatible {
var optionalValue: Wrapped? { return self }
}
func ?=<T, U> (originalValue: inout T, optionalNewValue: U) where U: OptionalCompatible, U.Wrapped == T {
originalValue = optionalNewValue.optionalValue ?? originalValue
}
Using a custom protocol will disable the auto-promotion of non-optionals to optionals, allowing only optionals to be used.
There is a caveat, though: in theory the conformance OptionalCompatible can be added to any type.
How could I make this cause a compiler warning or error when optionalNewValue is not actually an optional?
You cannot. It is a built-in fact of Swift that a nonOptional of type T can always be assigned or passed where an Optional<T> is expected.
I have the following variable:
var npill : String!
It's an Int value, but I can't set it as Int because of:
npillIn: fieldNumeroPillole.text!,
How can I convert this var to a Int var? I have tried the following:
var number1: Int = (npill! as NSString).intValue
By the above code I receive the following error:
cannot use instance member 'npill' within property initializer, property initializers run before "self" is aviable
If I then set:
var number1: Int = (self.npill! as NSString).intValue
The error it outputs is as follows:
Value of type '(NSObject) -> () -> Farmaco' has no member 'npill'
If anyone knows how I should be converting it properly, please let me know.
Update
Thank you to #Hamish for pointing out what the OP was asking
So the problem seems to be this
import Foundation
class Foo {
var npill : String!
var number1: Int = (npill! as NSString).intValue
}
error: cannot use instance member 'npill' within property initializer; property initializers run before 'self' is available
var number1: Int = (npill! as NSString).intValue
^
What's going on here?
You are using a property to populate another property, and this is not allowed.
Solution
However you can easily fix the problem postponing the initialisation of number1. Infact if you make number1 lazy, it will be populated only when used.
class Foo {
var npill : String!
lazy var number1: Int = { return Int(self.npill!)! }()
}
Warning: Of course this code will crash if npill is still nil when number1 is used.
Old version
You can simply write
let npill: String! = "34"
if let npill = npill, let num = Int(npill) {
print(num) // <-- here you have your Int
}
(As #Hamish pointed out in a comment below, I misunderstood what the OP was really asking about. I'll leave my answer, however, as some curiosa and insights regarding ! type annotation, which may be relevant for future readers of this question)
For any type of String optionals, their values needs to be unwrapped prior to using the failable init?(_ text: String) initializer or Int.
In your example, the variable npill is an optional, as you've annotated its type with the ! specifier (which should be used with care). Quoting from the implemented evolution proposal SE-0054 [emphasis mine]
Appending ! to the type of a Swift declaration will give it optional
type and annotate the declaration with an attribute stating that it
may be implicitly unwrapped when used.
Hence, it's entirely legal to use npill directly with the init?(_ text: String) initializer of Int, as it will be unwrapped (without any safety check for nil content!) on-the-fly upon use.
// UNSAFE example!
var npill: String! = "42"
if let npillInt = Int(npill) {
/* ^^^^^^^^ ^^^^^- since 'npill' has a type annotated with
| '!', it will be unsafely unwrapped at
| this point
\
the optional binding here safely unwraps the return from
the failable Int initializer, but has nothing to do with
the unwrapping of 'npill' */
print(npillInt) // 42
}
// why unsafe? consider
npill = nil
if let npillInt = Int(npill) { // runtime exception!
// ...
}
Generally you should avoid using the ! annotation, however, unless you are entirely certain that the content of the resulting optional variable will never ever be nil.
Leaving aside the cons of even using the ! annotation: you may implement a safe version of the unsafe example above, by overriding the unsafe implicit unwrapping with safe explicit unwrapping techniques. For a given optional variable declared using the ! annotation, we may still apply safe means to unwrap it, e.g. optional binding or using the nil coalescing operator. #appzYourLife has already showed one perfectly valid and safe way to handle the unwrapping and attempted type conversion of npill using optional binding, so I'll simply include another example using the nil coalescing operator instead:
// "safe" example (STILL: why use the `!` annotation?)
var npill: String! = "42"
if let npillInt = Int(npill ?? "x") {
/* ^^^^^ if 'npill' is 'nil', the Int initializer will
be given the value "x", which itself will lead
it to fail, which is safe here as we intend to
bind the result of the initialization to 'npillInt' */
print(npillInt) // 42
}
npill = nil
if let npillInt = Int(npill ?? "x") {
// ... doesnt enter
}
The consensus of the examples above is that if we're even slightly uncertain whether npill can ever be nil or not, we need to treat it as if it was just an optional not type annotated with ! (i.e. String?); overriding the default unsafe unwrapping with safe means when working with the variable. In such a case, why would we even want to use the ! typ annotation at all, as it only brings fragility/danger to our application?
For my default parameters should I write
func myFunc(optionA: Int? = 0)
or
func myFunc(optionA: Int! = 0)
Which is more advisable and/or more importantly which is used more in production? What happens if I use Int! instead of Int?? Can I still pass either an Int, nil, or no parameter whilst not having to unwrap the parameter when accessed? Is doing so considered lazy programming and may present potential memory insecurities?
0 Isn't nil. In either case, you're making optionA have a value of 0, not nil.
The correct answer here depends on what kind of interface you want your function to have.
If you want callers to be able to pass an Int, nil or no parameter at all, then optionA should be an Int? type.
If you want callers to be able to pass an Int, no parameter at all, but NOT nil, then optionA should be an Int type.
if I do not supply a value or nil is supplied for the parameter its default value becomes 0.
This is a very strange requirement, and likely indicates a design error. However, it is trivial to implement.
func myFunc(optionA: Int? = nil) {
let a = optionA ?? 0
// ...continue to use a...
}
That said, this is almost certainly a broken API. It would be helpful to give a more concrete example of what problem you're solving that isn't solved by just func myFunc(a: Int) (which should be your strong preference unless you have a good reason for something else).
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...
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.
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
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
You are better to read swift documentation than any answer here. Properly undestandig swift's type system is very important, so do it first.
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.
We can't pass nil to inout param, is there a short and concise way to create an empty object literal on the fly and pass to it for the sake of it,
let's say sometimes when we have nothing to pass, wanna pass an empty array, a empty dictionary or class literal object, how would that work in the following case?
func test(inout param: Any) {
println(param)
}
//call
test(&[]())
Edit - inout with nil works
func test(inout param: Any?) {
println(param)
}
var p: Any? = nil
test(&p)
There are many things that won’t work here.
You need to give more than [] to declare an array. You need to give some indication of it’s element type. Either in the brackets e.g. [Int]() or from other context i.e. if test took an [Int] arg not an Any. Otherwise the compiler has no idea what type you mean by [].
You cannot pass literals (for example, nil) into an inout parameter. In fact you can’t pass any kind of immutable value, including temporary objects like [Int](), or variables declared with let. The only things you can pass to an inout parameter are “lvalues” – that is, things that can be on the left-hand side of an assignment. That means even if you changed this to `test(&Int) it won’t work.
Finally, even if you declare var a = [Int]() and then try to call test(&a) you’ll get an error, because the implicit copy of a from an array to an Any will result in another immutable temporary, which also can’t be passed as an inout.