Integrating OpenMedKit Swift Library in macOS Applications: Clinical NLP On-Device

OpenMedKit enables on-device clinical NLP for macOS apps via Swift Package Manager, supporting both MLX (Apple Silicon-accelerated) and CoreML backends to perform PII extraction and named entity recognition completely offline.

OpenMedKit is a Swift package developed in the maziyarpanahi/openmed repository that brings clinical NLP capabilities—specifically named entity recognition (NER) and PII de-identification—directly to macOS applications. According to the source code in swift/OpenMedKit/, the library abstracts complex model runtime logic behind a single OpenMedBackend enum, allowing developers to run Hugging Face MLX models or CoreML bundles locally without network dependencies after initial setup.

Installing the Swift Package

Add OpenMedKit to your macOS project using Swift Package Manager (SPM). The package manifest lives in [swift/OpenMedKit/Package.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedKit/Package.swift) and defines dependencies on MLX, Transformers, and ZIPFoundation.

Via Xcode:

  1. Open your macOS project and select File > Add Packages….
  2. Enter the repository URL: https://github.com/maziyarpanahi/openmed.
  3. Select the OpenMedKit product and add it to your app target.

Or via Package.swift:

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "YourMacApp",
    platforms: [.macOS(.v14)],
    dependencies: [
        .package(url: "https://github.com/maziyarpanahi/openmed.git", from: "1.5.5")
    ],
    targets: [
        .executableTarget(
            name: "YourMacApp",
            dependencies: [
                .product(name: "OpenMedKit", package: "openmed")
            ]
        )
    ]
)

Choosing a Runtime Backend

OpenMedKit supports two backends defined in [OpenMedBackend.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedKit/Sources/OpenMedKit/OpenMedBackend.swift). Your choice depends on whether you target Apple Silicon Macs exclusively or need broader compatibility.

The MLX backend downloads and caches Hugging Face MLX artifacts locally, running inference with Apple Silicon acceleration. This is the optimal path for production macOS apps running on physical Apple Silicon hardware.

Use OpenMedModelStore.downloadMLXModel(repoID:) defined in [OpenMedModelStore.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedKit/Sources/OpenMedKit/OpenMedModelStore.swift) to fetch the model:

import OpenMedKit

// Download and cache the MLX model (async, one-time download)
let modelDirectory = try await OpenMedModelStore.downloadMLXModel(
    repoID: "OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-mlx"
)

// Initialize with MLX backend
let openmed = try OpenMed(
    backend: .mlx(modelDirectoryURL: modelDirectory)
)

// Extract PII entities
let entities = try openmed.extractPII(
    "Patient John Doe, DOB 1990-05-15, SSN 123-45-6789",
    confidenceThreshold: 0.85
)

CoreML Backend (Bundled Models)

The CoreML backend uses pre-converted .mlmodelc or .mlpackage bundles shipped with your app bundle. This approach works on both Intel and Apple Silicon Macs and is required for macOS Simulator testing.

import OpenMedKit

// Locate bundled CoreML resources
let modelURL = Bundle.main.url(forResource: "OpenMedPII", withExtension: "mlmodelc")!
let labelURL = Bundle.main.url(forResource: "id2label", withExtension: "json")!

// Initialize with CoreML backend
let openmed = try OpenMed(
    backend: .coreML(
        modelURL: modelURL,
        id2labelURL: labelURL,
        tokenizerName: "OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1"
    )
)

// Run analysis
let entities = try openmed.extractPII("Medical record number: MR123456")

The convenience initializers for both backends are implemented in [OpenMedKit.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedKit/Sources/OpenMedKit/OpenMedKit.swift).

Calling the Analysis API

Once initialized, the OpenMed class exposes two primary methods defined in the high-level API:

  • extractPII(_:confidenceThreshold:) – Returns [EntityPrediction] containing detected PII spans with labels and confidence scores.
  • analyzeText(_:confidenceThreshold:) – General NER analysis for clinical entities.

Both methods perform tokenization, inference, and post-processing (including smart span repair and entity merging logic found in [PostProcessing.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedKit/Sources/OpenMedKit/PostProcessing.swift)).

// Process clinical text
let clinicalText = """
Patient: Jane Doe
Date of Birth: 01/15/1970
Insurance ID: INS-987654321
"""

do {
    let predictions = try openmed.extractPII(clinicalText, confidenceThreshold: 0.8)
    
    for entity in predictions {
        print("[\(entity.label)] \(entity.text) (confidence: \(entity.confidence))")
    }
} catch {
    print("Inference failed: \(error.localizedDescription)")
}

SwiftUI Integration Example

For macOS applications using SwiftUI, implement an async task to handle model downloading and inference. The following pattern mirrors the implementation in the demo app's [ContentView.swift](https://github.com/maziyarpanahi/openmed/blob/master/swift/OpenMedScanDemo/OpenMedScanDemo/OpenMedScanDemo/ContentView.swift):

import SwiftUI
import OpenMedKit

struct ClinicalAnalyzerView: View {
    @State private var inputText = "Patient: Robert Smith, MRN: 123456, DOB: 03/22/1965"
    @State private var entities: [EntityPrediction] = []
    @State private var isProcessing = false
    @State private var errorMessage: String?
    
    var body: some View {
        VStack(spacing: 16) {
            TextEditor(text: $inputText)
                .font(.body)
                .border(Color.gray)
                .frame(minHeight: 100)
            
            Button(action: { Task { await analyze() } }) {
                if isProcessing {
                    ProgressView()
                        .scaleEffect(0.8)
                } else {
                    Text("Extract PII")
                }
            }
            .disabled(isProcessing)
            
            if let error = errorMessage {
                Text(error)
                    .foregroundColor(.red)
                    .font(.caption)
            }
            
            List(entities) { entity in
                VStack(alignment: .leading, spacing: 4) {
                    Text("[\(entity.label)] \(entity.text)")
                        .font(.headline)
                    Text(String(format: "Confidence: %.2f", entity.confidence))
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
            }
        }
        .padding()
        .frame(minWidth: 600, minHeight: 400)
    }
    
    private func analyze() async {
        isProcessing = true
        errorMessage = nil
        
        do {
            // Use MLX path for Apple Silicon Macs
            let modelDir = try await OpenMedModelStore.downloadMLXModel(
                repoID: "OpenMed/OpenMed-PII-ClinicalE5-Small-33M-v1-mlx"
            )
            let analyzer = try OpenMed(backend: .mlx(modelDirectoryURL: modelDir))
            entities = try analyzer.extractPII(inputText)
        } catch {
            errorMessage = error.localizedDescription
        }
        
        isProcessing = false
    }
}

Summary

  • OpenMedKit integrates via Swift Package Manager using the repository URL https://github.com/maziyarpanahi/openmed.
  • Two backends are available: MLX (Apple Silicon accelerated, downloads from Hugging Face) and CoreML (bundled offline models).
  • Key source files include OpenMedBackend.swift for runtime selection, OpenMedModelStore.swift for MLX downloads, and OpenMedKit.swift for the public API.
  • Primary methods are extractPII(_:confidenceThreshold:) and analyzeText(_:confidenceThreshold:), returning [EntityPrediction] arrays.
  • Post-processing logic in PostProcessing.swift handles entity merging and span repair automatically.

Frequently Asked Questions

Does OpenMedKit support Intel-based Macs?

Yes, but only with the CoreML backend. The MLX backend requires Apple Silicon for acceleration. For Intel Macs or macOS Simulator testing, bundle a CoreML model (.mlmodelc) and initialize using OpenMed(backend: .coreML(modelURL:id2labelURL:tokenizerName:)).

How large are the MLX model downloads?

The MLX artifacts are downloaded once per model and cached locally by OpenMedModelStore.downloadMLXModel(repoID:). The specific size depends on the Hugging Face repository (e.g., OpenMed-PII-ClinicalE5-Small-33M-v1), but typical clinical NLP models range from 100MB to 300MB compressed. Subsequent app launches use the cached local copy.

Can I use OpenMedKit for general NER beyond PII extraction?

Yes. While extractPII(_:) is optimized for personally identifiable information, the analyzeText(_:confidenceThreshold:) method performs general clinical named entity recognition. The underlying tokenizer and model configuration in OpenMedKit.swift support any token-classification model compatible with the OpenMed architecture.

Is internet connectivity required after model download?

No. Once the MLX model is cached locally or a CoreML model is bundled, all inference runs completely offline on-device. The library performs tokenization, model inference, and post-processing (span repair and entity merging) without network calls, ensuring HIPAA-compliant on-device processing for sensitive clinical text.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →