FHIRModels

repository·main·Indexed 42 days ago

https://github.com/apple/fhirmodels

A Swift library providing native, type-safe data models for Fast Healthcare Interoperability Resources (FHIR). It supports multiple FHIR versions (DSTU2, STU3, R4, R4B, R5) and integrates with Swift's type system through Codable and Sendable conformance. Features include ResourceProxy for dynamic instantiation, a specialized fhirModelsReadyDecoder for secure decoding, and support for FHIR primitives, extensions, and Value[x] types.

Tokens
3.9K
Snippets
12
Records
18
Agent score
53%

What's inside FHIRModels

  1. Overview of FHIRModels features and requirements

    main

    FHIRModels provides native Swift representations of FHIR resources, elements, and data types.

    Key Features

    • Versioned Targets: Separate targets are available for different FHIR versions: DSTU2, STU3, R4, R4B, R5, and latest build versions.
    • Type Safety: Enforced non-nullability for mandatory parameters and enums for most closed code systems.
    • Value[x] Support: Enums are used to support FHIR value[x] types.
    • Data Handling: Includes Date/Time parsing, validation, and conversion to/from NSDate.
    • Swift Integration: All models conform to Codable and Sendable.

    Requirements

    • Swift Version: 5.7 or newer.
    • Platforms: macOS, iOS, watchOS, tvOS, and Linux.
  2. Work with FHIRPrimitive types and extensions

    main

    FHIR Resource and Element types are Swift classes, while primitive data types (like FHIRString, FHIRBool, DateTime) are structs wrapped in a FHIRPrimitive<T> container.

    Best Practice: Instead of extracting the native Swift type (e.g., String) and passing it around, pass the full FHIRPrimitive object. This ensures that any FHIR extensions attached to the element are preserved.

    Even when using FHIRPrimitive, you can still perform comparisons against native types directly.

    // Instead of this:
    let id = resource.id?.value?.string
    
    // Do this to preserve extensions:
    // Use the FHIRPrimitive directly for comparisons:
    if resource.id == "101" { 
        // ...
    }
  3. Handle 'Value-X' properties using enums

    main

    In FHIR, some properties are mutually exclusive (e.g., a property might be either a Reference or a CodeableConcept). These are known as "Value-X" properties.

    In FHIRModels, these are represented as Swift enums to enforce exclusivity at the type level. To access the data, use pattern matching (if case or switch) to determine which variant of the enum is present.

    // Example: Handling MedicationStatement.medication (which can be a reference or a codeable concept)
    let med = try decoder.decode(MedicationStatement.self, from: data)
    
    if case .reference(let ref) = med.medication {
        print("--> medication reference display: \(ref.display ?? "{nil}")")
    } else if case .codeableConcept(let cc) = med.medication {
        print("--> medication codeable concept text: \(cc.text ?? "{nil}")")
        // Note: you should also look at `cc.coding`
    }
  4. Implement custom extension accessors via Swift extensions

    main

    Since extension values are often FHIR Primitives, the recommended pattern for working with extensions is to write a Swift extension on the relevant FHIR type (or FHIRPrimitive). This encapsulates the logic of searching for the extension URL and unwrapping its value.

    When implementing an accessor:

    1. Use extensions(for: "URL") to find the specific extension.
    2. Use pattern matching (e.g., if case .string(let str) = ...) to safely unwrap the extension's value.
    3. Return the underlying primitive value or nil if the extension is missing or of an unexpected type.
    extension FHIRPrimitive {
        var mothersFamilyName: String? {
            guard let mothersFamilyExtension = extensions(for: "http://hl7.org/fhir/StructureDefinition/humanname-mothers-family").first else {
                return nil
            }
            if case .string(let str) = mothersFamilyExtension.value {
                return str.value?.string
            }
            return nil
        }
    }
  5. Decode to a known Resource type

    main

    If the resource type is already known, you can decode the JSON data directly into that specific model type using a JSONDecoder.

    import ModelsR4
    
    let data = <FHIR JSON data>
    let decoder = JSONDecoder.fhirModelsReadyDecoder()
    do {
        let resource = try decoder.decode(Patient.self, from: data)
    } catch {
        print("Failed to instantiate Patient: \(error)")
    }
  6. Instantiating FHIR Models

    main
    Learn how to create instances of FHIR models within the fhirmodels framework. This guide covers the patterns for initializing different resource types and ensuring required fields are populated according to FHIR specifications.
  7. Install FHIRModels via Swift Package Manager

    main

    FHIRModels is best added to your project using Swift Package Manager (SPM).

    Using Xcode

    1. In Xcode 11 or newer, go to File » Add Package Dependencies...
    2. Search for FHIRModels or paste the GitHub URL: https://github.com/apple/FHIRModels.git
    3. Select your versioning strategy (e.g., "up to next major").
    4. To update, use File » Packages » Update to Latest Package Versions.

    Using Package.swift

    Add the following to your dependencies array:

    .package(url: "https://github.com/apple/FHIRModels.git", .upToNextMajor(from: "0.9.3"))
    dependencies: [
        .package(url: "https://github.com/apple/FHIRModels.git",
                .upToNextMajor(from: "0.9.3"))
    ]
  8. Instantiate FHIR models using ResourceProxy

    main

    If you have JSON data but do not know the specific resource type beforehand, use ResourceProxy. The ResourceProxy inspects the resourceType field in the JSON and automatically instantiates the correct class.

    You can access the underlying resource in several ways:

    • Use .get() to retrieve the resource as a generic Resource type.
    • Use .get(if: Type.self) to attempt to retrieve the resource as a specific type (returns an optional).
    • Use pattern matching (e.g., if case .patient(let patient) = proxy or switch proxy) to extract the typed resource.
    import ModelsR4
    
    let data = <FHIR JSON data>
    let decoder = JSONDecoder.fhirModelsReadyDecoder()
    do {
        let proxy = try decoder.decode(ResourceProxy.self, from: data)
        
        let resource = proxy.get()
        // `resource` is a generic `Resource`
        
        let patient = proxy.get(if: Patient.self)
        // `patient` is a nullable `Patient`
        
        if case .patient(let patient) = proxy {
            // `patient` is a `Patient`
        }
        
        switch proxy {
        case .patient(let patient):
            // `patient` is a `Patient`
            break
        default:
            break
        }
    } catch {
        print("Failed to instantiate: \(error)")
    }