Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Why a driverkit extension needs a CMIO extension

I developed a driverkit extension based on overriding-the-default-usb-video-class-extension, but the link didn’t give the details of realization. I asked DTS who gave two tips: 1, Do you also have a CMIO extension to load in place of the default overriding-the-default-usb-video-class-extension 2, Your DriverKit extension’s info.plist is also missing the CameraAssistantBundleID.

I want to know why a driverkit extension needs a CMIO extension, what’s the data and control flow?

My project like following:

ExampleCam is the main app, which install CMIO extension and USB driver extension.

The ExampleCam.entitlements:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.system-extension.install</key>
	<true/>
    <key>com.apple.developer.driver-extension.install</key>
    <true/>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.application-groups</key>
	<array>
		<string>$(TeamIdentifierPrefix)com.lqs.example.ExampleCam</string>
	</array>
	<key>com.apple.security.files.user-selected.read-only</key>
	<true/>
</dict>
</plist>

If I use the line: com.apple.developer.driver-extension.install, I can't run the app as the picture 2. But If I delete it, I got the error picture 1:

picture 1:

picture 2:

ContentView.swift:

//
//  ContentView.swift
//  ExampleCam
//
//

import SwiftUI
import SystemExtensions
import Logging

struct ContentView: View {
    @State private var logs: [String] = []
    @State private var extensionDelegate: ExtensionDelegate?
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
            
            // 日志显示区域
            ScrollView {
                Text(logs.joined(separator: "\n"))
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding()
            }
            
            // 按钮区域
            HStack {
                Button("Start USB Driver Extension") {
                    startCameraExtension()
                }
                
                Button("Stop USB Driver Extension") {
                    stopCameraExtension()
                }
            }
            .padding()
        }
        .padding()
    }
    
    // MARK: - Private Methods
       
       private func startCameraExtension() {
           guard let identifier = Self.extensionBundle().bundleIdentifier else {
               return
           }

           let logString = "identifier \(identifier)"
           logger.info("\(logString)")
           logs.append(logString)

           let activationRequest = OSSystemExtensionRequest.activationRequest(
               forExtensionWithIdentifier: identifier,
               queue: .main
           )
           extensionDelegate = ExtensionDelegate(logs: $logs)
           activationRequest.delegate = extensionDelegate
           OSSystemExtensionManager.shared.submitRequest(activationRequest)
       }
       
       private func stopCameraExtension() {
           guard let identifier = Self.extensionBundle().bundleIdentifier else {
               return
           }

           let logString = "identifier \(identifier)"
           logger.info("\(logString)")
           logs.append(logString)

           let deactivationRequest = OSSystemExtensionRequest.deactivationRequest(
               forExtensionWithIdentifier: identifier,
               queue: .main
           )
           extensionDelegate = ExtensionDelegate(logs: $logs)
           deactivationRequest.delegate = extensionDelegate
           OSSystemExtensionManager.shared.submitRequest(deactivationRequest)
       }
       
       private static func extensionBundle() -> Bundle {
           let extensionsDirectoryURL = URL(
               fileURLWithPath: "Contents/Library/SystemExtensions",
               relativeTo: Bundle.main.bundleURL
           )

           let extensionURLs: [URL]
           do {
               extensionURLs = try FileManager.default.contentsOfDirectory(
                   at: extensionsDirectoryURL,
                   includingPropertiesForKeys: nil,
                   options: .skipsHiddenFiles
               )
           } catch let error {
               fatalError("fatal 1 \(error)")
           }

           // 专门查找 ExampleMyUSB.dext
           guard let extensionURL = extensionURLs.first(where: { url in
               url.lastPathComponent.contains("ExampleMyUSB.dext")
           }) else {
               fatalError("fatal 2: ExampleMyUSB.dext not found")
           }
           
           guard let extensionBundle = Bundle(url: extensionURL) else {
               fatalError("fatal 3 \(extensionURL.absoluteString)")
           }

           return extensionBundle
       }
}

// MARK: - Extension Delegate
class ExtensionDelegate: NSObject, OSSystemExtensionRequestDelegate {
    @Binding var logs: [String]
    
    init(logs: Binding<[String]>) {
        self._logs = logs
    }
    
    func request(
        _ request: OSSystemExtensionRequest,
        actionForReplacingExtension existing: OSSystemExtensionProperties,
        withExtension ext: OSSystemExtensionProperties
    ) -> OSSystemExtensionRequest.ReplacementAction {
        let logString = "\(#function): (request: \(request.identifier))"
        logger.trace("\(logString)")
        DispatchQueue.main.async {
            self.logs.append(logString)
        }
        return .replace
    }
    
    func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
        let logString = "\(#function): (request: \(request.identifier))"
        logger.trace("\(logString)")
        DispatchQueue.main.async {
            self.logs.append(logString)
        }
    }
    
    func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
        let logString = "\(#function): (request: \(request.identifier), result: \(result.rawValue))"
        logger.trace("\(logString)")
        DispatchQueue.main.async {
            self.logs.append(logString)
        }
    }
    
    func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
        let logString = "\(#function): (request: \(request.identifier), error: \(error))"
        logger.trace("\(logString)")
        DispatchQueue.main.async {
            self.logs.append(logString)
        }
    }
}

#Preview {
    ContentView()
}


The extension target is a module to create a example camera, and the ExampleMyUSB is a driverkit according to overriding-the-default-usb-video-class-extension

Extension Info.plist:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

	<key>CMIOExtension</key>

	<dict>

		<key>CMIOExtensionMachServiceName</key>

		<string>$(TeamIdentifierPrefix)$(PRODUCT_BUNDLE_IDENTIFIER)</string>

	</dict>

</dict>

</plist>

ExampleMyUSB Info.plist:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

	<key>IOKitPersonalities</key>

	<dict>

		<key>ExampleMyUSB</key>

		<dict>

			<key>CFBundleIdentifierKernel</key>

			<string>com.apple.kpi.iokit</string>

			<key>IOClass</key>

			<string>IOUserService</string>

			<key>IOMatchCategory</key>

			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

			<key>IOProviderClass</key>

			<string>IOUserResources</string>

			<key>IOResourceMatch</key>

			<string>IOKit</string>

			<key>IOUserClass</key>

			<string>ExampleMyUSB</string>

			<key>IOUserServerName</key>

			<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

            <key>IOProbeScore</key>

            <integer>100000</integer>

            <key>idVendor</key>

            <integer>1452</integer>

            <key>idProduct</key>

            <integer>34068</integer>

            <key>IOProviderMergeProperties</key>

            <dict>

                <key>CameraAssistantBundleID</key>

                <string>com.lqs.example.ExampleCam.Extension</string>

            </dict>

		</dict>

	</dict>

	<key>OSBundleUsageDescription</key>

	<string></string>

</dict>

</plist>```
Why a driverkit extension needs a CMIO extension
 
 
Q