Is Nil-Coalescing always needed when using firstIndex(of: Character) in Swift? - swift

Excerpt from The Swift Programming Language (Swift 4.2) documentation by Apple.
let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
The above example works totally fine except after I exclude the Nil-coalescing part of the code.
let beginning = greeting[..<greeting.firstIndex(of: ",")]
By leaving out the ?? greeting.endIndex, Xcode returns an error.
The question is why is it necessary?
Why can't we just use firstIndex() directly to access the substring.

As it may be no index for the supplied character , so following your way the app will crash
func firstIndex(of element: Character) -> String.Index?
The optional return String.Index? solves it , and that's why you need ??
if you don't supply ??
Then you have to force unwrap let beginning = greeting[..<index] here index should be index! which will cause a crash if it's nil

Consider this example.
let greeting = "Hey There!"
let index = greeting.firstIndex(of: ",")
index is of type Index?, so it can be nil. Since the string has no ',', the value of index is nil.
So this code becomes illegal. Since index cannot be an optional value.
let beginning = greeting[..<index]
You can alternatively unwrap it and get the index if it is valid index like this.
guard let indexFound = index else {
print("Character not) found in string"
return
}
let beginning = greeting[..<indexFound]
print("The first occurance of the character is at \(beginning).")
Real world example: (I know it's not good, but it should be enough :x)
Remember the bot captcha's that pop up in websites? The ones that ask
you to identify the number of cars, street signs etc. So there you
click on the boxes which show the requirement. and click 'submit'. But
if there aren't any, you click 'Skip'. So drawing parallels. If you
have a valid index, it returns the valid index, otherwise it return
nil which is not an valid Index, but it is a valid answer to the
question asked. (Is there a ',' inside the string?) The problem here is, the next part. Slicing an array requires you to have a valid Index, so your index currently is an optional which you can change to a valid index by safely unwrapping like my example or having a nil-coalescing check at the time of getting the index.
You should look up optionals in Swift to get a better understanding.

Related

Fixed length array and a forced unwrapping of the last and the first elements

I have an array with 3 elements and want to take the first one and the last one elements.
let array = ["a", "b", "c"]
let first: String = array.first!
let last: String = array.last!
SwiftLint mark a force unwrap as a warning. Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
I don't want to use a default values like in an example below
let first :String = array.first ?? ""
Edit:
Why am I asking about it? Because, I would like to avoid an warnings from the SwiftLint when using a forced unwrapping when asking for a first and a last element of an array which was defined by a literal and has enough elements to be sure that there is the first and the last element.
Edit 2:
I have found a name for what I was looking for. It's called Static-Sized Arrays. Static-Sized Arrays discussion stoped in 2017 and there is no chance to use it.
Try with index:
let first = array[0]
let last = array[array.count - 1]
Why am I asking about it? Because, I would like to avoid an warnings
from the SwiftLint when using a forced unwrapping when asking for a
first and a last element of an array which was defined by a literal
and has enough elements to be sure that there is the first and the
last element.
You can't really avoid to unwrap optional value, so if you only need it for two cases extensions can help here.
extension Collection {
func first() -> Element {
guard let first = self.first else {
fatalError() // or maybe return any kind of default value?
}
return first
}
}
let array = [1, 2]
array.first() // 1
And if it need to be only in one swift file you can place this code in that file and mark extensions with private keyword.
Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
No you don't have to worry about it for a fixed array , actually the optional attachment for the properties first and last is designated to avoid crashes for an empty arrays

Swift 3: How to read from "Optional(Optional(stringValue))" without optional?

I got the string value from server like this.
let filename = "\(eventList[index]["filename"])"
But I got the value with Optional(Optional(stringValue)).
So I changed that like this.
let filename = "\(eventList[index]["filename"]!)"
Then I got the value with Optional(stringValue).
I can't do any more for this error.
How can I read the filename without any optional?
Use nil-coalescing operator aka double question mark operation. It is used to provide a default value when unwrapping an optional type.
let filename = eventList[index]["filename"] ?? ""
R̶e̶f̶:̶ ̶h̶t̶t̶p̶:̶/̶/̶w̶w̶w̶.̶j̶e̶e̶n̶a̶l̶i̶n̶f̶o̶t̶e̶c̶h̶.̶c̶o̶m̶/̶b̶l̶o̶g̶s̶/̶i̶o̶s̶/̶h̶o̶w̶-̶t̶o̶-̶d̶o̶-̶o̶p̶t̶i̶o̶n̶a̶l̶-̶v̶a̶r̶i̶a̶b̶l̶e̶-̶a̶s̶s̶i̶g̶n̶m̶e̶n̶t̶-̶w̶i̶t̶h̶-̶d̶e̶f̶a̶u̶l̶t̶-̶v̶a̶l̶u̶e̶-̶d̶o̶u̶b̶l̶e̶-̶q̶u̶e̶s̶t̶i̶o̶n̶-̶m̶a̶r̶k̶/̶
https://medium.com/#milanpanchal24/
Use if-let syntax to unwrap optional:
if let fileName = eventList[index]["filename"] {
// use fileName
}
eventList[index] accesses an array item at the given index. The item you are referring seems to be an optional dictionary so before accessing the dictionary item it needs to be unwrapped: eventLists[index]! (assuming it exists and valid of course otherwise it will crash)
then you can access the dictionary require value which is an optional as well:
eventLists[index]!["fileName"]!
assuming your list is valid you will get the desired String object.
I recommend using the safety checks (if-let or other variants) for preventing crashes

Swift 3 capitalize string

let first = postalText.text?[(postalText.text?.startIndex)!]
let second = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 1))!]
let third = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 2))!]
I'm trying to capitalize the FIRST and THIRD character and then merge all 3 into a new string
but the .uppercase and .capitalized doesn't work .
Also how do i check that the SECOND character is a number ?
.uppercased and .capitalized only work for strings, what you show there are Characters. You can cast a Character as a String and make it capitalized.
let firstCapitalized = String(first!).capitalized
If you want to check if a Character is an int, you can also make it a String, and then check if casting the String as an Int is non-nil:
if Int("\(second!)") != nil {
print("Is Integer")
}
These cases all assume your first, second, and third are all non-nil, and force-unwraps them.
EDIT
I had some free time and was overlooking some old posts on SO, and I realized this answer I posted isn't using the best coding form. First off, force unwrapping anything is always a bad idea (it's a recipe for a crash in the future), so for the first part. Do something like this:
let firstCapitalized = String(first ?? "").capitalized
This at least gives you a back-out in case first == nil then you'll just be stuck with an empty string.
For the second part, I would use optional unwrapping instead of if Int("\(second!)") != nil. I would say the more proper method would be something like this:
if let second = second, let stringConvertedToInteger = Int("\(String(second))") {
print("\(stringConvertedToInteger) is an integer")
} else {
print("Either second is nil, or it cannot be converted to an integer")
}
This will optionally unwrap the character second, and if it has a value, convert it to an integer (should it be one, checked by optional unwrapping). This is the safest way to do it, and will keep you from experiencing any runtime errors.

How do I take a substring to the first index of a character?

I'm very new to Swift; I've spent the morning reading StackOverflow and trying many strategies, in vain, to accomplish the following:
I have a string, say "12345 is your number!"
I want to extract "12345" to a variable.
In Java, I'd do something like:
String myStr = "12345 is your number!";
return myStr.substring(0, myStr.indexOf(" "));
How do I do something similar in Swift? I don't want to hard-code any assumptions about what the ending index will be. It might be 5 characters in, it might not. I just want to take the substring of everything up to the first occurrence of " ", wherever that might be.
The closest I've gotten so far is:
var myMessage = "12345 is your number!"
myMessage.endIndex.advancedBy(myMessage.characters.count - myMessage.characters.indexOf(" "))
but it doesn't compile for reasons I don't fully yet grok("Binary operator '-' cannot be applied to operands of type Distance (aka 'Int') and 'String.CharacterView.Index?'")
Any help on this is appreciated. Thank you.
Something like this should work:
myMessage.substringToIndex(myMessage.characters.indexOf(" ")!)
Note that in this code I force unwrapped the optional. If you're not guaranteed to have that space in the string, it might make more sense to have the index in a optional binding.
With optional binding, it would look something like this:
if let index = myMessage.characters.indexOf(" ") {
let result = myMessage.substringToIndex(index)
}
You can use a regex, try this code:
var myMessage = "12345 is your number!"
if let match = myMessage.rangeOfString("-?\\d+", options: .RegularExpressionSearch) {
print(myMessage.substringWithRange(match)) // 12345
let myNumber = Int(myMessage.substringWithRange(match)) // Then you can initialize a new variable
}
The advantage is that this method extracts only the numbers wherever they are in the String
Hope this help ;)
With Swift 5 you can use:
myStr.prefix(upTo: myStr.firstIndex(of: " ") ?? myStr.startIndex)
You may need to cast it back to String (String(myStr.prefix(upTo: myStr.firstIndex(of: " ") ?? myStr.startIndex))) since it returns a Substring

Swift: Remove text from string [duplicate]

I'm very new to Swift; I've spent the morning reading StackOverflow and trying many strategies, in vain, to accomplish the following:
I have a string, say "12345 is your number!"
I want to extract "12345" to a variable.
In Java, I'd do something like:
String myStr = "12345 is your number!";
return myStr.substring(0, myStr.indexOf(" "));
How do I do something similar in Swift? I don't want to hard-code any assumptions about what the ending index will be. It might be 5 characters in, it might not. I just want to take the substring of everything up to the first occurrence of " ", wherever that might be.
The closest I've gotten so far is:
var myMessage = "12345 is your number!"
myMessage.endIndex.advancedBy(myMessage.characters.count - myMessage.characters.indexOf(" "))
but it doesn't compile for reasons I don't fully yet grok("Binary operator '-' cannot be applied to operands of type Distance (aka 'Int') and 'String.CharacterView.Index?'")
Any help on this is appreciated. Thank you.
Something like this should work:
myMessage.substringToIndex(myMessage.characters.indexOf(" ")!)
Note that in this code I force unwrapped the optional. If you're not guaranteed to have that space in the string, it might make more sense to have the index in a optional binding.
With optional binding, it would look something like this:
if let index = myMessage.characters.indexOf(" ") {
let result = myMessage.substringToIndex(index)
}
You can use a regex, try this code:
var myMessage = "12345 is your number!"
if let match = myMessage.rangeOfString("-?\\d+", options: .RegularExpressionSearch) {
print(myMessage.substringWithRange(match)) // 12345
let myNumber = Int(myMessage.substringWithRange(match)) // Then you can initialize a new variable
}
The advantage is that this method extracts only the numbers wherever they are in the String
Hope this help ;)
With Swift 5 you can use:
myStr.prefix(upTo: myStr.firstIndex(of: " ") ?? myStr.startIndex)
You may need to cast it back to String (String(myStr.prefix(upTo: myStr.firstIndex(of: " ") ?? myStr.startIndex))) since it returns a Substring