Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

0002378

Suspended
Original poster
May 28, 2017
675
671
There is some app configuration code (e.g. ensuring that all logging is done to a specific file) that needs to run before any other app code is run. It needs to be the very first piece of my code that is run.

Now, it is not enough for me to put this in AppDelegate.applicationDidFinishLaunching() because certain other app code (object init()) runs before the app finishes launching. In other words, didFinishLaunching() is too late in the app lifecycle for my purposes.

So, basically, I'm trying to find a way to hook into the application life cycle stage that occurs before the app launches (and any other app code runs). I have looked at and tried implementing all NSApplicationDelegate lifecycle methods, with no luck. Ideally, there would be an NSApplicationDelegate method like applicationWillLaunch(), but there isn't one (there is applicationWillFinishLaunching, but that is too late).

I have achieved this, tentatively, by subclassing NSApplication and overriding its init() method. This works, but is there a better way ? Is there a method either in NSApplication or NSApplicationDelegate that I can implement/override that will execute before the app begins launching ?

Thanks !
 
Depending on what you are trying to do, there are a couple ways:

1) Lazy initialization. Have the action of getting "the current logger" object initialize the logger if there isn't already a current logger. This can use the singleton pattern, and while not ideal, at least leaves all the code localized to the logger component itself. At least as a first pass without knowing more details, this is my first choice.

2) You can either override NSApplication's init like you have, or NSApplication's run. This is my second choice, as it does the job, but tends to violate locality.

3) Swift supports code that lies outside of a function (it's how Playgrounds work). It should get executed prior to NSApp.run() is executed by Apple. This is my last choice, since this is relying on implicit behavior in Swift, and while it should work, it tends to make the code a little less clear.

In general, whenever I've got global state like loggers, I tend to fall onto the Singleton pattern with lazy initialization and then refactor as needed to see if I can clean it up after that.
 
  • Like
Reactions: 0002378
Depending on what you are trying to do, there are a couple ways:

1) Lazy initialization. Have the action of getting "the current logger" object initialize the logger if there isn't already a current logger. This can use the singleton pattern, and while not ideal, at least leaves all the code localized to the logger component itself. At least as a first pass without knowing more details, this is my first choice.

2) You can either override NSApplication's init like you have, or NSApplication's run. This is my second choice, as it does the job, but tends to violate locality.

3) Swift supports code that lies outside of a function (it's how Playgrounds work). It should get executed prior to NSApp.run() is executed by Apple. This is my last choice, since this is relying on implicit behavior in Swift, and while it should work, it tends to make the code a little less clear.

In general, whenever I've got global state like loggers, I tend to fall onto the Singleton pattern with lazy initialization and then refactor as needed to see if I can clean it up after that.

Thanks.

With reference to your point #1, that sounds a lot like how logging is done in Java, but in my Swift code, all I'm doing is:

Code:
NSLog(myLogMessage)

So, I'm not really initializing the logger myself.

My configuration code, which needs to run first, is the following:

Code:
// Make sure all logging is done to the app's log file
    private func configureLogging() {
        
        let allPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentsDirectory = allPaths.first!
        let pathForLog = documentsDirectory + ("/" + AppConstants.logFileName)
        
        freopen(pathForLog.cString(using: String.Encoding.ascii)!, "a+", stderr)
    }

I ended up putting this in AppDelegate.init().

So, I guess I'm saying I didn't understand your #1 suggestion. I'm not doing any logger initialization; I'm simply using NSLog() whenever I need to log something.

BTW, I'm a huge fan of the Singleton pattern and use it a lot.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.