I am asking for user input (this works) and trying to output different results depending on if the input was nil, an empty string, or a non-empty string using a switch clause (doesn't work).
The first attempt got me an error because I'm trying to compare an optional string with a non-optional string:
import Foundation
print("Hi, please enter a text:")
let userInput = readLine(stripNewline: true)
switch userInput {
case nil, "": // Error: (!) Expression pattern of type ‘String’ cannot match values of type ‘String?’
print("You didn’t enter anything.")
default:
print("You entered: \(userInput)")
}
Fair enough, so I create a optional empty string to compare to:
import Foundation
print("Hi, please enter a text:")
let userInput = readLine(stripNewline: true)
let emptyString: String? = "" // The new optional String
switch userInput {
case nil, emptyString: // Error: (!) Expression pattern of type ‘String?’ cannot match values of type ‘String?’
print("You didn’t enter anything.")
default:
print("You entered: \(userInput)")
}
So that gives me an error saying I cannot compare a ‘String?’ to a ‘String?’.
Why is that? Are they somehow still not the same type?
PS. I have the feeling that I'm missing something fundamental here, such as what Troy pointed out about optionals not being "the same type as the corresponding non-otpional type but with the additional possibility of not having a value" (not an exact quote, see his UPDATE at the end of the question: What does an exclamation mark mean in the Swift language?). But I'm stuck connecting the final dots why this isn't ok.
Replace
case `nil`, "":
with
case nil, .Some(""):
Note that optional is an enum with two possible values: .None and .Some(value).
As for your second example with String?, note that the matching in case is performed using ~= operator which is not defined for optionals.
If you define:
#warn_unused_result
public func ~=<T: Equatable>(lhs: T?, rhs: T?) -> Bool {
return lhs == rhs
}
both your examples will start to work (not recommended).
Did you try to get your userInput text as a String:
let response = userInput ?? ""
switch response {
case "":
print("You didn’t enter anything.")
default:
print("You entered: \(response)")
}
Then you would be sure it is the same type String.
I hope it works
Just as a supplement to the existing good answers: Instead of testing
against nil and the empty string you can go the other way around
and test against a non-nil string (with the input? pattern) and
add a where constraint:
switch userInput {
case let input? where !input.isEmpty:
print("You entered", input)
default:
print("You did not enter anything")
}
Equivalently:
if case let input? = userInput where !input.isEmpty {
print("You entered", input)
} else {
print("You did not enter anything")
}
However: readLine() returns nil only on an end-of-file
condition, which means that no data can be read anymore from standard
input. Therefore it might make more sense to terminate the
program in that case:
guard let userInput = readLine(stripNewline: true) else {
fatalError("Unexpected end-of-file")
}
// `userInput` is a (non-optional) String now
if userInput.isEmpty {
print("You did not enter anything")
} else {
print("You entered", userInput)
}
see the self-explanatory example ...
let txtArr: [String?] = [nil, "", "some text"]
for txt in txtArr {
switch txt {
case nil:
print("txt is nil")
case let .Some(t) where t.isEmpty:
print("txt is empty string")
default:
print("txt is \(txt)")
}
}
the code prints
txt is nil
txt is empty string
txt is Optional("some text")
Related
I have below func in my class.
static func getFirstCharInName(strName: String) -> String {
let firstCharInName = String(strName.first)
return firstCharInName.trim()
}
I encountered this err:
Value of optional type 'Character?' must be unwrapped to a value of type 'Character'
What seems to be the problem?
Thanks
func getFirstCharInName(strName: String) -> String {
let indexStartOfText = strName.index(strName.startIndex, offsetBy: 0)
let indexEndOfText = strName.index(strName.startIndex, offsetBy: 0)
let firstChar = String(strName[indexStartOfText...indexEndOfText])
return firstChar
}
This error means that the expression has optional value (the value can be nil) that is not yet unwrapped, strName.first returns an optional value of Character?, but your function demands a returning type of String which is not an optional type.
So, in order to fix this, you need to unwrap the optional value strName.first, it seems like you are not familiar with optionals, here's the code for your case (choose one from two options):
func getFirstCharInName(strName: String) -> String {
// option 1: force unwrap - can cause fatal error
return String(strName.first!)
// option 2: optional binding
if let firstCharInName = strName.first {
return String(firstCharInName)
} else {
// if the optional value is nil, return an empty string
return ""
}
}
PS. I don't really understand the function trim() in your question, but if you mean to strip away the blank spaces like " ", you can do:
firstCharInName.trimmingCharacters(in: .whitespaces)
Avoid the optional simply with prefix, it's totally safe. if there is no first character you'll get an empty string.
static func getFirstChar(in name: String) -> String { // the function name getFirstChar(in name is swiftier
return String(name.prefix(1))
}
I don't know what the trim function is supposed to do.
It means that value of optional type 'Character?' (as result of your part of code strName.first) must be unwrapped to a value of type 'Character' before you will be gonna cast it to String type.
You may use this variant:
func getFirstCharInName(strName: String) -> String {
return strName.count != 0 ? String(strName.first!) : ""
}
As you can see, the exclamation point is in the string strName.first! retrieves the optional variable as it was needed.
you can do something like that:
extension String {
var firstLetter: String {
guard !self.isEmpty else { return "" }
return String(self[self.startIndex...self.startIndex])
}
}
then
let name = "MilkBottle"
let first = name.firstLetter // "M"
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
}
I created some test code to show the problem I am having.
This compiles fine in a playground, however, when I try to put it into a project, Xcode gives the following warning: Treating a forced downcast to 'String' as optional will never produce 'nil' on line 30. I am given two suggestions to fix the problem:
Use 'as?' to perform a conditional downcast to 'String', which makes absolutely no sense. However, it does compile without warnings/errors, which seems strange because it is assigning an optional value to a non-optional type of String.
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
From the Swift language guide.
Unless it thinks I might want to assign nil if the conversion fails (therefore removing the dictionary entry), this makes no sense. Especially because I am sure it will succeed because I literally just checked to see if it was a String.
Add parentheses around the cast to silence this warning, which seems pointless, but does silence the warning. This seems like a strange thing to do, but then again, it may just be a poor way of confirming that you really want to do what you are trying to do.
Which option is right, or neither? What is causing this warning?
The correct solution is to use optional binding instead of the
forced unwrap operator !. Actually you can incorporate the check
value != nil into the switch statement:
for (key, value) in dict {
switch value {
case let s as String:
newDict[key] = s
case let i as Int:
newDict[key] = String(i)
case let b as Bool:
newDict[key] = b ? "1" : "0"
case let v?: // value is not `nil`
newDict[key] = String(v)
default: // value is `nil`
break
}
}
Here is your code, modified to show how you can cast a function result to match AnyObject? and avoid explicitly unwrapped optionals:
func returnsAString() -> AnyObject? {
return "I am a String." as? AnyObject
}
func returnsAnInt() -> AnyObject? {
return Int(123) as? AnyObject
}
func returnsABool() -> AnyObject? {
return true as? AnyObject
}
func returnsNilBool() -> AnyObject? {
return nil as Bool? as? AnyObject
}
var dict : [String : AnyObject?] = [String : AnyObject?]()
var newDict : [String : String ] = [String : String ]()
dict["string"] = returnsAString()
dict["int"] = returnsAnInt()
dict["bool"] = returnsABool()
dict["nil"] = returnsNilBool()
for (key, value) in dict {
switch value {
case let value as String:
newDict[key] = value
case let value as Int:
newDict[key] = String(value)
case let value as Bool:
newDict[key] = (value ? "1" : "0")
default:
newDict[key] = "nil"
}
}
print("Dict: \(dict)")
print("newDict: \(newDict)")
// Dict: ["nil": nil, "int": Optional(123), "bool": Optional(1), "string": Optional(I am a String.)]
// newDict: ["nil": "nil", "int": "123", "bool": "1", "string": "I am a String."]
This question already has answers here:
How to determine the type of a variable in Swift
(5 answers)
Closed 8 years ago.
What's wrong with my code that tries to check the type of a variable?
The following code produces error that says "'is' test is always true". Note that I don't want to set p to a value because it could be nil, hence the use of optional.
import Foundation
var p:String?
if p as String? {
println("p is a string type")
}
else {
println("p is not a string type")
}
Now if I test against String type, it won't even compile:
import Foundation
var p:String?
if p as String {
println("p is a string type")
}
else {
println("p is not a string type")
}
Is this a compiler bug? If not what did I do wrong?
Adding on to the answers that revolve around optional binding, there is a more direct way that Apple provides.
The is operator exists exactly for this purpose. However, it doesn't allow you to test trivial cases but rather for subclasses.
You can use is like this:
let a : Any = "string"
if a is String {
println("yes")
}
else {
println("no")
}
As expected, that prints yes.
You already know that p is an optional string. You don't need to convert it to a type, you can simply do Optional Binding:
if let aString = p {
println("p is a string: \(aString)")
}
else {
println("p is nil")
}
Normally you check if a variable is of a certain type using the as? operator:
var something : AnyObject = "Hello"
if let aString = something as? String {
println("something is a string: \(aString)")
}
but you do not use that mechanism when checking if an optional is nil.
This will also work if your object is an optional:
var something : AnyObject? = "Hello"
if let aString = something as? String {
println("something is a string: \(aString)")
}
Since p is an optional String, you can use it in your conditional test like so:
import Foundation
var p: String?
if p {
println("p has been assigned a String value")
}
else {
println("p is nil")
}
If p has been assigned a String value and you would like to use that value in the body of your if statement, then you can use 'optional binding' to assign its value to a local constant you can work with:
if let pValue = p {
println("p is assigned \(pValue)")
}
else {
println("p is nil")
}
In Objective C, one could do the following to check for strings:
if ([myString isEqualToString:#""]) {
NSLog(#"myString IS empty!");
} else {
NSLog(#"myString IS NOT empty, it is: %#", myString);
}
How does one detect empty strings in Swift?
There is now the built in ability to detect empty string with .isEmpty:
if emptyString.isEmpty {
print("Nothing to see here")
}
Apple Pre-release documentation: "Strings and Characters".
A concise way to check if the string is nil or empty would be:
var myString: String? = nil
if (myString ?? "").isEmpty {
print("String is nil or empty")
}
I am completely rewriting my answer (again). This time it is because I have become a fan of the guard statement and early return. It makes for much cleaner code.
Non-Optional String
Check for zero length.
let myString: String = ""
if myString.isEmpty {
print("String is empty.")
return // or break, continue, throw
}
// myString is not empty (if this point is reached)
print(myString)
If the if statement passes, then you can safely use the string knowing that it isn't empty. If it is empty then the function will return early and nothing after it matters.
Optional String
Check for nil or zero length.
let myOptionalString: String? = nil
guard let myString = myOptionalString, !myString.isEmpty else {
print("String is nil or empty.")
return // or break, continue, throw
}
// myString is neither nil nor empty (if this point is reached)
print(myString)
This unwraps the optional and checks that it isn't empty at the same time. After passing the guard statement, you can safely use your unwrapped nonempty string.
In Xcode 11.3 swift 5.2 and later
Use
var isEmpty: Bool { get }
Example
let lang = "Swift 5"
if lang.isEmpty {
print("Empty string")
}
If you want to ignore white spaces
if lang.trimmingCharacters(in: .whitespaces).isEmpty {
print("Empty string")
}
Here is how I check if string is blank. By 'blank' I mean a string that is either empty or contains only space/newline characters.
struct MyString {
static func blank(text: String) -> Bool {
let trimmed = text.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
return trimmed.isEmpty
}
}
How to use:
MyString.blank(" ") // true
You can also use an optional extension so you don't have to worry about unwrapping or using == true:
extension String {
var isBlank: Bool {
return self.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
}
extension Optional where Wrapped == String {
var isBlank: Bool {
if let unwrapped = self {
return unwrapped.isBlank
} else {
return true
}
}
}
Note: when calling this on an optional, make sure not to use ? or else it will still require unwrapping.
To do the nil check and length simultaneously
Swift 2.0 and iOS 9 onwards you could use
if(yourString?.characters.count > 0){}
isEmpty will do as you think it will, if string == "", it'll return true.
Some of the other answers point to a situation where you have an optional string.
PLEASE use Optional Chaining!!!!
If the string is not nil, isEmpty will be used, otherwise it will not.
Below, the optionalString will NOT be set because the string is nil
let optionalString: String? = nil
if optionalString?.isEmpty == true {
optionalString = "Lorem ipsum dolor sit amet"
}
Obviously you wouldn't use the above code. The gains come from JSON parsing or other such situations where you either have a value or not. This guarantees code will be run if there is a value.
Check check for only spaces and newlines characters in text
extension String
{
var isBlank:Bool {
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty
}
}
using
if text.isBlank
{
//text is blank do smth
}
Swift String (isEmpty vs count)
You should use .isEmpty instead of .count
.isEmpty Complexity = O(1)
.count Complexity = O(n)
isEmpty does not use .count under the hood, it compares start and end indexes startIndex == endIndex
Official doc Collection.count
Complexity: O(1) if the collection conforms to RandomAccessCollection; otherwise, O(n), where n is the length of the collection.
Single character can be represented by many combinations of Unicode scalar values(different memory footprint), that is why to calculate count we should iterate all Unicode scalar values
String = alex
String = \u{61}\u{6c}\u{65}\u{78}
[Char] = [a, l, e, x]
Unicode text = alex
Unicode scalar values(UTF-32) = u+00000061u+0000006cu+00000065u+00000078
1 Character == 1 extended grapheme cluster == set of Unicode scalar values
Example
//Char á == extended grapheme cluster of Unicode scalar values \u{E1}
//Char á == extended grapheme cluster of Unicode scalar values \u{61}\u{301}
let a1: String = "\u{E1}" // Unicode text = á, UTF-16 = \u00e1, UTF-32 = u+000000e1
print("count:\(a1.count)") //count:1
// Unicode text = a, UTF-16 = \u0061, UTF-32 = u+00000061
// Unicode text = ́, UTF-16 = \u0301, UTF-32 = u+00000301
let a2: String = "\u{61}\u{301}" // Unicode text = á, UTF-16 = \u0061\u0301, UTF-32 = u+00000061u+00000301
print("count:\(a2.count)") //count:1
For optional Strings how about:
if let string = string where !string.isEmpty
{
print(string)
}
if myString?.startIndex != myString?.endIndex {}
I can recommend add small extension to String or Array that looks like
extension Collection {
public var isNotEmpty: Bool {
return !self.isEmpty
}
}
With it you can write code that is easier to read.
Compare this two lines
if !someObject.someParam.someSubParam.someString.isEmpty {}
and
if someObject.someParam.someSubParam.someString.isNotEmpty {}
It is easy to miss ! sign in the beginning of fist line.
public extension Swift.Optional {
func nonEmptyValue<T>(fallback: T) -> T {
if let stringValue = self as? String, stringValue.isEmpty {
return fallback
}
if let value = self as? T {
return value
} else {
return fallback
}
}
}
What about
if let notEmptyString = optionalString where !notEmptyString.isEmpty {
// do something with emptyString
NSLog("Non-empty string is %#", notEmptyString)
} else {
// empty or nil string
NSLog("Empty or nil string")
}
You can use this extension:
extension String {
static func isNilOrEmpty(string: String?) -> Bool {
guard let value = string else { return true }
return value.trimmingCharacters(in: .whitespaces).isEmpty
}
}
and then use it like this:
let isMyStringEmptyOrNil = String.isNilOrEmpty(string: myString)