How can I expand the scope of a variable in Swift - swift

I'm writing a macOS Command Line Tool in Xcode. The variable "num1" keeps returning nil. I declared it in the beginning so it should be a global variable. How can I address this?
var num1: Int!
if userChoice == "add" {
print("Enter first number")
if let num1 = readLine() {
}
}
print ("\(num1)")

As mentioned, you have two num1 variables. The 2nd one is scoped to just the block of the 2nd if statement.
You also have an issue that readLine returns an optional String, not an Int.
You probably want code more like this:
var num1: Int?
if userChoice == "add" {
print("Enter first number")
if let line = readLine() {
num1 = Int(line)
}
}
print ("\(num1)")
Of course you may now need to deal with num1 being nil.
One option is to properly unwrap the value:
if let num = num1 {
print("\(num)")
} else {
print("num1 was nil")
}

Call the second num1 something else.
Put something in the if.
var num1: Int!
if userChoice == "add" {
print("Enter first number")
if let num2 = readLine() {
// This if-let runs if num2 is not nill.
print ("\(num2)")
num1 = Int(num2)
}
}
print ("\(num1)")

Related

Why is it everytime I try to get a user to input an integer in Swift, Xcode makes me put a "!" after the variable

I'm working on a code where I need to get 2 user inputted integers and output the smaller one. I am new to swift, and every time I try to get a user to input an integer, I get this error "Value of optional type 'String?' must be unwrapped to a value of type 'String'". I don't understand why I always need to put a "!" after readLine(), but thats what it makes me do.
print("Enter the first integer")
let int1 = Int(readLine()!)
print("Enter the second integer")
let int2 = Int(readLine()!)
let small_int = min(int1!, int2!)
print("The smaller integer is", small_int)
As you can read in docs:
The string of characters read from standard input. If EOF has already been reached when readLine() is called, the result is nil.
... so, simplified, readLine(strippingNewline:) always doesn't have to return value and it also can return nil (no value), so return type is String? which is optional type which says that your constant is String or nil
If you need to get non-optional value you can either force-unwrap optional value or check if value exists and if does, assign some non-optional constant/variable using optional binding. The same you can do for Int initializer which can also return nil since not every String is necessarily a integer
print("Enter the first integer")
let input1 = readLine()
print("Enter the second integer")
let input2 = readLine()
if let string1 = input1, let int1 = Int(string1), let string2 = input2, let int2 = Int(string2) {
let small_int = min(int1, int2)
print("The smaller integer is", small_int)
} else {
print("Invalid input")
}
alternatively you can use default value so if value is nil your constant will be assigned with given default value
print("Enter the first integer")
let int1 = Int(readLine() ?? "") ?? 0
print("Enter the second integer")
let int2 = Int(readLine() ?? "") ?? 0
let small_int = min(int1, int2)
print("The smaller integer is", small_int)
I recommend writing your code in a defensive way. That includes dealing with unexpected results.
Both, readline() and Int() can return nil. As the other answer already explained, readline returns nil if you have reached EOF.
So my recommendation is to use the ?? operator to provide alternative values for the failure cases. For example: let line = readline() ?? “”.
Alternatively, especially inside methods, you may want to bail out early with guard and have the actual work at the end of the method with validated and non-nil inputs.
With that in mind, your example can be rewritten like this:
let line = readline() ?? “”
let int1 = Int(line) ?? 0
//...
Or as a method with guard:
func smallerInteger() -> Int? {
print("Enter the first integer")
guard let line1 = readline() else {
return nil
}
guard let int1 = Int(line1) else {
return nil
}
print("Enter the second integer")
guard let line2 = readline() else {
return nil
}
guard let int2 = Int(line2) else {
return nil
}
return min(int1, int2)
}
Of course this can be improved by returning both an Int? and an Error? like (Int?, Error?) or with the latest Swift, a Result.

how to use the return value in a completion handler?

I'm trying to understand better the closures in swift and I've tried to create a silly function with a completion handler that returns a value but this value goes nowhere...
here's what I did..
func productToString(num: Int,num2: Int,completion: (Int)->String){
let result = num * num2
completion(result)
}
let numberToString = productToString(num: 3, num2: 6) { (res) -> String in
return "string:\(res)"
}
print(numberToString) // print statement prints ()
when I tried to save this return value in a variable it returned just a pair of curvy brackets.
how should I use this return value?
You're returning the value into the productToString function but not doing anything else with it.
func productToString(num: Int,num2: Int,completion: (Int)->String){
let result = num * num2
completion(result) // <--- Your return value ends up here
}
If you want to print the result you have to return it again out of the productToString function.
func productToString(num: Int,num2: Int,completion: (Int)->String) -> String {
let result = num * num2
return completion(result)
}
Sidenote: The empty brackets that are printed are an empty tuple which is equivalent to Void in Swift.

know the Datatype in Swift

i am new to swift i just started with the basics. In one of the Blog i saw a simple task which goes like this read a line from the stdin and check whether it is a integer,float,String.
I tried with the following code
let input = readLine()
var result = test(input)
print (result)
func test (obj:Any) -> String {
if obj is Int { return "This input is of type Intger." }
else if obj is String { return "This input is of type String." }
else { return "This input is something else. " }
}
when the input of 3245 is given it stores in the string format. and returns output as string.
how to overcome it..?
The readLine function returns a value of type String?. So your input variable can only be a String. It will never be Int or anything else.
If you want to see if the entered value is a valid number, you can try to convert the string to an Int.
if let input = readLine() {
if let num = Int(input) {
// the user entered a valid integer
} else {
// the user entered something other than an integer
}
}
As others have pointed out, readline() always returns a String?. It's up to you to parse that into whatever format you use it.
This is how I would do this:
let line = readLine()
switch line {
case let s? where Int(s) != nil:
print("This input is of type Intger.")
case let s? where Float(s) != nil:
print("This input is of type Float.")
case let s? where s.hasPrefix("\"") && s.hasSuffix("\""):
print("This input is of type String.")
default: print("This input is something else. ")
}
It exploits the ability of Int and Float's initializers to test the validity of a String, which almost entirely defeats the purpose of this exercise. But hey, it works, right? 😄
You can find of the type of object as
if let intt = obj as? Int {
// obj is a String. Do something with intt
}
else if let str = obj as? String {
// obj is a String. Do something with str
}
else {
//obj is something else
}

In swift when print optional value when non optional

The code result is:
num1 is Optional(5)
num2 is Optional(5)
num2 is 5
I want to know why in if{} num2 is an optional value, but print "num2 is 5"
var optionalNum : Int? = 5
let num1 = optionalNum
print("num1 is \(num1)")
if let num2 = optionalNum {
print("num2 is \(optionalNum)")
print("num2 is \(num2)")
} else {
print("optionalNum does not hold a value")
}
When you write
if let num2 = optionalNum { ...
You are performing an optional binding.
In plain english it means
If optionalNum contains a value, then
create a new constant num2 containing that value
AND execute the block inside the { ... }
AND make available the new num2 constant inside the block
So inside the block num2 is NOT an optional. So when you print it you get the plain value. This is the reason why it prints
num2 is 5

Swift Optionals - Variable binding in a condition requires an initializer

I am new to Swift and trying to figure out the Optional concept. I have a small piece of code in Playground which is giving me "Variable binding in a condition requires an initializer" error. Can someone please explain why and how do I fix it?
I only want to print "Yes" or "No" depending on if "score1" has a value or not. Here is the code:
import Cocoa
class Person {
var score1: Int? = 9
func sum() {
if let score1 {
print("yes")
} else {
print("No")
}
}//end sum
}// end person
var objperson = person()
objperson.sum()
The if let statement takes an optional variable. If it is nil, the else block or nothing is executed. If it has a value, the value is assigned to a different variable as a non-optional type.
So, the following code would output the value of score1 or "No" if there is none:
if let score1Unwrapped = score1
{
print(score1Unwrapped)
}
else
{
print("No")
}
A shorter version of the same would be:
print(score1 ?? "No")
In your case, where you don't actually use the value stored in the optional variable, you can also check if the value is nil:
if score1 != nil {
...
}
Writing
if let score1 {
doesn't make sense. If you want to see if score has a value, use
if score1 != nil {
or
if let score = score1 {
The last case binds a new non-optional constant score to score1. This lets you use score inside the if statement.
The code in your question is similar to something I saw in the swift book and documentation and they are correct.
Your playground is just using an old version of swift which currently doesn't support this syntax. Using a beta version of XCode should fix
https://www.reddit.com/r/swift/comments/vy7jhx/unwrapping_optionals/
the problem is that if let assumes you want to create a constant score1 with some value. If you just want to check if it contains a value, as in not nil you should just do it like below:
if score1! != nil {
// println("yes")
So your full code would look like this:
class Person {
var score1: Int? = 9
func sum() {
if score1 != nil {
println("yes")
}
else {
println("no")
}
}
}
var objperson = Person()
objperson.sum()
You can unwrap it using this:
import Cocoa
class Person {
var score1: Int? = 9
func sum() {
print("\(score1 != nil ? "YES" : "NO")")
}
}
And then call it like:
var objperson = Person()
objperson.sum()