Streaming is available in most browsers,
and in the Developer app.
-
Bring your driver to iPad with DriverKit
Discover how you can easily connect Thunderbolt and USB accessories to iPad with DriverKit. We'll show you how to convert your existing Mac drivers without any code changes, learn how to add real-time audio support with AudioDriverKit, and provide best practices and tips for developing drivers for iPad.
Resources
- Communicating between a DriverKit extension and a client app
- Implementing drivers, system extensions, and kexts
- System Extensions and DriverKit
Related Videos
WWDC21
WWDC20
-
Download
♪ ♪ Souvik Banerjee: Hi, and welcome to "Bring your driver to iPad with DriverKit." I'm Souvik, and today I'll discuss several exciting new developments in DriverKit. We're going to discuss three topics today. First, I'll give a brief overview of DriverKit. Then, I'll discuss some updates to AudioDriverKit. And I'll end with how to bring your drivers to the iPad Let's get started with an overview. In 2019, we introduced DriverKit, a replacement for IOKit device drivers. DriverKit brought a new way to extend the system that is more reliable and secure, running in userspace. And it's easier to develop since your process isn't in the kernel. These driver extensions, also known as dexts, are bundled in apps, and you can easily distribute your apps and drivers on the Mac App Store. People can easily find your driver with a search, and if your driver is no longer needed, you can just delete the app to uninstall it. Since introducing DriverKit, we have added support for many new driver families.
We now support Networking, Block Storage, Serial, Audio, and SCSI controller and peripheral drivers in addition to transports such as USB, PCI, and HID.
To learn more about the kinds of drivers you can build with DriverKit, please check out the "Create audio drivers with DriverKit" session from WWDC 2021 and the "Modernize PCI and SCSI drivers with DriverKit" session from WWDC 2020. Next, we added several new features in AudioDriverKit recently that I want to highlight. One of these features is real-time operations. We're excited to introduce a new feature in AudioDriverKit allowing you to register a real-time callback. This callback gets invoked every time an IO operation happens. You can use this callback if you need to modify your audio buffers on a real-time thread, such as for signal processing.
To register a real-time callback in AudioDriverKit, we declare an IOOperationHandler block to set on the IOUserAudioDevice. This block will be called from a real-time context when an IO operation occurs on the IOUserAudioStream buffers for the device.
Inside the block, we check what the operation is, and we can modify the data as necessary. Finally, we call SetIOOperationHandler to set the block on the audio device. Now let's talk about entitlements. When we introduced AudioDriverKit, you had to use the allow-any-userclient-access entitlement on your driver. In macOS 12.1, we introduced a new entitlement specifically for AudioDriverKit. Please update your audio drivers to use the new audio family entitlement instead of the allow-any-userclient-access entitlement. You can keep the allow-any-userclient-access entitlement if you want any app to be able to communicate with your driver. This new entitlement is public for development, so you can get started using this today without filing a request. In fact, all DriverKit family entitlements are now available to use for development. To request this entitlement for distribution, please visit the System Extension page on developer.apple.com. Now, I'm really excited to tell you about DriverKit on iPad. Professionals are increasingly using iPad to do their best work. But many rely on external hardware that they couldn't use on iPad. So today, we're excited to announce that DriverKit is coming to iPad.
DriverKit on macOS has made it possible to extend the system in a safe and secure way, and we're bringing that same technology to the iPad. In fact, if you've already created a driver with DriverKit on the Mac, you can bring that exact same driver to the iPad without any changes to your driver. USB, PCI, and Audio will be supported in iPadOS 16. This will enable Thunderbolt audio interfaces on iPad for the first time, and many more devices. This is made possible with the power of the M1 chip. All iPads with M1 will support DriverKit. DriverKit on iPadOS is the same as on macOS. This means that you can build one DriverKit driver and have it work on both platforms, no source changes required. In addition , using the new multiplatform apps feature in Xcode 14, you can easily create a single app target to deliver your driver on both Mac and iPad. For more information about multiplatform apps, please check out the "Use Xcode to develop a multiplatform app" session.
Xcode also now supports automatic signing of DriverKit drivers. It knows how to handle DriverKit on iPadOS and can provision for both Mac and iPad. You no longer need to configure manual signing for DriverKit drivers. Your iPadOS app and driver can be distributed on the App Store , just like on macOS. This means that you can take advantage of features like in-app purchases and have your driver be easily discoverable by users. Let's see just how easy it is to take an existing macOS driver and app and bring it to iPad. Here, I have an app called DriverKitSampleApp. It has a SwiftUI view with a label and a button allowing the user to install the driver. Our driver is called NullDriver. It prints a message when the driver starts and starts a timer that fires every second and increments a counter called timerCount. To make this an iPad app, all I need to do is select the DriverKitSampleApp target in Xcode...
And add iPad to Supported Destinations.
Now I can change my run destination to the iPad I have connected to my Mac.
Let's try running this on the iPad. Here's our iPad app. We have the label and the button from the view we saw in Xcode. Tapping the Install Dext button takes us to Settings, where we see this new Drivers link. We tap that link, and we see a list of all drivers bundled in this app. We can then enable our Null Driver. So you might have noticed several things in the demo. Our Null Driver is bundled inside our iPadOS app, and it gets automatically discovered by the system after installation. On macOS , you would need to use the SystemExtension framework to prompt the user to install the driver. On iPadOS, there is no SystemExtensions framework.
Inside Xcode, you can see that our driver is embedded within our app. Since drivers are low-level software and are privileged, they need to be approved by the user before they can run. On macOS, users need to go to the Security & Privacy preferences to allow system extensions. On iPadOS, the driver approvals are in the Settings app. There are two options for driver approvals. If there is at least one app with a driver installed, there will be a menu inside General Settings with a list of all available drivers. Each driver can be toggled on or off. If your app contains a Settings bundle, there will be a Drivers link inside your app's Settings. Your app should prompt the user to enable the driver in Settings. Let's start again with our macOS driver project and see how we can have our app prompt the user to enable the driver in Settings. We start by adding iPad to our supported destinations.
Our SwiftUI view has a button to install the driver, and our view model has a state machine that interacts with the SystemExtensions framework. Since this project is going to build for both Mac and iPad, we want to keep our Mac view and view model but create a new view that will be used on iPad.
Then, we can go to Build Phases and Compile sources and change the platform filter for each file to conditionally compile for iOS or macOS.
Now, let's add a Settings bundle to our app. We're going to use the default example Settings for now, but we can change these later to real Settings that the app can use.
Now let's check the iOS view we just created. We can copy our macOS view to our iOS view to use as a starting point.
Our iOS view doesn't use a view model, so we can remove that.
We also need to change our button action to open our Settings bundle. This will take the user into Settings, so that they can enable the driver.
Finally, we change the button text to make it clear that the user needs to enable the driver in Settings.
Let's see this in action. We have the view we designed, and tapping the button takes us to our Settings bundle. Then we go into Drivers and enable the Null Driver.
It's important to keep in mind that drivers launch on demand. Although we've enabled the driver in Settings, the driver only starts running when the hardware device is plugged in to the iPad. After the driver starts running, I can attach a debugger to it using Xcode wireless debugging. To do that, I go to the Debug menu in Xcode, attach to process, then select the NullDriver process. Once attached, I can set breakpoints or pause execution. Here, I've set a breakpoint in our timer. I'm going to print timerCount to see how many times our timer has been called.
When you're done debugging, detach from the driver process using the Debug menu in Xcode.
So now we have a driver. But a driver isn't very useful by itself. It needs to communicate with the rest of the system. Some DriverKit frameworks like AudioDriverKit handle this for you. But if you need to do something more advanced, such as creating a custom control panel app for your hardware, you need to have your app be able to communicate with your driver. This is what user clients allow you to do. They allow you to define your own interface, allowing app and driver communication. Apps use the IOKit.framework to open user clients. For an example of how this works, please see the sample code on developer.apple.com.
So we know that apps can communicate with drivers. But it's important to keep security in mind. Since drivers are privileged, we don't want to allow every app to communicate with drivers.
On macOS, the app needs the driverkit userclient-access entitlement, and the value is an array of allowed driver bundle identifiers.
On iPadOS, we added a new entitlement called Communicates With Drivers. It replaces the macOS user client entitlement. This entitlement grants your app the ability to open user clients to your driver.
If you want to add the Communicates With Drivers entitlement manually to your app, here's the XML entitlement string.
We can also add this entitlement from Xcode. In Xcode, we go to Signing and Capabilities, then add a new capability. Then, we can search for "communicates with drivers" and add the capability to our app.
Another use case for user clients is to allow apps from other developers to interact with your driver. So in this case, suppose you have an app and driver and you want to provide a service to other apps, including those from other developers. DriverKit user clients also support this.
Each app that needs to communicate with drivers needs the communicates with drivers entitlement. The driver needs the Allow Third Party User Clients entitlement. This allows apps built with a different team identifier to open a user client to the driver. Without this entitlement, only apps from the same team can communicate with the driver. If you want to add the Allow Third Party User Clients entitlement manually to your driver, here's the XML entitlement string.
Or we can add this capability from Xcode by going into Signing and Capabilities for our driver.
These new user client entitlements are public for development, which means that you can get started using this today without any approval. To request these entitlements for distribution, please see our developer website. DriverKit drivers also have important implications for app update. Automatic app update ensures users always get the latest version of your app. However, for apps containing drivers, the update process works a little differently. Let's suppose you distribute version 1 of your app on the app store. Then, you install that app along with its bundled driver on your iPad and enable the driver in Settings. When you plug in the hardware device for your driver, the driver starts running, and once the driver starts running, your app can begin communicating with your driver using user clients. Now, suppose you find a bug in your app and you submit version 2 to the App Store. Because of automatic app update, the version 2 app is downloaded and installed on your iPad automatically. The driver approval state is maintained through updates, so you don't need to approve the driver again. However, notice that the hardware is still plugged in, and our version 1 driver is still running. Driver version 2 was downloaded with the app update but does not start running. Since the old driver still continues running, your version 2 app may have to communicate with the version 1 driver.
When the hardware device is unplugged, the driver stops running, so now driver version 1 is done and we can update the driver to version 2.
Now, if you plug in the device again, we start the version 2 driver, and now your app is communicating with the new driver.
To recap: Apps are updated anytime with automatic app update. Drivers are updated after the device is unplugged. And your app may communicate with an old driver. When your app and driver are ready, you can submit them to the App Store. Your drivers can only run on devices that support DriverKit. If you want to restrict your app to those devices, such as if your app only installs a driver, add DriverKit to your app's UIRequiredDeviceCapabilities. This will prevent users from installing your app on a device that does not support DriverKit. We also suggest submitting a video to App Review showing how your app and driver work with your hardware device. So that's DriverKit on iPad. You can now bring USB, PCI, and Audio drivers to iPad with M1 and deliver those drivers inside apps on the App Store. And if you already have a driver, it's easy to bring that to iPad. We encourage developers to try using DriverKit on iPad and provide any feedback using Feedback Assistant. Thank you for watching.
-
-
2:25 - Register real-time callback in AudioDriverKit
// Declare a IOOperationHandler block to set on the IOUserAudioDevice. // The block will be called from a real time context when a i/o operation // occurs on the IOUserAudioStream buffers for the device. io_operation = ^kern_return_t(IOUserAudioObjectID in_device, IOUserAudioIOOperation in_io_operation, uint32_t in_io_buffer_frame_size, uint64_t in_sample_time, uint64_t in_host_time) { // Add custom code to make modifications to the buffers as necessary if (in_io_operation == IOUserAudioIOOperationWriteEnd) { ... } else if (in_io_operation == IOUserAudioIOOperationBeginRead) { ... } return kIOReturnSuccess; }; this->SetIOOperationHandler(io_operation);
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.