If statement within function doesn't work - ios

func endRound() {
timerLabel.isHidden = true
var label1Index = allEvents.index(of: label1.text!)
var label2Index = allEvents.index(of: label2.text!)
var label3Index = allEvents.index(of: label3.text!)
var label4Index = allEvents.index(of: label4.text!)
if (label4Index > label3Index > label2Index > label1Index) {
score = score + 1
let successImage = UIImage(named: "next_round_success")! as UIImage
nextRoundButtonOutlet.setImage(successImage, for: UIControlState.normal)
nextRoundButtonOutlet.isHidden = false
round = round + 1
} else {
let failImage = UIImage(named: "next_round_fail")! as UIImage
nextRoundButtonOutlet.setImage(failImage, for: UIControlState.normal)
nextRoundButtonOutlet.isHidden = false
round = round + 1
}
if (round == 6) {
}
}
For some reason, I recieve a:
"Value of type 'Array.Index?' not unwrapped; did you mean to use '!' or '??'"
error next to the first if statement.
When I add ! marks, as the compiler suggests, next to each variables within the if statement, I recieve a:
"Binary Operator '>' cannot be applied to operands of type 'Bool' and 'Array.Index' (aka int)"
and:
"Adjacent operators are in non-associative precedence group 'ComparisonPrecedence'"
Not sure what to do. The variables within the if statement represent the index number of the strings of the labels (just read the code and you would understand this).
Any help would be apperciated.

You have a few issues. The call to allEvents.index(of: label1.text!) returns an optional result since it is possible that text won't be found in the array. Adding the ! runs the risk of your app crashing if the text isn't found. One option would be to set a special value such as -1 in such a case.
var label1Index = allEvents.index(of: label1.text!) ?? -1
var label2Index = allEvents.index(of: label2.text!) ?? -1
var label3Index = allEvents.index(of: label3.text!) ?? -1
var label4Index = allEvents.index(of: label4.text!) ?? -1
Now for the if statement. You can't chain a bunch of > comparisons like that. You need:
if (label4Index > label3Index && label3Index > label2Index && label2Index > label1Index) {
}

As rmaddy says in his answer, you are getting a message about your indexes not being unwrapped because the string index(of:) function returns an Optional, which will be nil if the string isn't found.
As rmaddy also pointed out, your code to makes sure the strings are in the correct order is also invalid.
The code if a > b > c > d is not valid Swift. You have to rewrite that as
if a > b && b > c && c > d
Assuming that failing to find any of the strings means you want to follow the fail path, you could rewrite your code like this:
func endRound() {
var scored = false
timerLabel.isHidden = true
if let label1Index = allEvents.index(of: label1.text!),
let label2Index = allEvents.index(of: label2.text!),
let label3Index = allEvents.index(of: label3.text!),
let label4Index = allEvents.index(of: label4.text!),
label1Index > label2Index &&
label2Index > label3Index &&
label3Index > label4Index {
score = score + 1
let successImage = UIImage(named: "next_round_success")! as UIImage
nextRoundButtonOutlet.setImage(successImage, for: UIControlState.normal)
nextRoundButtonOutlet.isHidden = false
round = round + 1
} else {
let failImage = UIImage(named: "next_round_fail")! as UIImage
nextRoundButtonOutlet.setImage(failImage, for: UIControlState.normal)
nextRoundButtonOutlet.isHidden = false
round = round + 1
}
if (round == 6) {
}
}
That code should increase the score if all 4 strings are found and in the correct order. If one or more of the strings are not found, or anything is not in label1 label2 label3 label4 order, it will execute the else (fail) clause.
I think that's what you're after...

Related

Equation solver/parser

I have an app that solves physics problems based on given knowns and unknowns .. but it does it the wrong way..
let's say we have this equation ( d = v * t )
We have here three variables .. the way I did it is like this
let v = 10
let t = 5
let d = nil
let result = Double()
if d == nil && v != nil && t != nil {
result = v * t
}if v == nil && d != nil && t != nil {
result = d / t
}if t == nil && v != nil && d != nil {
result = d / v
}else{
result = nil
}
print(result) // "50"
obviously it's a headache .. this is only a small equation with three vars.. what if I had 20 equations each has 5 vars.. it would be very exhausting to type.. So I'm wondering if there is a way to just type the equation and call .solve or something .. something like this
let equation = "d = v * t"
let knowns = [d:20,v:50]
let result = equation.solve(usingKnowns: knowns)
print(result) // "Optional(t = 0.4)"
Thanks in advance,
Any help would be really great
The problem here is to see what variable is unknown, supose you have X variables and you always get X-1 variables known and 1 unknown you don't really need to check if the knowns variables are != nil.
Other possibility is the one I recommend you:
func resolve(a: Double?, b: Double?, c: Double?)-> Double{
//Default value
var result: Double = 0.0
//a missing
if let b = b, let c = c{
result = b*c
}
//b missing
if let a = a, let b = b{
result = a/b
}
//c missing
if let a = a, let c = c{
result = a/c
}
return result
}
In this case you have a single call with the 3 parameters, all of them optionals, so if you try a "if let" with 2 o them simultaneously and it works, it means the other one is the unknow variable.
This is the "cleanest" solution I can think of.

Updating code to latest Swift 4 syntax results in a "Thread 1: Fatal error: Index out of range" error

I'm currently in the process of updating some apps to the latest Swift 4 syntax and have run into a problem, and I'm sure I'm just missing something really obvious.
Initially my code was as follows:
let indices : [Int] = [0,1,2,3]
//let newSequence = shuffle(indices)
let newSequence = indices.shuffle()
var i : Int = 0
for(i = 0; i < newSequence.count; i++)
{
let index = newSequence[i]
if(index == 0)
{
// we need to store the correct answer index
currentCorrectAnswerIndex = i
}
Initially I had three error with the above code, namely:
Value of type '[Int]' has no member 'shuffle'
C-style for statement has been removed in Swift 3
Unary operator '++' cannot be applied to an operand of type '#lvalue Int'
To address these I changed the code so that it's now as follows:
let indices : [Int] = [0,1,2,3]
//let newSequence = shuffle(indices)
let newSequence = indices.shuffle()
var i : Int = 0
while i < newSequence.count {i += 1
let index = newSequence[i]
if(index == 0)
{
// we need to store the correct answer index
currentCorrectAnswerIndex = i
}
After changing the code and running a Product > Clean within Xcode, I no longer have any errors. However, when I use the app in the iOS Simulator it hangs at a certain point and checking within Xcode gives me the Thread 1: Fatal error: Index out of range error on the following line of code:
let index = newSequence[I]
I've read through other questions about the same error in Swift (see: 1, 2, 3 and 4) but these don't seem to apply in my scenario.
I know I'm missing something really obvious here, but at present it's just escaping me.
Any thoughts?
The Swift 3+ equivalent of the code is
let indices : [Int] = [0,1,2,3]
let newSequence = indices.shuffle() // this seems to be a custom function
for i in 0..<newSequence.count
{
let index = newSequence[i]
if index == 0 { // no parentheses in Swift
// we need to store the correct answer index
currentCorrectAnswerIndex = i
}
}
The reason for crash is that you increment i, perform some operations with it, and only then check if it is not out of range. To get rid of this error just move i += 1 to the end of your closure.
But why not use fast enumeration? You're still trying to do a loop the old way, but with new syntax. Instead of doing it C way
var i : Int = 0
while i < newSequence.count {
let index = newSequence[i]
if(index == 0)
{
// we need to store the correct answer index
currentCorrectAnswerIndex = i
}
i += 1
}
do it the correct Swift way
for (index, value) in newSequence.enumerated() {
if value == 0 {
currentCorrectAnswerIndex = index
}
}
Change position of (incrementing value) i as shown in below code. (Value of i is increment before it is used in loop operations, hence during final/last iteration of loop, value of i becomes larger than array indices)
let indices : [Int] = [0,1,2,3]
//let newSequence = shuffle(indices)
let newSequence = indices.shuffle()
var i : Int = 0
while i < newSequence.count {
let index = newSequence[i]
if(index == 0)
{
// we need to store the correct answer index
currentCorrectAnswerIndex = i
}
i += 1 // Update (increment) value for i at last
}
Update
(As suggested by MartinR) This would be better than you did:
let indices : [Int] = [0,1,2,3]
let newSequence = indices.shuffle()
for (i, newSeqIndex) in newSequence.enumerated() {
if (newSeqIndex == 0) {
currentCorrectAnswerIndex = i
}
}
You put a i += 1 in there that isn't in the original code.

Making a binary calculator app (swift)

For a project in Uni I decided to make a binary calculator app to learn a little bit more about coding.
I've come as far as completing a regular calculator app (but it only has two numbers: 1 ; 0) but I can't figure out how to make the calculator work like it should ( 1010 + 1101 = 10111 not 2111). All help is appreciated.
var numberEkraanil:Double = 0;
var eelmineNumber:Double = 0;
var tehesmatemaatikat = false
var operation = 0;
#IBOutlet weak var label: UILabel!
#IBAction func Numbers(_ sender: UIButton) {
if tehesmatemaatikat == true
{
label.text = String(sender.tag-1)
numberEkraanil = Double(label.text!)!
tehesmatemaatikat = false
}
else
{
label.text = label.text! + String(sender.tag-1)
numberEkraanil = Double(label.text!)!
}
}
#IBAction func nupud(_ sender: UIButton) {
if label.text != "" && sender.tag != 6 && sender.tag != 8
{
eelmineNumber = Double(label.text!)!
if sender.tag == 3 //Liitmine
{
label.text = "+";
}
else if sender.tag == 4 //Lahutamine
{
label.text = "-";
}
else if sender.tag == 5 // Korrutamine
{
label.text = "x";
}
else if sender.tag == 7 // Jagamine
{
label.text = "÷";
}
operation = sender.tag
tehesmatemaatikat = true;
}
else if sender.tag == 8
{
if operation == 3
{
label.text = String(eelmineNumber + numberEkraanil)
}
else if operation == 4
{
label.text = String(eelmineNumber - numberEkraanil)
}
else if operation == 5
{
label.text = String(eelmineNumber * numberEkraanil)
}
else if operation == 7
{
label.text = String(eelmineNumber / numberEkraanil)
}
}
else if sender.tag == 6
{
label.text = ""
eelmineNumber = 0;
numberEkraanil = 0;
operation = 0;
}
You can possibly convert binary numbers to decimal numbers. For example turn "1010" into int "10" and then reverse the process to get the binary again. In your example "1010 + 1101 = 10111" you can convert "1010" and into "10" and "13", make the ordinary calculation with those decimals and convert the result "23", which will give you "23".
But of course there are other ways. This website can help you with binary calculation. It's a math website: http://www.calculator.net/binary-calculator.html.
You will need to write a base 10 to base 2 and a base 2 to base 10 converter. Here is pseudo-code for both:
To convert a binary string to an integer, do the following:
Seed a result value to zero.
while your input string is not empty:
shift the result value 1 bit to the left
remove the left-most character from your input string, and if it's a 1, add 1 to your result value.
To convert an int to a binary string, do the same in reverse:
Copy your int to a scratch variable, scratch.
Set your output string to an empty string.
While scratch is not 0:
if scratch && 1 is 0, append a "0" to the left of your output string
if scratch && 1 is 1, append a "1" to the left of your output string
shift scratch 1 bit to the right
Once you have those building-blocks getting your calculator to work is pretty straightforward. When the user inputs a binary string, convert it to an integer working value. Do your calculations on integer values, and then convert the integer result to a binary string for display.

Integers Larger than Int64

I'm attempting to get a user input number and find the sum of all the digits. I'm having issues with larger numbers, however, as they won't register under an Int64. Any idea as to what structures I could use to store the value? (I tried UInt64 and that didn't work very well with negatives, however, I'd prefer something larger than UInt64, anyways. I'm having a hard time implementing a UInt128 from Is there a number type with bigger capacity than u_long/UInt64 in Swift?)
import Foundation
func getInteger() -> Int64 {
var value:Int64 = 0
while true {
//we aren't doing anything with input, so we make it a constant
let input = readLine()
//ensure its not nil
if let unwrappedInput = input {
if let unwrappedInt = Int64(unwrappedInput) {
value = unwrappedInt
break
}
}
else { print("You entered a nil. Try again:") }
}
return value
}
print("Please enter an integer")
// Gets user input
var input = getInteger()
var arr = [Int] ()
var sum = 0
var negative = false
// If input is less than 0, makes it positive
if input < 0 {
input = (input * -1)
negative = true
}
if (input < 10) && (input >= 1) && (negative == true) {
var remain = (-1)*(input%10)
arr.append(Int(remain))
input = (input/10)
}
else {
var remain = (input%10)
arr.append(Int(remain))
input = (input/10)
}
}
// Adds numbers in array to find sum of digits
var i:Int = 0
var size:Int = (arr.count - 1)
while i<=size {
sum = sum + arr[i]
i = (i+1)
}
// Prints sum
print("\(sum)")
You can use a string to perform the operation you describe. Loop through each character and convert it to an integer and add to the sum. Be careful to handle errors.

How to find out if letter is Alphanumeric or Digit in Swift

I want to count the number of letters, digits and special characters in the following string:
let phrase = "The final score was 32-31!"
I tried:
for tempChar in phrase {
if (tempChar >= "a" && tempChar <= "z") {
letterCounter++
}
// etc.
but I'm getting errors. I tried all sorts of other variations on this - still getting error - such as:
could not find an overload for '<=' that accepts the supplied arguments
For Swift 5 see rustylepord's answer.
Update for Swift 3:
let letters = CharacterSet.letters
let digits = CharacterSet.decimalDigits
var letterCount = 0
var digitCount = 0
for uni in phrase.unicodeScalars {
if letters.contains(uni) {
letterCount += 1
} else if digits.contains(uni) {
digitCount += 1
}
}
(Previous answer for older Swift versions)
A possible Swift solution:
var letterCounter = 0
var digitCount = 0
let phrase = "The final score was 32-31!"
for tempChar in phrase.unicodeScalars {
if tempChar.isAlpha() {
letterCounter++
} else if tempChar.isDigit() {
digitCount++
}
}
Update: The above solution works only with characters in the ASCII character set,
i.e. it does not recognize Ä, é or ø as letters. The following alternative
solution uses NSCharacterSet from the Foundation framework, which can test characters
based on their Unicode character classes:
let letters = NSCharacterSet.letterCharacterSet()
let digits = NSCharacterSet.decimalDigitCharacterSet()
var letterCount = 0
var digitCount = 0
for uni in phrase.unicodeScalars {
if letters.longCharacterIsMember(uni.value) {
letterCount++
} else if digits.longCharacterIsMember(uni.value) {
digitCount++
}
}
Update 2: As of Xcode 6 beta 4, the first solution does not work anymore, because
the isAlpha() and related (ASCII-only) methods have been removed from Swift.
The second solution still works.
Use the values of unicodeScalars
let phrase = "The final score was 32-31!"
var letterCounter = 0, digitCounter = 0
for scalar in phrase.unicodeScalars {
let value = scalar.value
if (value >= 65 && value <= 90) || (value >= 97 && value <= 122) {++letterCounter}
if (value >= 48 && value <= 57) {++digitCounter}
}
println(letterCounter)
println(digitCounter)
For Swift 5 you can do the following for simple strings, but be vigilant about handling characters like "1️⃣" , "④" these would be treated as numbers as well.
let phrase = "The final score was 32-31!"
var numberOfDigits = 0;
var numberOfLetters = 0;
var numberOfSymbols = 0;
phrase.forEach {
if ($0.isNumber) {
numberOfDigits += 1;
}
else if ($0.isLetter) {
numberOfLetters += 1
}
else if ($0.isSymbol || $0.isPunctuation || $0.isCurrencySymbol || $0.isMathSymbol) {
numberOfSymbols += 1;
}
}
print(#"\#(numberOfDigits) || \#(numberOfLetters) || \#(numberOfSymbols)"#);
I've created a short extension for letter and digits count for a String
extension String {
var letterCount : Int {
return self.unicodeScalars.filter({ CharacterSet.letters.contains($0) }).count
}
var digitCount : Int {
return self.unicodeScalars.filter({ CharacterSet.decimalDigits.contains($0) }).count
}
}
or a function to get a count for any CharacterSet you put in
extension String {
func characterCount(for set: CharacterSet) -> Int {
return self.unicodeScalars.filter({ set.contains($0) }).count
}
}
usage:
let phrase = "the final score is 23-13!"
let letterCount = phrase.characterCount(for: .letters)
In case you only need one information (letter or number or sign) you can do it in one line:
let phrase = "The final score was 32-31!"
let count = phrase.filter{ $0.isLetter }.count
print(count) // "16\n"
But doing phrase.filter several times is inefficient because it loops through the whole string.

Resources