An unofficial Swift Package Manager distribution of Google MediaPipe Tasks Vision for iOS and macOS.
The package provides:
MediaPipeTasksVision- MediaPipe Tasks Vision XCFramework
MediaPipeTasksVisionHandLandmarker- The standard Hand Landmarker model and helpers for creating
HandLandmarkerOptions
- The standard Hand Landmarker model and helpers for creating
- iOS 17 or later / macOS 14 or later
- arm64 iOS devices/simulators
- macOS: universal (arm64 + x86_64) linking, but MediaPipe inference runs only when executing natively on Apple Silicon — see Intel Macs and Rosetta
- Xcode 26.x or later
You can install this package with Swift Package Manager.
Select one of the following products:
| Product | Description |
|---|---|
MediaPipeTasksVision |
MediaPipe Tasks Vision APIs only |
MediaPipeTasksVisionHandLandmarker |
APIs and the bundled Hand Landmarker model |
Select MediaPipeTasksVisionHandLandmarker when using the standard Hand Landmarker model included with this package.
The bundled product includes hand_landmarker.task. Applications do not need
to copy the model, locate it with Bundle.module, or manage its checksum.
import MediaPipeTasksVision
import MediaPipeTasksVisionHandLandmarker
let options = try HandLandmarkerModel.makeOptions(
runningMode: .image,
numberOfHands: 2
)
let handLandmarker = try HandLandmarker(options: options)import MediaPipeTasksVision
import MediaPipeTasksVisionHandLandmarker
import UIKit
func detectHands(in image: UIImage) throws -> HandLandmarkerResult {
let options = try HandLandmarkerModel.makeOptions(
runningMode: .image
)
let handLandmarker = try HandLandmarker(options: options)
let mpImage = try MPImage(uiImage: image)
return try handLandmarker.detect(image: mpImage)
}Each detected hand contains 21 normalized landmarks, world landmarks, and handedness information.
On macOS there is no UIImage; wrap a CVPixelBuffer (for example the output
of a camera capture or an NSImage rendered into a BGRA buffer) instead:
import MediaPipeTasksVision
import MediaPipeTasksVisionHandLandmarker
func detectHands(in pixelBuffer: CVPixelBuffer) throws -> HandLandmarkerResult {
let options = try HandLandmarkerModel.makeOptions(
runningMode: .image
)
let handLandmarker = try HandLandmarker(options: options)
let mpImage = try MPImage(pixelBuffer: pixelBuffer)
return try handLandmarker.detect(image: mpImage)
}Set the delegate before creating the HandLandmarker.
let options = try HandLandmarkerModel.makeOptions(
runningMode: .liveStream,
numberOfHands: 2
)
options.handLandmarkerLiveStreamDelegate = delegate
let handLandmarker = try HandLandmarker(options: options)Send frames with monotonically increasing timestamps:
try handLandmarker.detectAsync(
image: mpImage,
timestampInMilliseconds: timestamp
)When using CVPixelBuffer or CMSampleBuffer, the underlying pixel format must be kCVPixelFormatType_32BGRA.
The model URL can be obtained directly when custom options are needed:
let modelURL = try HandLandmarkerModel.url
let options = HandLandmarkerOptions()
options.baseOptions.modelAssetPath = modelURL.path
options.runningMode = .video
options.numHands = 2Model metadata is also available:
let metadata = try HandLandmarkerModel.metadata()
print(metadata.modelVersion)
print(metadata.testedMediaPipeVersion)
print(metadata.sha256)Use the MediaPipeTasksVision product and provide an absolute path to your
model file:
import MediaPipeTasksVision
let options = HandLandmarkerOptions()
options.baseOptions.modelAssetPath = modelURL.path
options.runningMode = .image
options.numHands = 2
let handLandmarker = try HandLandmarker(options: options)MediaPipeTasksVisionHandLandmarker also provides a tracker API that hides
every MediaPipe type behind package-defined value types, which keeps app
binaries free of MediaPipe symbol references outside this package:
import MediaPipeTasksVisionHandLandmarker
guard MediaPipeHandTrackingSupport.isAvailable else {
// Fall back to another implementation (e.g. Vision).
return
}
let tracker = try MediaPipeHandTrackingFactory.makeTracker(
configuration: HandLandmarkTrackingConfiguration(numberOfHands: 2)
)
let result = try tracker.detect(in: pixelBuffer, timestampInMilliseconds: timestamp)
for hand in result.hands {
print(hand.handedness, hand.landmarks.count)
}The macOS slice is built from upstream MediaPipe sources plus the patches in
macos/ (Google does not ship macOS binaries):
- Inference runs on the CPU (XNNPACK). The GPU delegate is not available.
The macOS framework is universal so that universal apps can link and launch
everywhere, but only the arm64 slice contains MediaPipe. The x86_64 slice is
a link-only stub: every class exists for the linker and dyld, task
initializers report an error, and any other use raises
MPPUnsupportedArchitectureException.
Check MediaPipeHandTrackingSupport.isAvailable (or, when using the raw
APIs, guard with #if arch(arm64)) before creating MediaPipe objects. The
check reflects the executing binary slice, so it is also false on Apple
Silicon Macs when the app runs under Rosetta.
This project is licensed under the Apache License 2.0.
MediaPipe, the bundled Hand Landmarker model, and bundled third-party dependencies remain subject to their respective licenses and notices.
See: