Created
May 12, 2017 06:05
-
-
Save davidkneely/e3563793491a5eabec887dd95b440952 to your computer and use it in GitHub Desktop.
Hawaii iOS Developer Meetup 5/11/2017 - Optionals, Results, and Errors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import UIKit | |
// Ch 20 | |
// Big Nerd Ranch Swift Programming Book | |
// 2017_5_12 | |
/* | |
NOTE: Research reduce to start next meetup with answer to Joanne's question. | |
NOTE: Start with Result, Error return type with throws. | |
Recap before we begin tonight: Questions from last week re Closures? | |
Go over one-liner from last time. | |
Tonight's Topic: Result Errors vs. Optionals | |
------------------------------------------------------------- | |
BNR: Diversion into types of errors that could occur | |
Two kinds of errors | |
Reconverable Errors | |
Trying to open a file that does not exist. | |
Trying to communicate with a server that is down. | |
Trying to communicate when a device does not have a connection. | |
Non-Recoverable Errors | |
Force-unwrapping an optional that contains a nil | |
Trying to access an element past the end of an array | |
Causes program to "trap" (not elegant, scary to user) | |
*/ | |
/* | |
WHAT IS AN OPTIONAL? | |
Optionals are how nil is allows in the Swift Programming language. In Objective C, we are only able to use nil on objects. With Swift, we are able to set any type to nil with the user of optionals. e.g. Int, String, Enum, Classes, etc. | |
Optional is an enumeration that wraps around any type to allow for either some value or none value. | |
Optionals cannot be used directly. They either need to be bound or force unwrapped. | |
optional binding: | |
1) if let | |
func doSomething() { | |
let optional: String? | |
optional = "hello david" | |
if let unwrappedOptional = optional { | |
print(unwrappedOptional) | |
} | |
} | |
2) guard let | |
guard let unwrappedOptional = optional else { | |
return | |
} | |
print(unwrappedOptional) | |
// NOTE: Add in a guard let // keep in mind that this is local | |
print(unwrappedOptional) | |
3) force unwrap | |
var optional: String? | |
optional! // bad practice! | |
WHAT IS A RESULT ERROR? | |
Result type is an enum with two cases as well: | |
failure with associated value | |
success with an associated value | |
enum Result<A> { | |
case failure(Error) // constrains associated value with Error protocol | |
case success<A> | |
} | |
// sample call that can fail | |
func contentsOrNil(ofFile filename: String) -> String? | |
// if this fails we have no idea why, we just get nil | |
// so let's set up an Error enum where we can specify the possible Errors | |
enum FileError: Swift.Error { | |
case fileDoesNotExist | |
case noPermission | |
} | |
// sample code to flex the success and error possibilities | |
func contents(ofFile filename: String) -> Result<String> // no longer returning optional | |
let result = contents(ofFile: "input.txt") | |
switch result { | |
case let .success(contents): | |
print(contents) | |
case let .failure(error): | |
if let fileError = error as? FileError, fileError == .fileDoesNotExist { | |
print("File Not Found.") | |
} else { | |
// handle the error | |
} | |
} | |
// Next, instead of giving a function a Result return type to indicate that it can fail, we use the keyword "throws" | |
// Result refers to types | |
// Throws refers to functions | |
// updating our result return | |
func contents(ofFile filename: String) throws -> String | |
do{ | |
let result = try contents(ofFile: "input.txt") | |
print(result) | |
} catch FileError.fileDoesNotExist { // this one will catch the error that the file is not found | |
print("File Not Found") | |
} catch { // this one will catch any error that comes up | |
print(error) | |
// handle any other error | |
} | |
do try catch | |
*/ | |
// BNR - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
// Create 2 phase compiler | |
// Declaring the token type | |
enum Token { | |
case number(Int) | |
case plus | |
} | |
// Create Lexer | |
class Lexer { | |
enum Error: Swift.Error { | |
case invalidCharacter(Character) | |
} | |
let input: String.CharacterView | |
var position: String.CharacterView.Index | |
init(input: String) { | |
self.input = input.characters | |
self.position = self.input.startIndex | |
} | |
func peek() -> Character? { | |
guard position < input.startIndex else { | |
return nil | |
} | |
return input[position] | |
} | |
func advance() { | |
assert(position < input.endIndex, "Cannot advance past endIndex!") | |
position = input.index(after: position) | |
} | |
func getNumber() -> Int { | |
var value = 0 | |
while let nextCharacter = peek() { | |
switch nextCharacter { | |
case "0" ... "9": | |
// Another digit - add it to the value | |
let digitValue = Int(String(nextCharacter))! | |
value = 10 * value + digitValue | |
advance() | |
default: | |
// A nondigit - go back to regular lexing | |
return value | |
} | |
} | |
return value | |
} | |
func lex() throws -> [Token] { | |
var tokens = [Token]() | |
while let nextCharacter = peek() { | |
switch nextCharacter { | |
case "0" ... "9": | |
// Start of a number - need to grab the rest | |
let value = getNumber() | |
tokens.append(.number(value)) | |
case "+": | |
tokens.append(.plus) | |
advance() | |
case " ": | |
// Just advance to ignore spaces | |
advance() | |
default: | |
// Something unexpected - need to send back error | |
throw Lexer.Error.invalidCharacter(nextCharacter) | |
} | |
} | |
return tokens | |
} | |
} | |
// Create the Parser | |
class Parser { | |
enum Error: Swift.Error { | |
case unexpectedEndOfInput | |
case invalidToken(Token) | |
} | |
let tokens: [Token] | |
var position = 0 | |
init(tokens: [Token]) { | |
self.tokens = tokens | |
} | |
func getNextToken() -> Token? { | |
guard position < tokens.count else { | |
return nil | |
} | |
let token = tokens[position] | |
position += 1 | |
return token | |
} | |
func getNumber() throws -> Int { | |
guard let token = getNextToken() else { | |
throw Parser.Error.unexpectedEndOfInput | |
} | |
switch token { | |
case .number(let value): | |
return value | |
case .plus: | |
throw Parser.Error.invalidToken(token) | |
} | |
} | |
func parse() throws -> Int { | |
// Require number first | |
var value = try getNumber() | |
while let token = getNextToken() { | |
switch token { | |
// Getting a plus after a number is legal | |
case .plus: | |
// After plus, we must get a another number | |
let nextNumber = try getNumber() | |
value += nextNumber | |
case .number: | |
throw Parser.Error.invalidToken(token) | |
} | |
} | |
return value | |
} | |
} | |
func evaluate(_ input: String) { | |
print("Evaluating: \(input)") | |
let lexer = Lexer(input: input) | |
do { | |
let tokens = try lexer.lex() | |
print("Lexer output: \(tokens)") | |
let parser = Parser(tokens: tokens) | |
let result = try parser.parse() | |
print("Parser output: \(result)") | |
} catch Lexer.Error.invalidCharacter(let character) { | |
print("Input contained an invalid character: \(character)") | |
} catch Parser.Error.unexpectedEndOfInput { | |
print("Unexpected end of input during parsing") | |
} catch Parser.Error.invalidToken(let token) { | |
print("Invalid token during parsing: \(token)") | |
} catch { | |
print("An error occured: \(error)") | |
} | |
} | |
evaluate("10 + 3 + 5") | |
//evaluate("1 + 2 + abcdefg") | |
// BRN - - - - - - - - - - - - - - - - - - - - -- -- - - - - - -- - - - - - | |
// Handling Errors by Sticking your Head in the Sand | |
func evaluate001(_ input: String) { | |
print("Evaluating: \(input)") | |
let lexer = Lexer(input: input) | |
//let tokens = try! lexer.lex() | |
// guard let tokens = try? lexer.lex() else { | |
// | |
// print("Lexing failed, but I don't know why") | |
// | |
// return | |
// } | |
do { | |
let tokens = try lexer.lex() | |
print("Lexer output: \(tokens)") | |
let parser = Parser(tokens: tokens) | |
let result = try parser.parse() | |
print("Parser output: \(result)") | |
} catch Lexer.Error.invalidCharacter(let character) { | |
print("Input contained an invalid character: \(character)") | |
} catch Parser.Error.unexpectedEndOfInput { | |
print("Unexpected end of input during parsing") | |
} catch Parser.Error.invalidToken(let token) { | |
print("Invalid token during parsing: \(token)") | |
} catch { | |
print("An error occured: \(error)") | |
} | |
} | |
evaluate001("10 + 3 + 5") | |
evaluate001("1 + 2 + abcdefg") | |
// Mob Programming Challenge | |
// Create a function with an array of ints | |
// Add a throw into that function | |
// Add a do try catch block to it | |
// Catch all errors | |
// Create an enumeration for Errors | |
// Update the function to catch an index out of bounds error: start or end or both | |
print("- - - - - ") | |
func doSomething() { | |
//let optional: String? // with let this needs to be initialized with value | |
var optional001: String? | |
optional001 = "hello david" | |
if let unwrappedOptional = optional001 { | |
print(unwrappedOptional) | |
} | |
} | |
doSomething() | |
func doAnotherThing() { | |
let optional001: String? = "Hello, Joanne" | |
guard let unwrappedOptional = optional001 else { | |
return | |
} | |
print(unwrappedOptional) | |
} | |
doAnotherThing() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment