0

I have a closure that adds zero to left part of a number in a condition and return String. The number type is Int but it must work if the Type is optional Int.

I ended up to repeat my code, my goal is stop repeating my code in the way that my code works for Int and Int?

extension Int {
    var addLeftZero: String { addLeftZeroClosure(self) }
}


extension Optional where Wrapped == Int {
    var addLeftZero: String {

        if let unwrappedValue: Wrapped = self { return addLeftZeroClosure(unwrappedValue) }
        else { return "00" }
        
    }
}



let addLeftZeroClosure: (Int) -> String = { value in

    if (value >= 10) { return String(describing: value) }
    else { return "0" + String(describing: value) }
    
}
ios coder
  • 1
  • 4
  • 31
  • 91
  • 1
    I think you may just be looking for [Leading zeros for Int in Swift](https://stackoverflow.com/q/25566581/9607863). – George Dec 25 '21 at 22:57
  • @George: It is an example to show the issue, the goal is not the leftZero. but thanks for link. My goal is stop repeating my code in the way that my code works for Int and Int? – ios coder Dec 25 '21 at 22:58
  • @swiftPunk I know it is not related to your post [strZero](https://stackoverflow.com/a/66231042/2303865). You can also add a default value of 2 for maxLength. – Leo Dabus Dec 27 '21 at 02:48

1 Answers1

1

Read both parts first, before picking solution to use.


You could do something like this:

extension Int {
    var addLeftZero: String {
        String(format: "%02d", self)
    }
}

extension Optional where Wrapped == Int {
    var addLeftZero: String {
        self?.addLeftZero ?? "00"
    }
}

Example usage:

let a: Int = 1
let b: Int? = 2
let c: Int? = nil

print(a.addLeftZero) // Prints: 01
print(b.addLeftZero) // Prints: 02
print(c.addLeftZero) // Prints: 00

Although Xcode code-completion will automatically do a?.addLeftZero instead of a.addLeftZero.


A (possibly) better way would be to have this abstracted away in your own custom type:

struct PaddedNumber: ExpressibleByIntegerLiteral, ExpressibleByNilLiteral {
    let value: Int?

    var string: String {
        if let value = value {
            return String(format: "%02d", value)
        } else {
            return "00"
        }
    }

    init(_ value: Int?) {
        self.value = value
    }

    init(integerLiteral value: IntegerLiteralType) {
        self.value = value
    }

    init(nilLiteral: ()) {
        value = nil
    }
}

Example usage:

let a: PaddedNumber = 1
let b: PaddedNumber = 2
let c: PaddedNumber = nil

print(a.string) // Prints: 01
print(b.string) // Prints: 02
print(c.string) // Prints: 00
George
  • 25,988
  • 10
  • 79
  • 133
  • It looks must better and cleaner code, but you also, have to make 2 block of codes once for non-optional and once for optional. My goal is having just one block of code for both optional/non-optional. Do you think it is possible? – ios coder Dec 25 '21 at 23:06
  • 2
    @swiftPunk No because `Int` and `Optional` are different types. If it were me, I would either: 1) Handle the optional from the user end, such as `a?.addLeftZero ?? "00"`, however I see how that may not be wanted because then that is repeated. 2) I would do this instead. Have whatever this `Int` is for encased in another type, where this property is an `Int?`. Then you can handle what I mentioned in option #1, hidden from the user (the programmer). – George Dec 25 '21 at 23:08
  • I was hopping to reach for just one block of code for both optional/non-optional! But you are right! they are 2 different types! and they need one block of code for each. – ios coder Dec 25 '21 at 23:11
  • Extending optional for something like this should be used sparingly. It pretty much defeats the point of optional (to let a caller define how to handle nil for themselves, instead of trying to make senseless guesses like “nil is 0” on their behalf) – Alexander Dec 26 '21 at 01:15
  • 1
    @Alexander I agree, that’s why I recommend they have this as part of a suitable struct so this can be abstracted from the user to deal with. – George Dec 26 '21 at 01:18
  • This is definitely not a good solution. Anyone reviewing this code wouldn't know that objects b and c are optional. – Leo Dabus Dec 27 '21 at 01:56
  • @swiftPunk I wouldn't go this way. Much better to simply do `b?.addLeftZero ?? "00"` when the object is optional. Anyone reading your code will know exactly what is going on just by looking at your code. – Leo Dabus Dec 27 '21 at 01:59
  • @LeoDabus: I really do not understand why it is bad idea?! My goal is that I be able to use `addLeftZero` on `Int` or `Int?`. So with that goal and those extensions, the user knows what would the result of using on `Int` or `Int?`. Where is the issue here? Do not forget that the goal is apply and use! – ios coder Dec 27 '21 at 02:22
  • @swiftPunk Just abstract it away from the user, in a different type. It could be initialised with the Int, then you can get the String version. I’ll add to my answer tomorrow – George Dec 27 '21 at 02:24
  • @George: I really see no issue here! The goal is snap apply and use! – ios coder Dec 27 '21 at 02:25
  • @swiftPunk I just left my opinion. You are free to agree or not. – Leo Dabus Dec 27 '21 at 02:34
  • @LeoDabus: thanks, I know you were about to helping, I just said what was my goal, you know when I have to apply that `addLeftZero` to `Int?` I am sick and tired to deal safe unwrapping or given default value for each use case, therefor I use those 2 extensions and make myself free. – ios coder Dec 27 '21 at 02:48
  • I would make it non optional and initialize it with zero – Leo Dabus Dec 27 '21 at 02:49
  • Yeah, that sound's for me extra line of coding. :) but thanks. – ios coder Dec 27 '21 at 02:52
  • 1
    @swiftPunk Just added another method which you might prefer – George Dec 27 '21 at 18:08
  • @George: thanks, I never worked with Expressible, I must study it first. – ios coder Dec 27 '21 at 18:10
  • @swiftPunk Yeah there's loads of `ExpressibleBy*Literal` protocols. It enables you to use literals instead, shortening `PaddedNumber(1)` to just `1` (given you explicitly state the type still). – George Dec 27 '21 at 18:12
  • @George: I looked to your codes for second way real quick, But it has a down side! For example one of apple api returns `Int?`, How can I be able use second method there? – ios coder Dec 27 '21 at 18:17
  • @swiftPunk Wrap it in `PaddedNumber`, there is an init there taking in `Int?` – George Dec 27 '21 at 18:18
  • Okay, that is kind of need to have both options of string and value! good job. @George – ios coder Dec 27 '21 at 18:22