Search code examples
swiftxcodeavfoundationnsnotificationcenternsnotifications

Some NSNotification.Name constants are missing when enabling C++ interoperability


When enabling direct C++ interoperability in Xcode, this simple code below

NotificationCenter.default.addObserver(self,
                                       selector: #selector(subjectAreaDidChange(_:)),
                                       name: .AVCaptureDeviceSubjectAreaDidChange,
                                       object: nil)

gives me the following error

Type 'NSNotification.Name?' has no member 'AVCaptureDeviceSubjectAreaDidChange'

But if I switch C++ and Objective-C interoperability to just C / Objective-C then the code compiles without errors.

The issue seems to affect NSNotification.Name constants only from AVFoundation framework (although there maybe more).

Why do I get this error and how do I fix it?


Solution

  • Short answer

    It’s a known bug (https://forums.developer.apple.com/forums/thread/733693) for Xcode 15.

    You can work it around by using raw strings of needed notifications:

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(subjectAreaDidChange),
                                           name: .init(rawValue: "AVCaptureDeviceSubjectAreaDidChangeNotification"),
                                           object: nil)
    

    Long answer

    If you look inside AVFoundation/AVCaptureDevice.h you can find:

    AVF_EXPORT NSString *const AVCaptureDeviceSubjectAreaDidChangeNotification API_AVAILABLE(ios(5.0), macCatalyst(14.0), tvos(17.0)) API_UNAVAILABLE(macos, visionos) API_UNAVAILABLE(watchos);
    

    The interoperability just looks for “Notification” suffix in exported strings and converts them to NSNotification.Name e.g.:

    AVCaptureDeviceSubjectAreaDidChangeNotification -> NSNotification.Name.AVCaptureDeviceSubjectAreaDidChange
    

    The same works for strings in your app:

    // Test.h
    extern NSString *const MyNotification;
    
    // Test.m
    NSString *const MyNotification = @"MyNotification";
    

    Then you can write in your swift file:

    let notification: NSNotification.Name = .My
    

    But it works for C / Objective-C only and doesn't for C++ / Objective-C++ because the compiler doesn't generate appropriate properties in NSNotification.Name extension, for instance:

    extension NSNotification.Name {
        ...    
        @available(iOS 5.0, *)
        public static let AVCaptureDeviceSubjectAreaDidChange: NSNotification.Name
    }