Skip to content

Instantly share code, notes, and snippets.

@davidkneely
Created May 12, 2017 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidkneely/e3563793491a5eabec887dd95b440952 to your computer and use it in GitHub Desktop.
Save davidkneely/e3563793491a5eabec887dd95b440952 to your computer and use it in GitHub Desktop.
Hawaii iOS Developer Meetup 5/11/2017 - Optionals, Results, and Errors
//: 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