In java 8, you can do the following: -
Optional<String> foo = Optional.empty(); // or Optional.of("hello");
foo.map(String::toUpperCase).orElse("Empty String")
edit - another example
class Bar {
public Bar(final String baz) { ... }
}
foo.map(f -> new Bar(f)) // has mapped into an optional Bar
That toUpperCase was a little too simplified; I'm thinking more like being able to specify a lambda or function that can provide a mutated value based on an optional present.
I see that you can do the following in swift: -
guard let foo = optional else { return "Empty String" }
which is massively useful, and also
optional ?? "Empty String"
but is there any way to map a value based on the value being optionally present? i.e. so if a value is present, you can return a mutated value?
This functionality is also available for java/c# collections
Say for a given array of optional strings, you can make use of optional chaining within your map operation
let foo: [String?] = ["foo", "bar", nil, "baz"]
let bar = foo.map { $0?.uppercaseString ?? "Empty string" }
print(bar) // ["FOO", "BAR", "Empty string", "BAZ"]
A possibly more useful alternative is to apply a flatMap operation to simply remove nil-valued optional entries from the string array
let foo: [String?] = ["foo", "bar", nil, "baz"]
let bar = foo.flatMap { $0?.uppercaseString }
print(bar) // ["FOO", "BAR", "BAZ"]
W.r.t. your comment, you could e.g. call a failable initializer for, say, some structure Foo, within the flatMap operation, yielding an array of Foo instances given that the initializer succeeds.
struct Foo {
let foo: String
init?(bar: String?) {
guard let foo = bar else {
return nil
}
self.foo = foo
}
}
let foo: [String?] = ["foo", "bar", nil, "baz"]
let bar = foo.flatMap { Foo(bar: $0) }
print(bar) // [Foo(foo: "foo"), Foo(foo: "bar"), Foo(foo: "baz")]
In your example, that's just:
optional?.uppercaseString ?? "EMPTY STRING"
or if you'd prefer to modify after defaulting:
(optional ?? "empty string").uppercaseString
Did you have something more complicated in mind?
(You may be unfamiliar with the ?. operator. It's called optional chaining in Swift, and is similar to map.)
Related
I have a simple class in my application:
class MyClass{
var Name: String
var Foo1: Bar
var Foo2: Bar?
//var Foos: [Bar]
}
Foo1 is never nil, and Foo2 is optional, so it might be nil.
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
init(_ Name: String, _ Foo1: Bar, _ Foo2: Bar?){
self.Name = Name
self.Foo1 = Foo1
self.Foo2 = Foo2
}
In C# I'd write something like MyClass m = new MyClass("m", New List<Bar>{Bar1, Bar2}) or MyClass m = new MyClass("m", New List<Bar>{Bar1, null}). I'd prefer to rather have one property in MyClass as a List, instead of two separate fields where one might be nil.
I've tried this initializer:
init(_ Name: String, _ Foos: [Bar]) {
self.Name = Name
self.Foos = Foos
}
But when I try to pass an anonymous List to the initializer, I get this warning:
let m = MyClass("m", [Bar]().append(B1))
Cannot use mutating member on immutable value: function call returns immutable value
How can I pass a populated anonymous list to the initializer in Swift, like I would've done in C#?
Try this code
struct Bar { }
class MyClass {
var name: String
var foos: [Bar?]
init(name: String, foos: [Bar?]) {
self.name = name
self.foos = foos
}
}
let bar0 = Bar()
let bar1: Bar? = nil
let object = MyClass(name: "String", foos: [bar0, bar1])
Hope you got the idea. And read more about how it works in Swift
The most challenging requirement, which is also not met by the accepted answer:
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
One way of dealing with this could be a custom runtime error.
enum BarWrapperError: Error {
case emptyList
}
To pass the list and check if it at least contains one element, you can introduce another type
struct BarWrapper {
init(bars: [Bar]) throws {
guard bars.count > 0 else {
throw BarWrapperError.emptyList
}
self.bars = bars
}
let bars: [Bar]
var firstBar: Bar {
return bars[0]
}
var otherBars:[Bar] {
return Array(bars[1 ..< bars.count])
}
}
BarWrapper is initialised with a list of bars. If this list is empty, it will throw an error.
your MyClass would now look like:
class MyClass {
let name: String
let firstBar: Bar
let otherBars: [Bar]
init(name: String, barWrapper: BarWrapper) {
self.name = name
self.firstBar = barWrapper.firstBar
self.otherBars = barWrapper.otherBars
}
}
If you care for the error and want to continue the execution, you can use this like
do {
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
} catch BarWrapperError.emptyList {
print("empty bar list")
}
If you rather want to crash the app if the list is empty, you can shorten it to
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try! BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
by using try!
You can also do
let bar0 = Bar()
let bar1 = Bar()
if let barWrapper = try? BarWrapper(bars: [bar0, bar1]) {
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
}
if you don't need error handling and your app would be still operational if the MyClass instance isn't created.
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).
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.
Let's say I want to write a method on Array that either returns a copy of the array if its type is non-optional, or a subarray of unwrapped values if its type is an optional. To do this, I think I need to be able to test whether the Array's type T is an optional type or not. For example, this just returns a copy of the array in either case:
extension Array {
func unwrapped() -> Array<T> {
return filter({
var x: T? = $0
return x != nil
})
}
}
I understand that if I know I have an array of optionals I could just use filter() and map():
let foo: [String?] = [nil, "bar", "baz"]
let bar: [String] = foo.filter({ $0 != nil }).map({ $0! })
I'm not looking for a solution to that specific problem. Rather I'm wondering if there's a way to determine in an Array extension if its type is optional, which could be useful in a number of different convenience methods.
One possible solution is to use global functions instead of extensions; then you can overload with two definitions, one for non-optional and one for optional types:
func unwrapped<T>(a: [T]) -> [T] { return a }
func unwrapped<T>(a: [T?]) -> [T] {
return a.filter { $0 != nil }.map { $0! }
}
unwrapped([1, 2, 3, 4, 5]) // -> [1, 2, 3, 4, 5]
unwrapped([1, 2, 3, nil, 5]) // -> [1, 2, 3, 5]
I'm unsure whether this is guaranteed to work; it would be interesting if someone can find a case where it breaks or somewhere in the Swift guides that says it's always correct.
See also the discussion in this question.
Use reduce:
let unwrapped = foo.reduce([String]()) {
if let bar = $1 as String? { return $0 + [bar] }
return $0
}
That will return a copy if the original is non-optional, or a subarray of non-nil unwrapped values if the original is optional. You don't need to know if the array contains optionals in order to do this.
If an array contains at least one value, you can determine if that value is an optional like this:
extension Array {
func optTest() {
switch reflect(self[0]).disposition {
case .Optional:
println("I am an optional")
default:
break
}
}
}
You would only need to test the first value. But I haven't figured out how to test an empty array - I suppose you could add an element to it and then test that element...
To do this, I think I need to be able to test whether the Array's type T is an optional type or not.
No, you don't have to - just use flatMap!
let foo: [String?] = [nil, "bar", "baz"]
let foo2: [String] = ["bar", "baz"]
foo.flatMap{$0}
foo2.flatMap{$0}
// yield both ["bar", "baz"] and are of type Array<String>
The Array struct can be extended to return its generic type, and then a protocol can be used to test for a typeless Optional.
The example is made for Swift 2, but I believe it should work similarly on previous versions.
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
extension Array {
func elementType() -> Any.Type {
return Element.self
}
}
[String]().elementType() is OptionalProtocol.Type // false
[String?]().elementType() is OptionalProtocol.Type // true
Specific to Swift 2, the Array extension can be foregone entirely since the typealias allow to access the Element type:
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
[String]().dynamicType.Element.self is OptionalProtocol.Type // false
[String?]().dynamicType.Element.self is OptionalProtocol.Type // true
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"
}
}