When you cannot initialize an object due to invalid input, you can use a "failable init".
init?(s:String) {
if s.isEmpty {
return nil
}
// now set up instance vars
}
Then you can do this:
var x:Foo = ""
if x == nil { blah blah
But is you conform to the StringLiteralConvertible protocol and implement those 3 inits, you cannot have a failable init.
My question: how do you check for invalid input when you use StringLiteralConvertible?
Here's a gist of those 3 inits if you've never used StringLiteralConvertible.
If you use StringLiteralConvertible, you can assign a literal string to a variable:
var x: Foo = "test"
Any string you provide is good, as long as it is a valid string. If it's not a string:
var x: Foo = 1
or it's a malformed string:
var x: Foo = test"
it won't pass compilation, so a failable initializer would be useless.
But if by valid you mean depending on the content of the string, then you are right. A literal must be syntactically correct and of the expected type in order to be considered valid in the context where it is used, and I don't think there's a way to override that.
Related
Consider the following example where foo is explicitly defined as optional:
let foo: Int? = 10
let bar = foo.map { $0 * 2 }
This compiles and works as expected. Now consider a subsequent example using optional chaining:
let foo: [Int]? = [0, 1, 2]
let bar = foo?.count.map { $0 * 2 }
This fails to compile with the following message:
error: value of type 'Int' has no member 'map'
Why does the compiler see foo?.count as an Int and not an Int?? This defies the Swift Language Book:
To reflect the fact that optional chaining can be called on a nil value, the result of an optional chaining call is always an optional value, even if the property, method, or subscript you are querying returns a non-optional value.
This is an operator precedence issue. You need to add parentheses to change the order:
let bar = (foo?.count).map { $0 * 2 }
Consider the case of a Collection like String rather than an Int:
let foo: String? = "ABC"
let bar = foo?.uppercased().map(\.isLowercase) // [false, false, false]
In this case, it's sensible that the .map applies to the String rather than to the Optional. If the precedence were the other way, then you'd need a ?. at every step (or a lot of parentheses :D)
The type of count unwraps to Int if available. Int does not implement the function map. Optional chaining simply fails when it finds nil. The compiler is trying to tell you if it does not fail the operation map cannot happen. The result is bar which is indeed an optional, because if the operation fails( because something unwraps to nil) the entire chain must return nil.
I sometimes see a function which accepts an object as a parameter which has '!' as suffix.
I know it means the parameter is an implicitly unwrapped optional.
But I don't know what is the difference between the two case. i.e. defining a parameter with '!' and without '!'.
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
func tame(cat: Cat) {
print("Hi, \(cat.name)")
}
func tame2(cat: Cat!) {
print("Hi, \(cat.name)")
}
let cat = Cat(name: "Jack")
tame(cat: cat)
tame2(cat: cat)
As a result there is no difference between the function tame and tame2.
If the parameter has a possibility of being nil, I should define the parameter as an optional right?
func tame3(cat: Cat?) {
if let cat = cat {
print("Hi, \(cat.name)")
}
}
In which case should I define a function's parameter as implicitly unwrapped optional like tame2?
In which case should I define a function's parameter as implicitly unwrapped optional like tame2?
Short answer: Never.
If the parameter has a possibility of being nil, I should define the parameter as an optional right?
Right.
Implicit unwrapped optionals are useful for properties which could not be initialized in an init method but are supposed to have always a value and for compatibility to bridge Objective-C API to Swift, nothing else.
Can anyone tell me what is T.Type when I use JSONDecoder().decode()?
I think it is type to decode data what I encoded.
So many example use above method like this:
JSONEncoder().decode([People].self, from: data)
If I check that method's definition I can see T.Type.
I know the generics but what is T.Type?
What's the difference between just T and T.Type?
when we declare some variables, we allocated their types like this
var someValue: Int
, not var someValue: Int.self
What is the T.Type exactly and Type.self?
T.Type is used in parameters and constraints to mean "the type of the thing itself, not an instance of the thing".
For example:
class Example {
static var staticVar: String { return "Foo" }
var instanceVar: String { return "Bar" }
}
func printVar(from example: Example) {
print(example.instanceVar) // "Bar"
print(example.staticVar) // Doesn't compile, _Instances_ of Example don't have the property "staticVar"
}
func printVar(from example: Example.Type) {
print(example.instanceVar) // Doesn't compile, the _Type_ Example doesn't have the property "instanceVar"
print(example.staticVar) // prints "Foo"
}
You get a reference to a Type's .Type (the Type object itself) at runtime by calling TheType.self. The syntax TheType.Type is used in type declarations and type signatures only to indicate to the compiler the instance vs. type distinction. You can't actually get a reference to, for example, Int's type at runtime or in your function implementations by calling Int.Type. You would call Int.self
In the example code var someValue: Int, the specific notation identifier: Type (in this case, someValue: Int) means that someValue will be an instance of Int. If you wanted someValue to be a reference to the actual type Int, you would write var someValue: Int.Type = Int.self Remember that the .Type notation is only used when declaring types and type signatures to the compiler, and the .self property is used in actual code to retrieve a reference to the type object itself at execution time.
The reason why JSONDecoder().decode requires a parameter of T.Type (where T conforms to Decodable) is because any type conforming to Decodable has an initializer init(from decoder: Decoder). The decode method will need to call this init method on a type that conforms to Decodable, not on an instance of a type that conforms to Decodable. For example:
var someString: String = ""
someString.init(describing: 5) // Not possible, doesn't compile. Can't call an initializer on an _instance_ of String
var someStringType: String.Type = String.self
someStringType.init(describing: 5) // Iniitializes a String instance "5"
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?
Why is this variable i not substituted for Bool but optional binding is?
Swift claims this in the guide..
Swift’s type safety prevents non-Boolean values from being substituted
for Bool. The following example reports a compile-time error:
let i = 1
if i {
// this example will not compile, and will report an error
}
Yet this compiles
var foo: String?
if let bar = foo {
print("Non nil")
}
An optional is essentially an enum with two possible values either a designated value, in your example a string, or nil. An if let binding is not being substituted for a Bool in your example. Instead, it is checking if the variable foo is nil and if it is not nil then it sets bar equal to foo and performs the code inside the block. An if let binding is thus essentially a shortcut for
var foo: String?
if foo != nil {
//Set bar equal to the unwrapped value of foo
let bar = foo!
//Do whatever
}
The efficiency of this is best shown in a case where you would want to check if something is nil and check some characteristic of the value if it is not nil. Say you wanted to also check that foo has more than 5 characters and if it does then do something. Since foo could be nil you would have to check for it specifically to ensure it is not. An if let binding will let you do this:
if let bar = foo where bar.characters.count > 5 {
print(bar)
}
rather than this:
if foo != nil && foo?.characters.count > 5 {
let bar = foo!
print(bar)
}
It basically makes very readable, and thus more maintainable code. Also, it prevents you from having to unwrap the optional manually (the ! operator at the end of foo).