Is there a shortcut to specify placeholder text when the value is nil in Swift?
Right now I do:
let myText:String!
if myDouble != nil{
myText = "\(myDouble!)"
}else{
myText = "Value not provided"
}
That works, but it's very annoying to have to do that all the time. Is there a way to do something like
let myText:String = "\(myDouble ?? "Value no provided")"
That fails because it wants a default Double value, but I really want a String value.
You can use map and nil-coalescing:
let myText = myDouble.map { String($0) } ?? "Value not provided"
If myDouble is nil, the result of map is nil and the result is the value after the ??.
If myDouble is not nil, the result is the output of the map which creates a string from the Double.
For more details, please see the documentation for the map function of the Optional enumeration in the Swift standard library.
It seems reasonable to do
let myDouble: Double? = Double(3.0)
let myText = myDouble?.description ?? "Value not provided"
If myDouble is nil, then myText is "Value not provided". If myDouble is not nil, it's assigned the string representation of the number.
I think good approach to this is to make Extension to optional where Double is the Wrapped element:
extension Optional where Wrapped == Double {
var stringValue: String {
guard let me = self else { return "No Value Provided" }
return "\(me)"
}
}
// Use it like this:
myDouble.stringValue
Another approach could be making your custom operator like this:
public func ??(rhd: Double?, lhd: String) -> String {
if let unwrapped = rhd {
return String(unwrapped)
} else {
return lhd
}
}
And now your line let myText:String = "\(myDouble ?? "Value no provided")" works.
Please let me now if you don' understand anything.
This should work:
let myText = myDouble != nil ? String(myDouble!) : "Value not provided"
Related
In Kotlin we have a list of standard (scope) functions (e.g. let, apply, run, etc)
Example of usage below
val str : String? = "123"
str?.let{ print(it) }
This makes the code looks more succinct without need to have if (str != null)
In swift, I code it as below
let str: String? = "123"
if str != nil { print(str!) }
I have to have if str != nil. Is there a let provided by default that I could use (without me writing my own)?
FYI, I'm new to Swift, and check around doesn't seems to find it.
if you like if, extend the functionality of Optional
extension Optional {
func `let`(do: (Wrapped)->()) {
guard let v = self else { return }
`do`(v)
}
}
var str: String? = "text"
str.let {
print( $0 ) // prints `text`
}
str = nil
str.let {
print( $0 ) // not executed if str == nil
}
You could use Optional.map :
let str1 : String? = "123"
str1.map { print($0) }
prints 123.
let str2 : String? = nil
str2.map { print($0) }
Doesn't print anything.
So if an optional is not nil, it is unwrapped and used as a parameter to the map closure. if not, the closure won't be called.
A more idiomatic approach in swift would be to use optional binding :
var str: String? = "123"
if let s = str {
print(s)
}
Apparently the Swift way of doing the nil check
let str: String? = "123"
if let strUnwrapped = str { print(strUnwrapped) }
And if really want to ensure it is not nil, use guard (thanks #CouchDeveloper for pointing out)
let str: String? = "123"
guard let strUnwrapped = str else { return }
I know this didn't explicitly answer the question of having scoping function or not, but it provides me my original intent of finding the Swift way of checking nil or non nil I was looking for like the let of Kotlin being used.
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 trying to convert an Int16 value to String but it gives value with Optional, won't allow me to forced unwrap it.
String(describing:intValue)
Result : Optional(intValue)
Unwrap intValue first, then pass it to the string initializer: String(unwrappedIntValue)
Here are some ways of handling the optional. I've added explicit string# variables with type annotations, to make it clear what types are involved
let optionalInt: Int? = 1
// Example 1
// some case: print the value within `optionalInt` as a String
// nil case: "optionalInt was nil"
if let int = optionalInt {
let string1: String = String(int)
print(string1)
}
else {
print("optionalInt was nil")
}
// Example 2, use the nil-coalescing operator (??) to provide a default value
// some case: print the value within `optionalInt` as a String
// nil case: print the default value, 123
let string2: String = String(optionalInt ?? 123)
print(string2)
// Example 3, use Optional.map to convert optionalInt to a String only when there is a value
// some case: print the value within `optionalInt` as a String
// nil case: print `nil`
let string3: String? = optionalInt.map(String.init)
print(string3 as Any)
// Optionally, combine it with the nil-coalescing operator (??) to provide a default string value
// for when the map function encounters nil:
// some case: print the value within `optionalInt` as a String
// nil case: print the default string value "optionalInt was nil"
let string4: String = optionalInt.map(String.init) ?? "optionalInt was nil"
print(string4)
You can convert a number to a String with string interpolation:
let stringValue = "\(intValue)"
Or you can use a standard String initializer:
let stringValue = String(intValue)
If the number is an Optional, just unwrap it first:
let optionalNumber: Int? = 15
if let unwrappedNumber = optionalNumber {
let stringValue = "\(unwrappedNumber)"
}
Or
if let unwrappedNumber = optionalNumber {
let stringValue = String(unwrappedNumber)
}
Let's say we have String?? value. Optional of Optional may have 3 states:
let strOptOpt1: String?? = .Some(.Some("actual value"))
let strOptOpt2: String?? = .Some(.None)
let strOptOpt3: String?? = .None
To safely unwrap them to String, in Swift 1.1 we could:
if let str:String = strOptOpt? {
println(str)
}
else {
println("was nil") // `.Some(.None)` or `.None`
}
But it does not work anymore in Swift 1.2:
if let str:String = strOptOpt? {
// ^ [!] error: '?' must be followed by a call, member lookup, or subscript
At the moment, I think, I have to do like this:
if let strOpt = strOptOpt, str = strOpt {
println(str)
}
OR using switch:
switch strOptOpt {
case let .Some(.Some(str)):
println(str)
default:
println("was nil")
}
But, there must be simpler way to do this, I think. Anyone know that?
As of Swift 2, you can use if/case with a pattern, and x? as
a synonym for .Some(x):
if case let str?? = strOptOpt1 {
print(str)
} else {
print("was nil") // `.Some(.None)` or `.None`
}
For me, the best solution I found is using ?? operator in optional binding.
let strOptOpt: String?? = ...
if let str = strOptOpt ?? nil {
println(str) // "actual value"
}
Works for all these cases:
.Some(.Some("actual value"))
.Some(.None)
.None
But, if strOptOpt is String???, you should do:
let strOptOpt: String??? = ...
if let str = (strOptOpt ?? nil) ?? nil {
println(str)
}
I always get double optional in this situation
let field: ChangeField?? = json["field_id"].int.map{ ChangeField(rawValue: $0) }
Instead of map you can use flatMap
let field: ChangeField? = json["field_id"].int.flatMap{ ChangeField(rawValue: $0) }
In “The Swift Programming Language.” book, Apple mentions using if and let together when accessing an optional variable.
The book gives the following code for example:
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
What is the advantage of using if let name = optionalName, rather than if optionalName != nil (and always referring to it as, optionalName)? Is there any difference, or is it simply convention?
Because it also unwraps the optional value, so this code:
if let name = optionalName {
greeting = "Hello, \(name)"
}
is equivalent to:
if optionalName != nil {
let name:String = optionalName!
greeting = "Hello, \(name)"
}
This language sugar is known as Optional Binding in Swift.
Optional Types
In Swift T and T? are not the same types, but the underlying value of an optional T? type can easily be realized by using the ! postfix operator, e.g:
let name:String = optionalName!
Which now can be used where a String is expected, e.g:
func greet(name:String) -> String {
return "Hello, \(name)"
}
greet(name)
Although as its safe to do so, Swift does let you implicitly cast to an optional type:
let name = "World"
let optionalName: String? = name
func greet(optionalName:String?) -> String? {
if optionalName != nil {
return "Hello, \(optionalName)"
}
return nil
}
//Can call with either String or String?
greet(optionalName)
greet(name)
It isn't actually needed in that case. You could just use optionalName in the if. But if optionalName was a calculated property it would have to be calculated in the conditional then again in the body. Assigning it to name just makes sure it is only calculated once.