Understanding function with string validation - ios

Consider following:
extension String {
func isValidEmail() -> Bool {
let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
print(characterset)
if self.rangeOfCharacter(from: characterset.inverted) != nil {
return false
} else {
return true
}
}
}
var name = "Login"
name.isValidEmail() // print true
var incorretLogin = "Loginъ"
incorretLogin.isValidEmail() // print false
Yes, function is work. But im in confussion - how its work?
If i understand correct it work like that:
it take set of characters, then check if all of tested string characters contain symbols from set, and if it is not, then it return false.
Ok, but what is inverted for? If i remove inverted, result will be wrong:
var name = "Login"
name.isValidEmail() // false
var incorretLogin = "Logъin"
incorretLogin.isValidEmail() // false
Now i understand nothing.
If function check simply if string letters are from character set, then why is it matter if set inverted or not?
Could someone explain?
I play a bit in playground:
let characterset = CharacterSet(charactersIn: "a")
print(characterset)
print(characterset.inverted)
Print same result:
<CFCharacterSet Items(U+0061)>
<CFCharacterSet Items(U+0061)>

inverted "returns an inverted copy of the receiver." (see https://developer.apple.com/documentation/foundation/characterset).
In your case inverted means all the characters except the ones you provide in the initializer (all characters except letters and digits). So the method returns false if the email string contains any character that is not a letter or a digit.
Playground example:

According to the documentation
rangeOfCharacter(from:)
Finds and returns the range in the receiver of the first character from a given character set.
The receiver is the string being checked. When no character from the set is found in the string, nil is returned.
When the set is inverted, it contains all invalid characters. Hence, rangeOfCharacter(from:) returns the location of the first invalid character. That is why your first approach works.
When you remove inverted, the call returns the location of the first valid character. Since "Logъin" has both valid and invalid characters, both calls return false. If you call your second function on a string consisting entirely of invalid characters, e.g. "Логин", you would get true.
Note that you can simplify the implementation by removing if:
let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
return self.rangeOfCharacter(from: characterset.inverted) == nil

Related

Checking if a string contains any special characters in swift while allowing all languages

I saw a similar post to this but none of the answers there helped me. I am looking to check if a string does not contain any special characters using swift regular expressions. I want to allow other iPhone keyboard letters though like Chinese, Hindi, Arabic, Korean, etc.. I only do not want to allow special characters like +, &, #, $, % though.
I have tried using:
func usernameTest(testStr:String) -> Bool {
return testStr.range(of: "^[ !\"#$%&'()*+,-./:;<=>?#\\[\\\\\\]^_`{|}~]+", options: .regularExpression) != nil
}
This did not work though. I thought it would check to see if any special characters were used, but when I tested phrases like "Tom###." and "%Will!!" it returned false. I would have expected this to return true since the strings that I passed in contained one or more of the special characters in the range.
Any help would be appreciated as we want our users to be able to create their usernames in any language but we still do not want to allow spaces or special characters.
you can try this way
var charSet = CharacterSet.init(charactersIn: "##$%+_)(")
var string2 = "test#3"
if let strvalue = string2.rangeOfCharacter(from: charSet)
{
print("true")
}
in the characterset init you can give the special characters which you want to check the occurance.
You must remove character "^" in the regex. So your function should look like this:
func usernameTest(testStr:String) -> Bool {
return testStr.range(of: "[ !\"#$%&'()*+,-./:;<=>?#\\[\\\\\\]^_`{|}~]+", options: .regularExpression) != nil }
func usernameTest(testStr:String) -> Bool {
let letters = CharacterSet.punctuationCharacters
let range = testStr.rangeOfCharacter(from: letters)
// range will be nil if no letters is found
if range != nil {
return true
}
else {
return false
}
}
try this one

Finding new line character in a String in "Swift 3"

In my app I am receiving some incoming data from a web service. In that data some wrong values can also be received like new line characters. I want to find in response string that if it contains a new line character or not.
Before Swift 3 I was able to do it like this
string.rangeOfString("\n")) == nil)
But in Swift 3 this methods is no longer available. However substring method is available which does with the help of Range.
I want to detect if my string contains "\n" how this would be accomplished using this method in Swift 3.
Short answer for Swift 5+
You can use
string.contains { $0.isNewline }
or KeyPath based syntax
string.contains(where: \.isNewline)
to detect if string contains any newline character.
Long answer
Swift 5 introduced couple of new properties on Character. Those simplify such tests and are more robust then simple check for \n.
Now you can use
myCharacter.isNewline
For complete list check Inspecting a Character section in Character docs
Example:
Character("\n").isNewline // true
Character("\r").isNewline // true
Character("a").isNewline // false
If you just want to know if it is there and don't care where it is, string.contains("\n") will return true if it is there and false if not.
You can also use
yourString.rangeOfCharacter(from: CharacterSet.newlines) != nil
which is more elegant as it's not using harcoded newline character string
Swift 3
string.range(of: "\n")
To check:
if string.range(of: "\n") == nil{
}
Or if you simply want to check the string contains \n or not, Then,
if !str.characters.contains("\n") {
}
.characters.contains() should do the trick:
let s1 = "Hi I'm a string\n with a new line"
s1.characters.contains("\n") // => true
let s2 = "I'm not"
s2.characters.contains("\n") // => false
let string:String = "This is a string"
if string.range(of: "\n") == nil {
print ("contains nil")
}
else {
print("contains new line")
}
perferctly working in swift 3.

How to capitalize each word in a string using Swift iOS

Is there a function to capitalize each word in a string or is this a manual process?
For e.g. "bob is tall"
And I would like "Bob Is Tall"
Surely there is something and none of the Swift IOS answers I have found seemed to cover this.
Are you looking for capitalizedString
Discussion
A string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values.
and/or capitalizedStringWithLocale(_:)
Returns a capitalized representation of the receiver using the specified locale.
For strings presented to users, pass the current locale ([NSLocale currentLocale]). To use the system locale, pass nil.
Swift 3:
var lowercased = "hello there"
var stringCapitalized = lowercased.capitalized
//prints: "Hello There"
Since iOS 9 a localised capitalization function is available as capitalised letters may differ in languages.
if #available(iOS 9.0, *) {
"istanbul".localizedCapitalizedString
// In Turkish: "İstanbul"
}
An example of the answer provided above.
var sentenceToCap = "this is a sentence."
println(sentenceToCap.capitalizedStringWithLocale(NSLocale.currentLocale()) )
End result is a string "This Is A Sentence"
For Swift 3 it has been changed to capitalized .
Discussion
This property performs the canonical (non-localized) mapping. It is suitable for programming operations that require stable results not depending on the current locale.
A capitalized string is a string with the first character in each word changed to its corresponding uppercase value, and all remaining characters set to their corresponding lowercase values. A “word” is any sequence of characters delimited by spaces, tabs, or line terminators (listed under getLineStart(_:end:contentsEnd:for:)). Some common word delimiting punctuation isn’t considered, so this property may not generally produce the desired results for multiword strings.
Case transformations aren’t guaranteed to be symmetrical or to produce strings of the same lengths as the originals. See lowercased for an example.
There is a built in function for that
nameOfString.capitalizedString
This will capitalize every word of string. To capitalize only the first letter you can use:
nameOfString.replaceRange(nameOfString.startIndex...nameOfString.startIndex, with: String(nameOfString[nameOfString.startIndex]).capitalizedString)
Older Thread
Here is what I came up with that seems to work but I am open to anything that is better.
func firstCharacterUpperCase(sentenceToCap:String) -> String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = sentenceToCap.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
or if I want to use this as an extension of the string class.
extension String {
var capitalizeEachWord:String {
//break it into an array by delimiting the sentence using a space
var breakupSentence = self.componentsSeparatedByString(" ")
var newSentence = ""
//Loop the array and concatinate the capitalized word into a variable.
for wordInSentence in breakupSentence {
newSentence = "\(newSentence) \(wordInSentence.capitalizedString)"
}
// send it back up.
return newSentence
}
}
Again, anything better is welcome.
Swift 5 version of Christopher Wade's answer
let str = "my string"
let result = str.capitalized(with: NSLocale.current)
print(result) // prints My String

Is a String part of another String in Swift

I programmed a Search Bar in Swift. Now I need to check if the searched Word is part of a Word in my Array
Example :
Search: hel - word in array: hello --> true
Search: bs - word in array: hello --> false
And is there a way to check that I only get true when the Search is the beginning of the Word in my Array --
Example:
Search: hel - word in array: hello --> true
Search: lo - word in array: hello --> false
Sorry for my bad english :D
This code will do exactly what you want, I assumed that you ensure that the input and the word form your array is a valid string. Using startIndex and endIndex is recommended to get proper glyph counting in a String in Swift and you can easily refer to a part of a string by using brackets:
var input = "hel"
let wordInArray = "hello"
if wordInArray[input.startIndex..<input.endIndex] == input {
println(true)
} else {
println(false)
}
this will evaluate to true when the input is "hel" and gives false in case of "lo"
(depending from your spec, you must check if the length of the search input is less or equal to the word in the Array or use min(input.endIndex,wordInArray.endIndex) at the max value of the range to prevent error)
In Objective-C there was a function for finding locations of characters in NSStrings. rangeOfString:. In swift, from what I've found, it's
var range = text.rangeOfString("your-string");
if(range.startIndex == 0)
//This was found at the very beginning of the text string
Work with that code a bit and you will be able to find the location of the found string inside the searched string.

Check if string is already a currency string?

I would like to create a function that looks at a string, and if it's a decimal string, returns it as a currency-formatted string. The function below does that, however if I pass in a string that is already formatted, it will fail of course (it expects to see a string like '25' or '25.55' but not '$15.25'
Is there a way to modify my function below to add another if condition that says "if you've already been formatted as a currency string, or your string is not in the right format, return X" (maybe X will be 0, or maybe it will be self (the same string) i'm not sure yet).
func toCurrencyStringFromDecimalString() -> String
{
var numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
if (self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()).utf16Count == 0)
{
//If whitespace is passed in, just return 0.0 as default
return numberFormatter.stringFromNumber(NSDecimalNumber(string: "0.0"))!
}
else if (IS_NOT_A_DECIMAL_OR_ALREADY_A_CURRENCY_STRING)
{
//So obviously this would go here to see if it's not a decimal (or already contains a current placeholder etc)
}
else
{
return numberFormatter.stringFromNumber(NSDecimalNumber(string: self))!
}
}
Thank you for your help!
Sounds like you need to use NSScanner.
According to the docs, the scanDecimal function of NSScanner:
Skips past excess digits in the case of overflow, so the receiver’s
position is past the entire integer representation.
Invoke this method with NULL as value to simply scan past a decimal integer representation.
I've been mostly programming in Obj-C so my Swift is rubbish, but here's my attempt at translating the appropriate code for detecting numeric strings (as also demonstrated in this answer):
let scanner: NSScanner = NSScanner(string:self)
let isNumeric = scanner.scanDecimal(nil) && scanner.atEnd
If the string is not a decimal representation, isNumeric should return false.

Resources