Objective C Modernisation for a Better Swift Experience
Hi, I've been enjoying using MobileVLCKit, but a lot of API is not nicely imported from Objective-C into Swift. This is mostly because the ObjC code has not been modernised. Below are a couple of examples of how a modernised VLCKit would result in a significantly improved experience when using Swift.
Examples:
Nullability:
Properties and parameters in header files should be marked as null or nonnull, which allows them to be imported as optional or non-optional in Swift. Many classes in MobileVLCKit currently lack nullability annotations. Unannotated variables are imported as implicitly unwrapped optionals, which are unpleasant to use.
// without nullability
// Is it possible for the initialiser to return `nil`? Am I allowed to pass a `nil` drawable or will this result in a crash? Will the player only be `nil` when the drawable is `nil`?
let player: VLCMediaListPlayer! = VLCMediaListPlayer(drawable: /* UIView! */)
Options
Option types should be declared using the NS_OPTION macro in ObjC, because these are imported as OptionSet in Swift.
// before
let options = VLCMediaParseLocal | VLCMediaFetchLocal | VLCMediaParseNetwork | VLCMediaFetchNetwork
media.parse(withOptions: Int32(options))
// after
media.parse(withOptions: [.parseLocal, .fetchLocal, .parseNetwork, .fetchNetwork])
Typing
Constants
Related constants should be grouped together in ObjC using typdefs and the NSEXTENSIBLESTRING_ENUM macro, and these are imported as structs with raw values in Swift.
Typed Collections
NSArray, NSDictionary, NSSet, etc in Objc support lightweight generics, and these make imported code much easier to use in Swift.
let media: VLCMedia = ...
// before
// trackInformation is imported as [Any]
let tracksInfo = media.tracksInformation as! [[AnyHashable: Any]]
let videoTrack = tracksInfo.first(where: { $0[VLCMediaTracksInformationType] as? String == VLCMediaTracksInformationTypeVideo })
let width = videoTrack[VLCMediaTracksInformationVideoWidth] as? Int
let height = videoTrack[VLCMediaTracksInformationVideoHeight] as? Int
//after
let tracksInfo: [[VLCMediaTracksInformation: Any]] = media.trackInformation
// some casting is still required because the track information is a dictionary
let videoTrack = tracksInfo.first(where: { $0[.type] as? VLCMediaTracksInformationType == .video })
let width = videoTrack[.videoWidth] as? Int
let height = videoTrack[.videoHeight] as? Int
Misc
Perhaps this will be a more subjective improvement, but in cases such as VLCMedia.tracksInformation
, dictionaries could be completely replaced with their own objects. Each piece of track information would be stored as a property of the object, which would remove the need to type cast and allow the user to know which values will always exist and which can be optional.
Some of the examples would be trivial to implement in the project (while others (e.g. nullability annotations) would take more time), but all would result in breaking changes for Swift users and some would result in breaking changes for ObjC users.
Despite this, I think the improvements to the API would far outweigh the costs of migrating code to support the latest changes. This includes those people using ObjC, too (e.g. they'd get warnings when setting a nonnull property to nil or when adding a string to an NSArray<NSNumber *>).
Is this something the VLC team would be interested in? If so, I'd be happy to submit some patches.