Advance json parsing pt2


My notes on “Throw Oriented Code”, great for parsing spaghetti JSON ⚠️️ This article is in beta at the moment ⚠️️

Example showing how you can create Nested Error types

And handle these accordingly from the first try call in the “try-hierarchy”. In theory, you could structure entire Code frameworks with nested Try / Error code.

/*
 * TODO: Add description switch in the error types
 * TODO: Add relevant JSON case
 */
struct A {
   let b: B?
   struct B{
       let c: C?
       struct C {
           let value:String?
       }
       func getC() throws -> C {
           guard let c = self.c else {
               throw OuterError.InnerError(error:.RegularInnerError)//👈 this is 2 steps deep in the call hierarchy
           }
           return c
       }
   }
}

func test(_ a:A) throws -> String {
   guard let b = a.b else {
       throw OuterError.RegularError
   }
   let c = try b.getC()
    _ = c
   ///Handle all the other,
   return "success"
}



enum OuterError: Swift.Error { //, CustomStringConvertible
   enum InnerErrorType:Swift.Error {
      case RegularInnerError
      case OtherInnerError
   }
   case RegularError
   case UnexpectedError(message: String)
   case InnerError(error: InnerErrorType)
}

func login() {
   Swift.print("login")
   do {
       let b = A.B(c:nil)
       let a = A(b:b)
       let result = try test(a)
       print("result \(result)")
   } catch OuterError.RegularError {
       print("regular err")
   } catch OuterError.InnerError(let innerError) {
       print("inner err")
       switch innerError {
       case .RegularInnerError:
           print("regular inner err 🎉 i'm deep in the hierarchy 💪")
       case .OtherInnerError:
           print("other err")
       }
   } catch {//all other cases
       print(error)
   }
}

login()

// Output:
// login
// inner err
// regular inner err 🎉 i'm deep in the hierarchy 💪

Throw oriented code should be favoured to nil coalesing code:

/*
Suppose a function myFunction is supposed to return a String, however, at some point it can run into an error. A common approach is to have this function return an optional String? where we return nil if something went wrong.

Example:
*/   
func readFile(named filename: String) -> String? {
    guard let file = openFile(named: filename) else { return nil }
    let fileContents = file.read()
    file.close()
    return fileContents
}

func printSomeFile() {
    let filename = "somefile.txt"
    guard let fileContents = readFile(named: filename) else {
        print("Unable to open file \(filename).")
        return
    }
    print(fileContents)
}
/*
Instead, we should be using Swift's try/catch behavior when it is appropriate to know the reason for the failure.
You can use a struct such as the following:
*/

struct Error: Swift.Error {
    public let file: StaticString
    public let function: StaticString
    public let line: UInt
    public let message: String

    public init(message: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
        self.file = file
        self.function = function
        self.line = line
        self.message = message
    }
}
Example usage:

func readFile(named filename: String) throws -> String {
    guard let file = openFile(named: filename) else {
        throw Error(message: "Unable to open file named \(filename).")
    }

    let fileContents = file.read()
    file.close()
    return fileContents
}

func printSomeFile() {
    do {
        let fileContents = try readFile(named: filename)
        print(fileContents)
    } catch {
        print(error)
    }
}

References:

  • decodable and subclases: insp: https://medium.com/tsengineering/swift-4-0-codable-decoding-subclasses-inherited-classes-heterogeneous-arrays-ee3e180eb556