swiftobjective-cobjective-c-nullability

When decorating ObjC code with nullability annotations, do you also have to do the definition, or just the declaration?


We're prepping a bunch of or ObjC code for consumption by Swift, which of course requires nullability annotations. Now it's my understanding that those nullability annotations only need to be at the declaration site, not on the definition. This means for cases where the declaration is in say a header file and the definition is in a m/mm file, you don't need to add them to the latter.

For instance...

Foo.h:

- (nullable Foo *)getFooWithKey:(NSString *_Nonnull)key;

Foo.mm:

- (Foo *)subtree:(NSString *)key
{
  // Some implementation here...
}

Now some of my coworkers who have a lot more experience with ObjC are saying they have to go in both places, meaning the mm file actually has to be this...

Foo.mm:

- (nullable Foo *)subtree:(NSString *_Nonnull)key
{
  // Some implementation here...
}

When I ask why, they say so they 'match' up. However, when we remove them from the m/mm files, they still seem to import in Swift just fine without them because Swift is only looking at the headers.

That said, I'm not sure if there are other things to consider that do require them in both places that we're just not testing for so I can't say that's definitive, only that our tests worked.

Readability vs. Productivity

Now normally, even if the latter doesn't actually do anything, if it aids in readability, that would be enough to say 'put it in both places'. However, in our particular case, we have potentially tens of thousands of APIs to update so eliminating that much extra work would be a huge win for everyone. Plus, it makes writing code-mods easier too.

The closest thing I've found in Apple's documentation about this are these two excerpts (emphasis mine)...

However, in the common case there’s a much nicer way to write these annotations: within method declarations you can use the non-underscored forms nullable and nonnull immediately after an open parenthesis, as long as the type is a simple object or block pointer.

and

The non-underscored forms are nicer than the underscored ones, but you’d still need to apply them to every type in your header. To make that job easier and to make your headers clearer, you’ll want to use audited regions.

It's not definitive though. The first may be calling that out as 'a nicer way' that is 'specific to declarations' but it doesn't say annotations in general only go there. The latter too says the non-underscored ones can be used in a header but again, doesn't say nullability annotations in general can only appear in a header, only that the audited regions do.

That said, does anyone know of where Apple would clarify this, or can anything else be shared which would let us know it's ok to skip them in the definitions/implementations?


Solution

  • Only the declaration needs to be annotated. For methods, that means the header file with the interface.