How to handle Objective-C imports with incorrect nullability

I'm trying to use the MPMediaEntity.enumerateValues(forProperties:using:) method from Apple's Media Player framework but I'm encountering an issue that I believe is related to Swift's Objective-C interoperability.

When I execute this code:

MPMediaQuery.playlists().collections!.forEach {
    $0.enumerateValues(
        forProperties: [MPMediaPlaylistPropertyDescriptionText],
        using: { property, value, stop in
            print("running block")
        })
}

I get a crash with an EXC_BAD_ACCESS signal at the address 0x0 (i.e. a null pointer dereference). However, if I execute the following Objective-C code:

MPMediaQuery *playlistsQuery = [MPMediaQuery playlistsQuery];
NSArray<MPMediaItemCollection *> *playlists = [playlistsQuery collections];
NSSet<NSString *> *set = [NSSet setWithObject:MPMediaPlaylistPropertyDescriptionText];
[playlists enumerateObjectsUsingBlock:^(MPMediaItemCollection *obj, NSUInteger idx, BOOL *stop) {
    [obj enumerateValuesForProperties:set usingBlock:^(NSString *property, id value, BOOL *stop) {
        NSLog(@"%@", value ? @"value is not nil" : @"value is nil");
    }];
}];

then the program does not crash. Furthermore, the output of the Objective-C program shows that value is sometimes nil, despite Swift importing the parameter as a non-null Any. I hypothesize that the crash is caused by Swift trying to convert an Objective-C class into an Any existential without checking whether the pointer is nil.

I have a two questions:

  • Regarding the value parameter of the block being imported into Swift as Any instead of Any?, is this a bug? Or is it intentional?
  • Is there a way to avoid this crash?

A few other notes:

  • I tried passing in a closure that operates on Any? instead of Any (i.e. using: { (property, value: Any?, stop) in ... }), but it still crashed.
  • I did check that permission to use the music library was granted for both the Swift and Objective-C programs.
  • The MPMediaEntity.value(forProperty:) method, which provides similar functionality, does use Any? instead of Any for its return type. However, it's less efficient for accessing multiple properties, so it's suboptimal for my use case.
  • It seems that nil is only returned if the playlist description is empty. However, some playlists with an empty description return an empty string instead of nil. Playlist folders seem to always return nil for their description, but I'm not 100% sure.

You can write an Objective-C shim that imports the correct optional signature into Swift.

1 Like

I've used .apinotes files in the past to correct nullability, incorrect types and naming in frameworks where I don't control the API, but that won't work for Apple's frameworks.

@bzamayo's suggestion of a shim is a good one :+1:

1 Like