1

I have this struct defined to be used in my ErrorLogger. However, some of the parameters such as Timestamp I'd like to get right away with the functions listed. How can I pass a default value instead of nil for the parameters: header, errorLocation, userId? In order to signal that a default value will be used if no other value is provided.

The idea is that if I pass some default value to these parameters then the struct functions would be called to get the values or else the values would be overwritten by whatever the user passes during initialization.

I am also getting the error:

self used before all stored properties are initialized

on this line:

self.errorLocation = getErrorLocation()

Initializing struct from viewController:

let logError = LogError(header: nil, errorLocation: nil, userID: nil, description: "An error occurred while verifying if the user profile exists", errorMessage: "\(error?.localizedDescription ?? "")")

struct in ErrorLogger:

struct LogError {
    var header: String
    var errorLocation: String
    var userID: String
    var description: String //describes the type of error
    var errorMessage: String //actual error returned by given function

    //TODO: add timestamp to lofError
    //TODO: add logEvent (enum: Error, Info) For example: data validation errors can be of event Info

    init(header: String?, errorLocation: String?, userID: String?, description: String, errorMessage: String) {
        if header != nil {
            self.header = header!
        } else {
            self.header = " ::Error::"
        }
        if errorLocation != nil  {
            self.errorLocation = errorLocation!
        } else {
            self.errorLocation = getErrorLocation()
        }
        if userID != nil {
            self.userID = userID!
        } else {
            self.userID = getUserID()
        }
        self.description = " ::description::" + description
        self.errorMessage = " ::errorMessage::" + errorMessage
    }

    func getUserID() -> String {
        var userUID: String = ""
        if Auth.auth().currentUser != nil {
            userUID = (Auth.auth().currentUser!.uid.isEmpty ? "" : Auth.auth().currentUser!.uid)
        } else {
            userUID = ""
        }
        let userUIDStr: String = " ::UserID::" + userUID
        return userUIDStr
    }

    func getErrorLocation() -> String {

        // Get class name
        let filePath: String = #file
        let components = filePath.components(separatedBy: "/")
        let className: String = components.isEmpty ? "" : components.last!

        // Get line number
        let line: Int = #line

        // Get column number
        let column: Int = #column

        // Get function name
        let funcName: String = #function

        let errorLocationStr: String  = " ::className::\(className)::lineNumber\(line)::columnNumber\(column)::funcName::\(funcName)"

        return errorLocationStr
    }

    func toString() -> String {
        let completeLogErrorMessageStr: String = header + errorLocation + userID + description + errorMessage
        return completeLogErrorMessageStr
    }
}

1 Answer 1

2

You have to give all of the properties a valid before you can call methods. I would refactor your init to something like this:

init(header: String?, errorLocation: String?, userID: String?, description: String, errorMessage: String) {
    self.header = header ?? " ::Error::"
    self.errorLocation = errorLocation ?? ""
    self.userID = userID ?? ""
    self.description = " ::description::" + description
    self.errorMessage = " ::errorMessage::" + errorMessage

    if errorLocation == nil  {
        self.errorLocation = getErrorLocation()
    }
    if userID == nil {
        self.userID = getUserID()
    }
}

That will fix the "self used before all stored properties are initialized" error.

To use default values, update the signature:

init(header: String? = nil, errorLocation: String? = nil, userID: String? = nil, description: String, errorMessage: String) {
    // nothing else changes
}

Now you can call the initializer without the nil parameters.

For example:

let logError = LogError(header: nil, errorLocation: nil, userID: nil, description: "An error occurred while verifying if the user profile exists", errorMessage: "\(error?.localizedDescription ?? "")")

becomes:

let logError = LogError(description: "An error occurred while verifying if the user profile exists", errorMessage: "\(error?.localizedDescription ?? "")")
Sign up to request clarification or add additional context in comments.

5 Comments

Perfect! that does fix the error. I would still like to find a way to avoid passing nil when I would like to just use the default. Is there some way to define a default value flag? A clean way to do this?
can you provide an example of how the call to init changes after your changes?
I see. For someone who hasn't seen ErrorLogger it won't be clear to them that the errorLocation, UserID , errorHeader, will be defined by default. Isn't this problematic? to hide those parameters?
Anyone that codes in Swift should know that parameters may have default parameter values and that if the default value is desired, the parameter can be left out of the function call. There's nothing special with ErrorLogger in this regard. Developers will see the init has parameters with default values, especially if it is documented properly. If the developer doesn't notice, it's no big deal if they explicitly pass the parameter with a value that happens to be the default. It's just a little extra typing that wasn't really needed.
Thank you so much for your answer. This was very helpful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.