Contents

hotwheels93/swift-medikationsplan

A Swift library for parsing the **Bundeseinheitlicher Medikationsplan (BMP)** — the standardized German medication plan as defined in § 31a SGB V.

Installation

In Xcode: File > Add Package Dependencies... and enter the following URL:

https://github.com/Hotwheels93/swift-medikationsplan

Or add it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/Hotwheels93/swift-medikationsplan", from: "1.0.0")
]

Usage

import MedikationsplanKit // from swift-medikationsplan package

let parser = BMPParser()
let plan = try parser.parse(xmlString: xmlContent)

// Patient
print(plan.patient.fullName)           // "Dr. Michaela von Musterhausen (Freifrau)"
print(plan.patient.formattedBirthDate) // "13.12.1936"

// Author (printing entity)
print(plan.author.name)               // "Praxis 2"
print(plan.author.formattedAddress)    // "Hauptstraße 55, 01234 Am Ort"

// Clinical parameters
if let params = plan.parameters {
    print(params.allergies)            // "Penicillin"
    print(params.weight)               // 85.0
}

// Medications
for section in plan.sections {
    print(section.displayTitle)        // e.g. "Dauermedikation"

    for entry in section.entries {
        switch entry {
        case .medication(let med):
            print(med.displayName)         // "Aspirin 100"
            print(med.dosageDescription)   // "1 - 0 - 1 - 0 Stück"
            print(med.ingredientsSummary)  // "Acetylsalicylsäure 100 mg"
            print(med.weekday?.shortName)  // "Mo" (for weekly medications)
        case .freeText(_, let text):
            print(text)
        case .recipe(let recipe):
            print(recipe.text)
        }
    }
}

In addition to strings, raw Data can be parsed directly:

let data: Data = ... // e.g. from a Data Matrix scan
let plan = try parser.parse(data: data)

Data Model

| Type | Description | |------|-------------| | Medikationsplan | Root element containing version, patient, author, and sections | | Patient | First name, last name, date of birth, gender, insurance ID (eGK) | | Author | Printing entity (LANR, IDF, KIK, address, contact) | | ClinicalParameters | Allergies, pregnancy, weight, height, creatinine | | MedicationSection | Section with section code (411–425) | | Medication | PZN, name, dosage form, dosage schedule, active ingredients | | ActiveIngredient | Active ingredient name and strength | | Recipe | Compounding recipe text | | SectionCode | Enum of all section codes (on-demand, permanent, self-medication, …) | | DosageUnit | Enum of all dosage units per Appendix 4 of the spec | | Weekday | Weekday for weekly medication schedules (v2.8) |

All types conform to Sendable, Codable, and Equatable, and are safe for use with Swift Concurrency.

Note: Display names for section codes, dosage units, weekdays, and gender are provided in German, as they originate from the BMP specification.

Serialization (Models → XML)

BMPSerializer converts a Medikationsplan back into BMP XML:

let xml = BMPSerializer.serialize(plan)

Combined with parsing, this enables full roundtrip: XML → models → XML.

PZN Validation

The PZN struct validates German Pharmazentralnummern (8-digit identifiers with a check digit):

let pzn = PZN("06453257")
print(pzn?.isValid)  // true

let pzn2 = PZN(number: 544786)
print(pzn2?.digits)  // "00544786" (zero-padded)

// Static convenience
PZN.isValid("06453257")  // true

The check digit algorithm uses a weighted sum modulo 11 as specified by IFA.

PZN Codes

Medications in the BMP are identified by their Pharmazentralnummer (PZN) — a German pharmaceutical registration number. MedikationsplanKit provides the PZN as an Int value but does not include a database to resolve PZN codes to medication details (trade name, package size, price, etc.). You will need an external PZN database or API for this, such as:

JSON Serialization (Codable)

All model types conform to Codable, enabling JSON serialization:

let json = try JSONEncoder().encode(plan)
let decoded = try JSONDecoder().decode(Medikationsplan.self, from: json)

This is useful for caching, persistence, or transmitting medication plans as JSON.

Error Handling

The parser throws BMPParserError:

do {
    let plan = try parser.parse(xmlString: xml)
} catch let error as BMPParserError {
    switch error {
    case .invalidData:
        // Data could not be read as UTF-8
    case .xmlParsingFailed(let detail):
        // Invalid XML
    case .missingRequiredField(let field):
        // A required field is missing (e.g. patient, author)
    case .unsupportedVersion(let version):
        // BMP version not supported
    }
}

FHIR R4 Transformation

The optional MedikationsplanFHIR module converts a parsed Medikationsplan into a FHIR R4 Document Bundle using Apple's FHIRModels library.

Add MedikationsplanFHIR as an additional product dependency:

.product(name: "MedikationsplanFHIR", package: "swift-medikationsplan")
import MedikationsplanKit
import MedikationsplanFHIR

let plan = try BMPParser().parse(xmlString: xml)
let bundle = MedikationsplanFHIRConverter.convert(plan)

// Encode as FHIR JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let json = try encoder.encode(bundle)
let plan = try MedikationsplanFHIRConverter.convert(bundle)

The roundtrip BMP → FHIR → BMP is lossless. All BMP-specific data (section codes, free text entries, recipes, namePrefix/nameSuffix, formCode, additionalLine, printDate, BMP version/instanceID/language) is preserved via FHIR extensions.

The converter produces a FHIR Document Bundle containing:

| BMP | FHIR Resource | |-----|---------------| | Patient | Patient (name, birthDate, gender, eGK identifier) | | Author (LANR) | Practitioner (LANR identifier, address, telecom) | | Author (KIK/IDF) | Organization (IK/IDF identifier, address, telecom) | | Medication | Medication (PZN coding, ingredients, form) + MedicationStatement (dosage with timing codes) | | Free Text | Basic resource with BMP extensions | | Recipe | Basic resource with BMP extensions | | Allergies | AllergyIntolerance (one per allergy entry) | | Weight/Height/Creatinine | Observation (LOINC-coded, UCUM units) | | Pregnancy/Breastfeeding | Observation (LOINC-coded) | | Document structure | Composition (LOINC 77603-9, sections for medications, allergies, clinical parameters) |

Standard coding systems are used: LOINC, UCUM, PZN (http://fhir.de/CodeSystem/ifa/pzn), KVID-10 (http://fhir.de/CodeSystem/identifier-type-de-basis), LANR (https://fhir.kbv.de/NamingSystem/KBV_NS_Base_ANR).

Note: MedikationsplanKit has no external dependencies. The FHIRModels dependency is only pulled in when you use MedikationsplanFHIR.

Platforms

  • iOS 15+
  • macOS 12+

Sources

This implementation is based on the official BMP specifications published by the KBV (Kassenärztliche Bundesvereinigung):

Disclaimer

This software is provided "as is", without warranty of any kind, express or implied. Use at your own risk. The authors assume no liability for any damages arising from the use of this software, in particular for damages resulting from incorrect interpretation of medication data.

This library does not constitute medical or pharmaceutical advice. Parsed data should always be verified against the original data on the printed medication plan.

License

MIT License. See LICENSE.

Package Metadata

Repository: hotwheels93/swift-medikationsplan

Default branch: main

README: README.md