Calling `max` function when it's hidden by static member

Context: I want to be able to assign/init UInt32 values with four-character strings as in the file type/creator codes of the old days, or magic numbers used in some file formats.

I developed a function that works fine. It calls the max function to make sure to use at most 4 characters from the input string:

    let length = max(asciiRepresentation.count, 4)

But this lines doesn't compile when put in an extension to UInt32:

extension UInt32 {
    init(asciiRepresentation: String) {
        let length = max(asciiRepresentation.count, 4)
        
        var result: UInt32 = 0
        var index = asciiRepresentation.startIndex
        for _ in 0 ..< length {
            let char = asciiRepresentation[index]
            let byte: UInt32 = UInt32(char.asciiValue ?? 0)
            result = result << 8 + byte
            index = asciiRepresentation.index(after:index)
        }
        self = result
    }
}

In my playground (Xcode 11.2.1):

error: CodingDecoding.playground:46:22: error: static member 'max' cannot be used on instance of type 'UInt32'
        let length = max(asciiRepresentation.count, 4)
                     ^~~
                     Self.

My understanding is that the global max function is hidden by the UInt32.max static member that represents the largest possible UInt32 number.

1- Is that correct?
2- Is there a way to call the global max function anyway?

Of course, I can work around that with an if statement easily.

Also feel free to comment on my code: I am a noob, and I guess this code could be written better. I would appreciate the learning experience.

You can disambiguate in such cases using the name of the module. The standard library is named Swift; therefore: Swift.max.

3 Likes

perfect. thanks

1 Like

Are you sure you want to use max here? If asciiRepresentation has 5 characters, length will be 5 and you’ll index off the end of your string.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

ps This is how I’d write your extension:

init?(fourCharCode: String) {
    guard
        let macRoman = fourCharCode.data(using: .macOSRoman),
        macRoman.count == 4
    else {
        return nil
    }
    self = macRoman.reduce(0) { soFar, byte in
        return soFar << 8 | UInt32(byte)
    }
}

Traditional Mac OS four character codes supported non-ASCII values, interpreted as MacRoman.

3 Likes

Duh, of course not max. Typical brain fart (for me). I intended min...

But thank you so much for your rewrite. It brings much value on several levels, for me as a beginner:

  • tip about MacRoman (though I am not sure how it applies to a file format such as .MP4)
  • failable Initializer
  • guard
  • the use of reduce
  • even the use of | instead of +

Note about String.data(using:):

I had a hard time understanding where this is coming from. This function is nowhere to be found in the Xcode documentation for String. It has a quick help, but that's it. Jumping to definition leads to a page with only imports. Jumping to definition of String leads to a very long file, where this function is missing.

Only googling for it, leads to NSString.data

So this function requires bridging String to NSString. Therefore it requires Foundation. Right?

Overall, I found Xcode documentation difficult to use, of rather little help for reference purposes, and very spotty.

of course, searching for "data" anywhere is not very specific and returns many false positives...

Or perhaps I missed something. Is there documentation on how to use Xcode documentation?

though I am not sure how it applies to a file format such as .MP4

That’s an interesting question. MPEG-4’s container format is a ‘child’ of QuickTime, and QuickTime definitely used MacRoman for this. I’m not sure whether they removed that dependency as part of the standardisation process. It’s possible that they didn’t address it at all, and just standardised on the UInt32 values. The only way to know for sure is to look at the MPEG-4 standard itself.

Note about String.data(using:):

I had a hard time understanding where this is coming from.

Indeed. The Quick Help usually has a link to the documentation but that’s not present in this case. I suspect this is because the symbol is implemented in Foundation’s Swift overlay. Regardless, the fact that Xcode won’t take you to the documentation is bugworthy IMO. Please file a bug about that (and post your bug number, just for the record).

… it requires Foundation. Right?

Yes.

So this function requires bridging String to NSString.

If I’m right, and this is part of Foundation’s Swift overlay, the bridging situation is nuanced.

Personally, I don’t lose a lot of sleep worrying about this sort of bridging. If something is slow and I’ve profiled it and the profile shows that bridging is part of the problem, I look into it. Otherwise I just accept it as a part of life.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

Thanks again. Feedback posted using Feedback Assistant app, with number FB7440716.