Should I use enums or class hierarchy for errors in swift? -
in swift 2 type conforms errortype protocol can thrown , caught. me, makes sense have common error hierarchy , re-use in multiple places. but, apple documentation seems push developers using enums handling errors.
for example, hierarchy let me catch , process common validationerror without knowing it's exact sub-type. allow different parts of application extend validationerror.
myapperror validationerror invalidpatherror wrongfiletypeerror
mixing different styles of defining errors not idea. so, should model error handling around class hierarchies or enums?
tl;dr
enums shorter, faster write, easier understand potential errors, , compiler make sure catch of errors.
the full story
errortype
empty protocol (there hidden properties _code : int
, _domain : string
apple takes care of that).
i quote swift programming guide (link)
swift enumerations particularly suited modeling group of related error conditions, associated values allowing additional information nature of error communicated.
to elaborate on that, enums allow express can go wrong. when doing error handling, have specific conditions can fail (swift pushes in direction optionals , type safety). because errors distinct cases shouldn't need many layers of inheritance (if add details answer please). errors can represented enums. using large inheritance hierarchy overly complicated.
say want every error have message can displayed user. instead of subclass use protocol.
protocol myapperror : errortype { var message: string { } }
taking given example little further represent validationerror
enum (as there many validation errors).
enum validationerror : myapperror { case invalidpatherror (string) case wrongfiletypeerror (expectedfiletype: string) var message: string { switch self { case .invalidpatherror(let invalidpath): return "\(invalidpath) invalid path" case .wrongfiletypeerror(let expectedfiletype): return "expected type of \(expectedfiletype)" } } }
_
func myfilefunction(path: string) throws { guard let url = nsurl(string: path) else { throw validationerror.invalidpatherror(path) } guard let data = nsdictionary(contentsofurl: url) else { throw validationerror.wrongfiletypeerror(expectedfiletype: ".plist") } print(data) } { try myfilefunction("hi.jpg") } catch validationerror.invalidpatherror(let path) { print("darn, had bad path \(path)") } catch validationerror.wrongfiletypeerror(let expectedtype) { print("darn, expected type \(expectedtype)") } catch (let error myapperror) { print("darn, old error \(error.message)") }
the compiler knows function throw validationerrors warns if try , catch myapperror
. here another/better way it.
do { try myfilefunction("hi.jpg") } catch (let error validationerror) { switch error { case .wrongfiletypeerror(let expectedtype): print("darn, expected type \(expectedtype)") case .invalidpatherror(let path): print("darn, had bad path \(path)") } }
lets compare oo class/inheritance
class myapperror : customstringconvertible { let message: string init(message: string) { self.message = message } var description: string { return message } } class validationerror : myapperror { } class invalidpatherror : validationerror { let path: string init(message: string, path: string) { self.path = path super.init(message: message) } override var description: string { return "\(path) invalid path" } } class wrongfiletypeerror : validationerror { let expectedfiletype: string init(message: string, expectedfiletype: string) { self.expectedfiletype = expectedfiletype super.init(message: message) } override var description: string { return "expected type of \(expectedfiletype)" } }
_
func myfilefunction(path: string) throws { guard let url = nsurl(string: path) else { throw invalidpatherror(path: path) } guard let data = nsdictionary(contentsofurl: url) else { throw wrongfiletypeerror(expectedfiletype: ".plist") } print(data) } { try myfilefunction("hi.jpg") } catch (let error invalidpatherror) { print("darn, had bad path \(error.path)") } catch (let error wrongfiletypeerror) { print("darn, expected type \(error.expectedfiletype)") } catch (let error myapperror) { print("some other error") }
as can see, works creating error classes adds lot of baggage. don't automatic namespacing validationerror.wrongfiletypeerror
or let error validationerror
. reading class knows invalidpatherror
, wrongfiletypeerror
caught. contrast enum version know validationerror
instances being caught , compiler tells this.
Comments
Post a Comment