Xcode 7.3.1 to 8.1: NSApp delegate reference problem

I have created an app in which the application delegate contains a number of methods accessed by other classes. The class is identified as the application delegate, using <NSApplicationDelegate>. The app compiles correctly in XCode 7.3.1. However, when upgrading to XCode 8.1, the code no longer compiles. Every reference to a method in the application delegate, [[NSApp delegate] whatever], results in an error: No known instance method for selector 'whatever'. It looks as if the compiler has the wrong idea about which class is the application delegate. Is there something new in 8.1 I need to do to identify the application delegate? Any ideas on how to fix this welcome.

Replies

In your example, is

whatever
a method in the
NSApplicationDelegate
protocol? Or one of your methods?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

The methods in question are ones I created. Thanks.

There’s a general explanation here and then something more.

In general, Objective-C in ARC mode has to know about every method you call so it can decide the correct ARC behaviour for that method. If you have code like this:

- (void)test {
    id someObj = nil;
    [someObj someCompletelyUniqueMethod];
}

the compiler has no idea how to handle the ARC requirements for

-someCompletelyUniqueMethod
and thus generates this error.

You can resolve this issue by putting a declaration of

-someCompletelyUniqueMethod
somewhere visible to the compiler. In your case, you’d add it to the class interface for the app delegate and then include that header. And, indeed, that will resolve this problem.

However, it doesn’t resolve your problem (-: If you get the above code working and then change it to this:

- (void)test {
    [[NSApp delegate] someCompletelyUniqueMethod];
}

the error comes back, and this time only on Xcode 8. I can see why this is happening (more on this below) but I’m not sure if this change in behaviour is correct or not. You should feel free to file a bug so that one of the compiler engineers can take a look.

Why this is different relates to the type of

[NSApp delegate]
. In recent SDKs it’s type is
id<NSApplicationDelegate>
, so it’s not a plain
id
as in the first example above.

As to what you should do about this, I have a bunch of recommendations:

  • The most expedient solution is to cast

    [NSApp delegate]
    to
    id
    . This brings things back to how they were working with Xcode 7. There’s a code snippet showing this below.
  • A better solution is to cast

    [NSApp delegate]
    to
    (AppDelegate *)
    , which gives better type checking in the case where the method has multiple, potentially conflicting, ARC behaviours. This is also shown below.
  • The solution I recommend is that you avoid structuring your app so that lower-level code calls ‘up’ to the app delegate. That structure tends to produce a very inflexible source base, where you can’t move things around because everything depends on the app delegate. A better approach is to have your app delegate push dependencies down into lower-level code.

- (void)testExpedient {
    [(id)[NSApp delegate] someCompletelyUniqueMethod];
}

- (void)testBetter {
    [(AppDelegate *)[NSApp delegate] someCompletelyUniqueMethod];
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"