Default values for function parameters where value passed in is nil - swift

Is there a neat way to combine default function parameter values and optionals, so the parameter takes on the value of the supplied default when the parameter is specified in the function call, but it's value is nil?
For example:
class MyObj {
var foobar:String
init(foo: String?="hello") {
self.foobar = foo!
}
}
let myObj = MyObj() // standard use of default values - don't supply a value at all
println(myObj.foobar) // prints "hello" as expected when parameter value is not supplied
var jimbob: String? // defaults to nil
...
// supply a value, but it is nil
let myObj2 = MyObj(foo: jimbob) // <<< this crashes with EXC_BAD_INSTRUCTION due to forced unwrap of nil value
println(myObj2.foobar)
...or is the best bet to give the member constants/variables default values and then only change them if there is a value provided to the constructor, like this:
let foobar:String = "hello"
init(foo: String?) {
if foo != nil {
self.foobar = foo!
}
}
Feels like there should be a neater solution, given other language features in this area.

How about:
class MyObj {
var foobar:String
init(foo: String?=nil) {
self.foobar = foo ?? "hello"
}
}

Related

Cannot force unwrap the value

var tuples: String! = "subash"
if let goin = tuples {
print(goin!)
}
I am receiving this error:
Cannot force unwrap the value of non-optional type String
I don't what happening constant goin is same as tuples but why it's showing me an error when I do force unwrap
Instead of the above code, this is running well:
var tuples: String! = "subash"
print(tuples!)
But kindly I need a solution to my above problem
That's something normal if you know how optionals work.
Inside the if statement, the right expression must be either Optional or Implicitly Unwrapped Optional, BUT NOT "normal" value.
This is the correct code:
let tuples: String! = "subash"
if let goin = tuples {
print(goin) // NO NEED to unwrap, because going IS NOT Optional
}
The reason this code runs fine:
var tuples: String! = "subash"
print(tuples!)
print(tuples)
... is because tuples is of type Implicitly Unwrapped Optional.
However, in general cases like this:
let myVar: String! = "some string"
if let myNewVar = myVar {
// Some code...
}
... myVar is always an Implicitly Unwrapped Optional, whereas myNewVar is of String type, because of how Optional Unwrapping works with if let statements.
Finally, if we unwrap the value this way:
let myVar: String! = "some string"
if let myVar = myVar {
// Some code...
print(myVar)
}
The printed value is the temp myVar, which is of type String and shadows the Implicitly Unwrapped myVar variable we initially declare.

Why does Optional Binding evaluate to type Bool?

Why is this variable i not substituted for Bool but optional binding is?
Swift claims this in the guide..
Swift’s type safety prevents non-Boolean values from being substituted
for Bool. The following example reports a compile-time error:
let i = 1
if i {
// this example will not compile, and will report an error
}
Yet this compiles
var foo: String?
if let bar = foo {
print("Non nil")
}
An optional is essentially an enum with two possible values either a designated value, in your example a string, or nil. An if let binding is not being substituted for a Bool in your example. Instead, it is checking if the variable foo is nil and if it is not nil then it sets bar equal to foo and performs the code inside the block. An if let binding is thus essentially a shortcut for
var foo: String?
if foo != nil {
//Set bar equal to the unwrapped value of foo
let bar = foo!
//Do whatever
}
The efficiency of this is best shown in a case where you would want to check if something is nil and check some characteristic of the value if it is not nil. Say you wanted to also check that foo has more than 5 characters and if it does then do something. Since foo could be nil you would have to check for it specifically to ensure it is not. An if let binding will let you do this:
if let bar = foo where bar.characters.count > 5 {
print(bar)
}
rather than this:
if foo != nil && foo?.characters.count > 5 {
let bar = foo!
print(bar)
}
It basically makes very readable, and thus more maintainable code. Also, it prevents you from having to unwrap the optional manually (the ! operator at the end of foo).

How to get the unwrapped type from an optional type in Swift?

I'm trying to get an unwrapped type from an optional type in runtime.
The following code would print the type of a as Optional<String>.
class MySubClass: MyClass {
var a: String? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(child.value.dynamicType)
}
Now I want to unwrap the type and get String, what should I do to make this happen in runtime?
Assuming you have an optional
let someVar: String?
then print(type(of: someVar)) will print
Optional<String>
but if you add the following extension to Optional
protocol OptionalProtocol {
func wrappedType() -> Any.Type
}
extension Optional: OptionalProtocol {
func wrappedType() -> Any.Type {
return Wrapped.self
}
}
then print(someVar.wrappedType()) will print
String
No reflection whatsoever
Summary
As long as the optional is not referenced by Any or AnyObject the code will work fine.
For Any you will have to cast it to OptionalProtocol first. Running
let someVar: String?
let anyVar = someVar as Any
if let op = anyVar as? OptionalProtocol {
print(op.wrappedType())
}
will print
String
As for AnyObject, strangely enough (at least for me), it doesn't cast to OptionalProtocol.
The original StackOverflow answer can be found here
I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.
My idea would be to test for all possible value types in your model objects could be or hold. Like:
let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
// do everything you need to store a string
}
else if let anInteger = myModelObject as? Int {
// do everything you need to store an integer
}
// and so on ...
Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.
You can either unwrap the optional explicitly with a bang (!) or with an if let.
For example:
var foo: String? = nil
if foo == nil {
print("foo is nil")
foo = "bar"
}
let fooBang = foo!
print("fooBang: \(fooBang)")
if let ifLetFoo = foo {
print("ifLetFoo: \(ifLetFoo)")
}
This will print:
foo is nil
fooBang: bar
ifLetFoo: bar
In your context, I think print(child.value.dynamicType!) might be what you're looking for.
If you cast the value to the non-optional String, it will print you the unwrapped type.
let mirror = Mirror(reflecting: a)
for child in mirror.children {
print(String(child.value).dynamicType) //String
}
Or you can play with the String and get the type from the Optional type.
class MySubClass: MyClass {
var a: Int? = nil
}
var a = MySubClass()
let mirror = Mirror(reflecting: a)
for child in mirror.children {
let typeArr = String(child.value.dynamicType).characters.split{$0 == "<"}.map(String.init)
let typeArr2 = typeArr[1].characters.split{$0 == ">"}.map(String.init)
print(typeArr2[0]) // print: Int
}
Sorry this is lame but you can do something like this.

Is there also a shortcut to prevent result from being nil or empty in Swift?

I can use the following expression to prevent a variable from being nil:
let result: String = someTextField.text ?? ""
Is there also a shortcut to prevent result from being nil or empty in Swift?
You can try to check it using the .isEmpty method or else use a default value
let result = (someTextField.text ?? "").isEmpty ? "default" : string
?? is called Nil Coalescing Operator
The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
According to you question, ?? just is the shortcut of preventing result from being nil.
And there is no shortcut to preventing result from being both nil and empty. As empty is type string, it's different from nil.
Here is the solution:
let result: String =
someTextField.text?.isEmpty() != false ? "is null or empty" : someTextField.text!
Yes you can do it with ?? and ? but if you want you can define your own short cut.
// So we can apply to any type by extending the type to conform to the protocol
protocol CanBeEmpty
{
var isEmpty: Bool { get }
}
extension String: CanBeEmpty
{
// String doesn't need the var cos it already has it
}
extension Optional where Wrapped: CanBeEmpty
{
func unwrappedOrDefault(def: Wrapped) -> Wrapped
{
switch self
{
case .None:
return def
case .Some(let wrapped):
return wrapped.isEmpty ? def : wrapped
}
}
}
let foo: String? = "foo"
let fooUnwrapped = foo.unwrappedOrDefault("default") // "foo"
let bar : String? = ""
let barUnwrapped = bar.unwrappedOrDefault("default") // "default"
let baz: String? = nil
let bazUnwrapped = baz.unwrappedOrDefault("default") // "default"
If you think that is too much typing, create an operator
infix operator ??? {}
func ???<T: CanBeEmpty> (a: Optional<T>, b: T) -> T
{
return a.unwrappedOrDefault(b)
}
foo ??? "default" // "foo"
bar ??? "default" // "default"
baz ??? "default" // "default"

Assigning `nil` value to a generically typed variable in Swift

I think var value: T = nil is causing error below because XCode can't convert nil value to the generic type T.
class Node<T> {
var value: T = nil
var next: Node
init(value: T) {
self.value = value
self.next = Node()
}
init() {
self.next = Node()
}
}
The error message reads
Could not find an overload for '_coversion' that accepts the supplied
arguments
Is there a way to assign nil value to a variable in Swift?
You need to declare the variable as optional:
var value: T? = nil
Unfortunately this currently seems to trigger an unimplemented compiler feature:
error: unimplemented IR generation feature non-fixed class layout
You can work around it by declaring T with a type constraint of NSObject:
class Node<T:NSObject> {
var value: T? = nil
var next: Node
init(value: T) {
self.value = value
self.next = Node()
}
init() {
self.next = Node()
}
}
Try using
var value: T? = nil
The question mark makes the variable an optional, meaning that it can either have a value or be nil.