11

I've come across an issue where I cannot access my app's main window, as it returns nil.

let window = NSApplication.sharedApplication().mainWindow

I've found similar questions:

How to get Main Window (App Delegate) from other class (subclass of NSViewController)?

But doing:

let window = (NSApplication.sharedApplication() as! NSArray).objectAtIndex(0)

Doesn't seem to work either.

Do I have to mess around in Storyboard?

Thanks in advance.

Update:

I'm actually trying to port something from Objective-C.

    NSWindow *mainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
    NSLog(@"%@", mainWindow.contentViewController);

This returns a proper value, when put in the viewDidLoad() block in a NSViewController, so I'm guessing there is something wrong with NSApplication.sharedApplication().

Community
  • 1
  • 1
Naoto Ida
  • 1,275
  • 1
  • 14
  • 29

6 Answers6

9

It also can return nil if the main app window is not active on macOS. For instance I ran into this when I was making a drag and drop file uploader. If the window was not in the front (on the operating system) it would return nil. The following line of code will activate your app (bring to front)

NSApp.activateIgnoringOtherApps(true)

I also needed a timer to delay my call of mainWindow in my case.

quemeful
  • 9,542
  • 4
  • 60
  • 69
2

You could do this instead:

   func applicationDidBecomeActive(notification: NSNotification) {

        NSApplication.sharedApplication().mainWindow?.movable = false
    }
fredsco
  • 313
  • 4
  • 19
1

From the docs:

The value in this property is nil when the app’s storyboard or nib file has not yet finished loading. It might also be nil when the app is inactive or hidden.

So, this is a perfectly normal thing to have happen, depending on the circumstances.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks for replying. I am trying to set a value to view2 when a table cell for view 1 is tapped. So, it is supposed to be finished loading. Why would this be? – Naoto Ida Jul 17 '15 at 04:26
  • 1
    Is your window the main window? If it's an `NSPanel` then it refuses to become main unless you override `canBecomeMainWindow()` to return true. Similarly, if it has no title bar and is not resizable, it will refuse to become main. – Ken Thomases Jul 17 '15 at 04:43
  • 1
    As my `Initial Controller`, a `NSWindowController` with `NSWindow` is set. The `NSWindow` has a title bar, and is resizable. I'm not sure of the `NSPanel`...have never used it in my code. – Naoto Ida Jul 17 '15 at 04:49
1

If your app only contains one window, or always starts with the same window without even removing it from memory, you can use this code:

if let window = NSApp.windows.first {
    window.makeKeyAndOrderFront(self) // Or do something else
}

If you need the window during startup of your app, you should load this code async, like this:

DispatchQueue.main.async {
    if let window = NSApp.windows.first {
        window.makeKeyAndOrderFront(self)
    }
}
Ely
  • 8,259
  • 1
  • 54
  • 67
0

Make sure you have a Window in your storyboard as Initial Controller

Basil Mariano
  • 2,437
  • 1
  • 20
  • 13
0

For me the accepted answer did not work. The documentation for activate(ignoringOtherApps:) says:

...you shouldn’t assume the app will be active immediately after sending this message.

For this reason, mainWindow will most likely remain nil. So for me the following works:

extension NSApplication {
  /// Activates the app so main window / key window are no longer `nil`. Pauses a bit between activations and keeps trying.
  /// - Parameter completion: completion called with the key / main window
  @objc public static func withKeyOrMainWindow(mainOnly: Bool = false, completion: @escaping (_ window: NSWindow)->Void) {
    DispatchQueue.main.async {
      let window = mainOnly ? NSApplication.shared.mainWindow : NSApplication.shared.keyWindow ?? NSApplication.shared.mainWindow
      if let window = window {
        completion(window)
      }
      else {
        // Activate the app
        NSApp.activate(ignoringOtherApps: true)
        
        // Short delay
        DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
          NSApplication.withKeyOrMainWindow(completion: completion)
        }
      }
    }
  }
  
  /// Activates the app so main window is no longer `nil`. Pauses a bit between activations and keeps trying.
  /// - Parameter completion: completion called with the main window
  @objc public static func withMainWindow(completion: @escaping (_ window: NSWindow)->Void) {
    return withKeyOrMainWindow(mainOnly: true, completion: completion)
  }
}

Usage:

NSApplication.withKeyOrMainWindow { window in
  alert.beginSheetModal(for: window) { (response: NSApplication.ModalResponse) in
        handler?(response)
  }
}
strangetimes
  • 4,953
  • 1
  • 34
  • 62