So, I wrote a category on NSPersonNameComponents to implement some custom name-formatting logic, and ran up against an interesting problem.  Try as I might, my code in the category just wasn’t visible at all from swift.  I wrote code like this:

   let components = PersonNameComponents(…)

components.myObjCFunction()

but whatever I would do, the compiler would complain that  PersonNameComponents didn’t have a function myObjCFunction()

But, the ‘generated interface' showed that there was in fact a myObjCFunction(), and if I wrote more code in that .m file, like a category on NSString, I’d be able to use it from swift, so the module was getting imported correctly, and there wasn’t a problem with the header includes. What on earth was wrong?

After spending far too long chasing some rabbit holes, I figured out that PersonNameComponents and NSPersonNameComponents are not the typical pair of swift/ObjC Foundation classes.  NSPersonNameComponents is indeed an Objective C class, but PersonNameComponents is a struct (not a class). This mucked with the visibility of the selectors in the category

It turns out this code worked just fine:

   let nsComponents = NSPersonNameComponents(…)

nsComponents.myObjCFunction()

And I was happy for about two minutes until the compiler complained about this:

   let formatter = PersonNameComponentsFormatter(…)

   let nsComponents = NSPersonNameComponents(…

   let formattedName =  formatter.string(from:nsComponents) 


Apparently, PersonNameComponentsFormatter is equivalent to NSPersonNameComponentsFormatter, but it takes a PersonNameComponents struct as an argument here, and can’t take an NSPersonNameComponents.

The solution was easy, once I figured out that the compiler would be happy with

  let formattedName =  formatter.string(from:nsComponents as PersonNameComponents) 

So, tl;dr: sometimes the swift equivalent of an Objective-C class isn’t a swift class, and in that case, writing a category on the Objective-C class isn’t visible to the swift equivalent.  In that case, one needs to actually use the objectiveC class in swift with the NS name and everything.