Proxy Wrap the iOS 14 Logger class

1.6k views Asked by At

I have an app that is available on iOS 12. In the past I've used the excellent CocoaLumberjack, but I'd like to move to the native Logger class.

I'm happy for logs not to be collected for older iOS version, but I would like the app to run there.

Is there a good way to avoid #available calls everywhere? Generally I like #available, but for logging it's unwieldy

if #available(iOS 14.0, *) {
     logger.info("Setting person from \(self.person, privacy: .public) to \(newValue, privacy: .public)")
}

If I wrap the Logger class, I can't get it to compile, because the logging system has compile time constraints:

import os

public class Logger {

    @available(iOS 14.0, *)
    static let logger = os.Logger(subsystem: "com.progress", category: "ProgressKit")

    public static func debug(_ input: String) {
        if #available(iOS 14.0, *) {
            logger.debug(OSLogMessage(stringLiteral: input)) //  Fails: Argument must be a string interpolation
        } else {
            print(input)
        }
    }
}

Is there a way to use the new system do this without having #available statements everywhere??

2

There are 2 answers

0
Alexander On BEST ANSWER

There's literally a special case in the compiler to ensure that OSLogMessage can only be constructed from a string literal.

https://github.com/apple/swift/pull/31109/files

That means there's no way to wrap it, and you're forced to either:

  1. Expedite the obsolesce of old devices by dropping support for everything before Big Sur, and use the shiny new APIs directly, or
  2. Don't use it, and use the os_log APIs, ugly as they may be.
4
matt On

The problem is not availability. The problem is that, as you’ve discovered, the argument to a Logger log command is not a String. It is a private class that is representable by a literal string. Thus you cannot pass a String into a wrapper and relay it to the Logger. You must call the Logger method directly with a literal string.

However, Logger is itself just an ingenious wrapper for os_log which does take a String. So you could use that instead. Or you could, as you suggest, just use availability and other backward compatibility techniques everywhere.