Search code examples
swift

Data structure for flagging input error in swifty way


I am writing a tool which accepts certain set of inputs, including some files, parses them, does error checking and if there are no errors then proceeds to do the actual work.

There are about 20 predefined errors with specific error strings in specification for which the tool does input verification.

I can do these error checks without using any data structure (I am new to swift land). But I am intrigued by use of enums and exploring if they make sense in this case.

So, I am planning to write something as follows:

enum ErrorCheck {
     case incorrectFilePathProvided (code: Int, description: String)
     case inputFileIsMissingVersionNumber (code: Int, description: String)
     ....
}

let errorIncorrectFilePathProvided = ErrorCheck.inputFileNotProvided(1, "User has provided incorrect input file")
....

static func validateInputParams(param1: String, param2: NSDictionary) -> NSArray {
     var result: NSMutableArray = []
     if !param1.hasPrefix("/Application") {
        result.add([ErrorCheck.errorIncorrectInputFilePathProvided.code : ErrorCheck.errorIncorrectInputFilePathProvided.description])
     }
     
     if !param2["specificKey"] {
             result.add([ErrorCheck.errorIncorrectInputFilePathProvided.code : ErrorCheck.errorIncorrectInputFilePathProvided.description])
      }
} 

Does this approach makes sense or is there more swifty way to do this?


Solution

  • I think you might have misunderstood how enums work, because if each every has a unique code and message, code and message should not be the associated values of each case.

    If you just want an error type that consists of a code and a message, just write a struct with those properties, and static lets for each error.

    struct ErrorCheck {
        let code: Int
        let message: String
        
        private init(code: Int, message: String) {
            self.code = code
            self.message = message
        }
        
        static let incorrectFilePathProvided = ErrorCheck(code: 1, message: "User has provided incorrect input file")
        static let inputFileIsMissingVersionNumber = ErrorCheck(code: 2, message: "Input file is missing a version number")
    }
    

    You can also use an enum, but without associated values. I personally find the switches a bit annoying to write though.

    enum ErrorCheck {
        case incorrectFilePathProvided
        case inputFileIsMissingVersionNumber
        
        var code: Int {
            switch self {
            case .incorrectFilePathProvided:
                1
            case .inputFileIsMissingVersionNumber:
                2
            }
        }
        var message: String {
            switch self {
            case .incorrectFilePathProvided:
                "User has provided incorrect input file"
            case .inputFileIsMissingVersionNumber:
                "Input file is missing a version number"
            }
        }
    }
    

    For validateInputParams, it should not take a NSDictionary and return an NSArray. You should avoid using Objective-C collection types when writing Swift.

    // param2 can be [AnyHashable: Any] if you are not sure about the key type
    func validateInputParams(param1: String, param2: [String: Any]) -> [ErrorCheck] {
        var errors = [ErrorCheck]()
        if !param1.hasPrefix("/Application") {
            errors.append(.incorrectFilePathProvided)
        }
        
        if param2.keys.contains("version") {
            errors.append(.inputFileIsMissingVersionNumber)
        }
        
        return errors
    }
    

    I would also suggest conforming to Error:

    extension ErrorCheck: Error {}
    

    This isn't necessary for validateInputParams since it returns the errors instead of throwing them, but who knows what you might need in the future?

    Conforming to LocalizedError is also an option if the message is supposed to be displayed to the end user. In this case you should rename message to errorDescription. Other APIs that needs to display your error will use that property.