Bronze Challenge - is there a simpler way


#1

Hi You All!!!
Really had my problems with that Challenge. What a downer, already problems with the first challenge. I just could not figure out that whole NSCharacterSet stuff… So I had to come up with this approach:
First I made sure to allow any “”-String (is that called an empty String?), so I’m able to delete.
Then I only try to initialise an Int from a String - Int(string) - , instead of trying to match with NSCharacterSet. And if that initialisation fails (e.g. Int(string) == nil ), then string can not be a number, right?

here is the whole method:

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        let existingTextHasDecimalSeperator = textField.text?.rangeOfString(".")
        let replacementTextHasDecimalSeperator = string.rangeOfString(".")
        
        
        if string == "" {
            // this would be the case if I try to delete an character
            return true   
        }
        else if (existingTextHasDecimalSeperator != nil && replacementTextHasDecimalSeperator != nil) || Int(string) == nil {
            return false
        }
        else {
            return  true
        }
    }

What do you think? Did I make some wrong assumptions?
Greetings from Germany


#2

If you could be a little more specific, then maybe we could explain away your confusion. For instance, do you understand what the following code does:

    let letters = NSCharacterSet.letterCharacterSet()
    
    if string.rangeOfCharacterFromSet(letters) != nil {
        return false
    }

I think everyone had problems, so keep your chin up. I ended up using a mix of things including a regex to make sure the string only contains numbers and decimal points, as well as marching through the string to count decimal points. I think that short if statements that test for one condition, then accept or reject the string is a reasonable approach to take.

I don’t know what your general experience is with computer programming, but iOS programming is not for beginners. You need to have already studied a couple of other languages and programmed for several years prior to trying to learn iOS programming, and you should probably have had some GUI programming experience in another language as well.

In addition, the book doesn’t explain much of the code, so unless you are capable of searching, reading, and understanding the docs, you have no chance of learning iOS programming with this book.

In any case, the challenge you faced is a bottomless pit. There is no way to account for all the possibilities without a ton of code, so at some point you have to accept that your solution won’t be perfect and stop there. Someone else posted a question wondering why people were having so many problems with this challenge when they felt the solution was very easy. After many iterations of pointing out the flaws in their code, and the person adding more sophisticated and complicated if statements to fix the flaws, they finally gave up trying to handle all the possibilities.

First I made sure to allow any “”-String (is that called an empty String?), so I’m able to delete

In other languages that is known as either an “empty string” or a “blank string”. Either one is fine. I spent a lot of time testing the delete/backspace thing to figure out what actually happens when you hit the delete key, and the test I came up with was:

    if string.characters.count == 0 {
        return true
    }

See the discussion here:

Your solution is shorter (therefore better!). I can’t remember if there was a reason why I didn’t just test for "" (Ah, yes. See the discussion at the link above for why testing for a blank string won’t necessarily work.)

Then I only try to initialise an Int from a String - Int(string) - , instead of trying to match with NSCharacterSet.

That’s a tricky solution! I like it.

And if that initialisation fails (e.g. Int(string) == nil ), then string can not be a number, right?

Unfortunately, that conclusion is incorrect. For instance, 89.5 is a number but Int("89.5") returns nil. But let’s think about that. The temperature converter isn’t going to be used by physicists at CERN to theorize about particle collisions where high precision is needed. Rather, the temperature converter is going to be used by world travelers who want to know approximately what 21 degree celsius is in Fahrenheit–they don’t need to be able to enter decimal points at all.

But, if I remember correctly, the challenge does talk about allowing numbers with a single decimal point to be typed into the converter. You could always use Double().

I think the point of the challenge was to introduce us to some of the String functions and string handling in general. For instance, you can’t just say mytext.count to get the length of a string. You might want to read Apple’s Guide to Swift [about Strings] (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID285) up to the Unicode section, then just call it good and move on. You don’t need to learn everything about String handling the first time you try it.

Part of learning to program in a given language is learning what features the language provides. This challenge introduced us to some of the features of Strings that Swift and iOS provide.


#3

duplicate duplicate duplicate


#4

I solved the problem this way.

let decimalCharacterSet = NSCharacterSet(charactersInString: "0123456789.\\b")
if string.rangeOfCharacterFromSet(decimalCharacterSet.invertedSet) != nil {
    return false
}

if the replacement string contains any characters not 0-9, ‘.’ or backspace, then it is rejected. You don’t need any additional checks for “” to detect the delete key

“.” is accepted
"24" is accepted
"2/4" is rejected
"84.2" is accepted
"0x12" is rejected


#5

In Swift, “\\b” does not represent a backspace:

let str = "123\\b"

for charCode in str.utf8 {
    print(charCode)
}

--output:--
49
50
51
92  <---
98  <--

A backspace is represented by the ascii code 8. For instance, here is your string in python:

s = "123\b"

for char in s:
    print(ord(char))

--output:--
49
50
51
8

According to the Swift language guide, only the following escape sequences are recognized inside Strings:

Null Character (\0)
Backslash (\\)
Horizontal Tab (\t)
Line Feed (\n)
Carriage Return (\r)
Double Quote (\")
Single Quote (\')
Unicode scalar (\u{n}), where n is between one and eight hexadecimal digits

So your Swift String actually consists of a literal backslash followed by a “b”.

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html

To represent a backspace in a String, you would have to write:

let str = "123\u{8}"

Now, look at the output:

let str = "123\u{8}"

for charCode in str.utf8 {
    print(charCode)
}

--output:--
49
50
51
8   <--- backspace

#6

Both \\b and \u{8} work, although i read your documentation reference and agree the escape sequence is not documented. I need to think about why it worked.


#7

They may produce the same result in your code, but "\\b" and "\u{8}" are not equivalent. The first one is two characters long and has nothing to do with a backspace. The second is one character long and represents a backspace.


#8

Hey, I’ve got an idea. There’s automatic bridging between a Swift string and NSString, so if a Swift string is bridged to NSString, then a literal backslash("\") and a literal “b” may be converted to a backspace.


#9

I’m totally with you. \u{8} is the correct representation of backspace. I appreciate that you pointed it out.

With further testing, i don’t think “\\b” actually works. In fact, with this in the character set string, it errantly accepts ‘’ and ‘b’. So I’m totally convinced with your assessment that it is really just characters 92 and 98.

I think there is another explanation. I removed “\\b” from the character set and only included “0123456789.”

let decimalCharacterSet = NSCharacterSet(charactersInString: "0123456789.")
if string.rangeOfCharacterFromSet(decimalCharacterSet.invertedSet) != nil {
    return false
}

it accepted backspace! Maybe because string is “”, it doesn’t return nil against the inverted Set. Informal testing seems to point to this. “”.rangeOfCharacterFromSet(…) seems to never return nil


#10

With further testing, i don’t think “\b” actually works.

Good. I hate having to blame weird things on bridging.

“”.rangeOfCharacterFromSet(…) seems to never return nil

Hmmm…I’m not seeing that:

import UIKit

let decimalCharacterSet = NSCharacterSet(charactersInString: "0123456789.")
print("".rangeOfCharacterFromSet(decimalCharacterSet))

--output:--
nil

#11

Sorry, i got mixed up. Yes, it DOES return nil, therefore, my test for != nil fails and doesn’t reject it.


#12

Wow! I had not seen all these responses! Great!

After a short hiatus I am back at working through the book. Thought I have now bought the Sixt edition. Currently I am in chapter 13. I will now read everything all of you posted!