What Are the Advantages/Use Cases of Optional Patterns Introduced in Swift 2

What are the advantages/use cases of optional patterns introduced in swift 2?

I believe you're conflating two different concepts. Admittedly, the syntax isn't immediately intuitive, but I hope their uses are clarified below.
(I recommend reading the page about Patterns in The Swift Programming Language.)

case conditions

The "case condition" refers to the ability to write:

  • if case «pattern» = «expr» { ... }
  • while case «pattern» = «expr» { ... }
  • for case «pattern» in «expr» { ... }

These are particularly useful because they let you extract enum values without using switch.

Your example, if case let x? = someOptional ..., is a valid example of this, however I believe it's most useful for enums besides Optional.

enum MyEnum {
case A
case B(Int)
case C(String)
}

func extractStringsFrom(values: [MyEnum]) -> String {
var result = ""

// Without case conditions, we have to use a switch
for value in values {
switch value {
case let .C(str):
result += str
default:
break
}
}

// With a case condition, it's much simpler:
for case let .C(str) in values {
result += str
}

return result
}

You can actually use case conditions with pretty much any pattern that you might normally use in a switch. It can be weird sometimes:

  • if case let str as String = value { ... } (equivalent to if let str = value as? String)
  • if case is String = value { ... } (equivalent to if value is String)
  • if case 1...3 = value { ... } (equivalent to if (1...3).contains(value) or if 1...3 ~= value)

The optional pattern, a.k.a. let x?

The optional pattern, on the other hand, is a pattern that lets you unwrap optionals in contexts besides a simple if let. It's particularly useful when used in a switch (similar to your username/password example):

func doSomething(value: Int?) {
switch value {
//case 2: // Not allowed
case 2?:
print("found two")

case nil:
print("found nil")

case let x:
print("found a different number: \(x)")
}
}

What does case mean without switch statement in Swift?

This is the Optional Pattern. It tests and unwraps an Optional, executing the conditional only if the Optional is non-nil.

The keyword case is needed because it follows from the original switch...case syntax. The case tests a pattern and if it matches then the following statement is executed. In your example the let next? is the pattern. If the value is unwrapped and assigned then the case matches and your code is executed.

From the documentation:

Optional Pattern

An optional pattern matches values wrapped in a Some(Wrapped) case of
an Optional or ImplicitlyUnwrappedOptional
enumeration. Optional patterns consist of an identifier pattern
followed immediately by a question mark and appear in the same places
as enumeration case patterns.

Because optional patterns are syntactic sugar for Optional and
ImplicitlyUnwrappedOptional enumeration case patterns, the following
are equivalent:

let someOptional: Int? = 42
// Match using an enumeration case pattern
if case .Some(let x) = someOptional {
print(x)
}

// Match using an optional pattern
if case let x? = someOptional {
print(x)
}

The optional pattern provides a convenient way to iterate over an
array of optional values in a for-in statement, executing the body of
the loop only for non-nil elements.

let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values
for case let number? in arrayOfOptionalInts {
print("Found a \(number)")
}
// Found a 2
// Found a 3
// Found a 5

Should I use optional for properties of object models that will be parsed from JSON?

There are three ways you can go with this:

  1. Always send all the JSON data, and leave your properties non-optional.

  2. Make all the properties optional.

  3. Make all the properties non-optional, and write your own init(from:) method to assign default values to missing values, as described in this answer.

All of these should work; which one is "best" is opinion-based, and thus out of the scope of a Stack Overflow answer. Choose whichever one is most convenient for your particular need.

Why create Implicitly Unwrapped Optionals, since that implies you know there's a value?

Consider the case of an object that may have nil properties while it's being constructed and configured, but is immutable and non-nil afterwards (NSImage is often treated this way, though in its case it's still useful to mutate sometimes). Implicitly unwrapped optionals would clean up its code a good deal, with relatively low loss of safety (as long as the one guarantee held, it would be safe).

(Edit) To be clear though: regular optionals are nearly always preferable.

What is the purpose of protocols if all methods are optional?

Historically, for delegates and data sources in Cocoa, informal protocols were used. Informal protocol was implemented trough a category for NSObject class:

@interface NSObject (NSTableViewDelegate)

- (int)numberOfRowsInTableView:(NSTableView *)tableView;

// ...

@end

Later, optional methods in protocols were introduced. This change leads to better documenting of class responsibilities. If you see in code, that class conforms to NSTableViewDelegate, you suspect that somewhere exists a table view, that managed by instance of this class.

Also, this change leads to stronger checks in compile time. If programmer accidentally assign wrong object to delegate or dataSource properties, compiler will warn.

But your assumption is also correct. Optional methods are also suggestions for possible functionality.

How to loop through an optional collection

You have a couple of options. You can provide an empty array as the default value for elements in a for ... in loop:

let elements: [Element]?

for element in elements ?? [] {

}

Or you can use forEach and optional chaining on elements

elements?.forEach { element in

}

Bear in mind that optional collections are an anti-pattern in Swift. You can represent the lack of value using an empty collection, so wrapping a collection in an optional doesn't provide any extra value, while complicating the interface.

Advantages of KVC

The example that you have used is not the optimal case of using KVC or keyPath itself.

The true power, IMO, of keyPath is unleashed when you're working with protocols. Let me give you an example -

Say you have a protocol Identifiable which has only one member - id . Each type that conforms to this, must use id property which uniquely identifies it.

protocol Identifiable {
var id: Int { get }
}

struct Person: Identifiable {
var id: Int
var name: String
}

Here, a Person type having a member id doesn't sound too good. Consider another one -

struct Book: Identifiable {
var id: Int
var title: String
var author: String
}

Here, as well , a book can be uniquely identified by id but it doesn't sound good.

Here's where keyPath comes into play.

You can define member inside a protocol with some name and let the conforming types write their own name for that particular protocol member. The conforming types can map or tell the compiler that their particular member is the replacement of the one inside the protocol using keyPath.

protocol Identifiable {
associatedtype ID
static var id: WritableKeyPath<Self,ID> { get }
}

struct Person: Identifiable {
static var id = \Person.socialSecurityNumber

var socialSecurityNumber: Int
var name: String
}

struct Book: Identifiable {
static var id = \Book.isbn

var isbn: String
var title: String
var author: String
}

func printID<T: Identifiable>(aType: T) {
print(aType[keyPath: T.id])
}

printID(aType: Person(socialSecurityNumber: 1234, name: "Shubham Bakshi"))
printID(aType: Book(isbn: "qwertyui", title: "The Theory of Everything", author: "Stephen W. Hawking"))

As an added advantage , your conforming type can have id of any type , String, Int rather than Int only (as was in previous case)

If you want to give only a specific type to id, say Int only, you can replace the definition of our protocol with this one -

protocol Identifiable {
static var id: WritableKeyPath<Self,Int> { get }
}

This will force the conforming types to use Int for their id substitute.

Source - Swift KeyPath - Paul Hudson

Why are null checks bad / why would I want an optional to succeed if it's null?

Optionals provide clarity of type. An Int stores an actual value - always, whereas an Optional Int (i.e. Int?) stores either the value of an Int or a nil. This explicit "dual" type, so to speak, allows you to craft a simple function that can clearly declare what it will accept and return. If your function is to simply accept an actual Int and return an actual Int, then great.

func foo(x: Int) -> Int

But if your function wants to allow the return value to be nil, and the parameter to be nil, it must do so by explicitly making them optional:

func foo(x: Int?) -> Int?

In other languages such as Objective-C, objects can always be nil instead. Pointers in C++ can be nil, too. And so any object you receive in Obj-C or any pointer you receive in C++ ought to be checked for nil, just in case it's not what your code was expecting (a real object or pointer).

In Swift, the point is that you can declare object types that are non-optional, and thus whatever code you hand those objects to don't need to do any checks. They can just safely just use those objects and know they are non-null. That's part of the power of Swift optionals. And if you receive an optional, you must explicitly unpack it to its value when you need to access its value. Those who code in Swift try to always make their functions and properties non-optional whenever they can, unless they truly have a reason for making them optional.

The other beautiful thing about Swift optionals is all the built-in language constructs for dealing with optionals to make the code faster to write, cleaner to read, more compact... taking a lot of the hassle out of having to check and unpack an optional and the equivalent of that you'd have to do in other languages.

The nil-coalescing operator (??) is a great example, as are if-let and guard and many others.

In summary, optionals encourage and enforce more explicit type-checking in your code - type-checking that's done by by the compiler rather than at runtime. Sure you can write "clean" code in any language, but it's just a lot simpler and more automatic to do so in Swift, thanks in big part to its optionals (and its non-optionals too!).

Swift: guard let vs if let

if let and guard let serve similar, but distinct purposes.

The "else" case of guard must exit the current scope. Generally that means it must call return or abort the program. guard is used to provide early return without requiring nesting of the rest of the function.

if let nests its scope, and does not require anything special of it. It can return or not.

In general, if the if-let block was going to be the rest of the function, or its else clause would have a return or abort in it, then you should be using guard instead. This often means (at least in my experience), when in doubt, guard is usually the better answer. But there are plenty of situations where if let still is appropriate.



Related Topics



Leave a reply



Submit