Artigo Guillaume Rongier · jan 11, 2021 9m read

iOS, FHIR e IRIS for Health

Swift-FHIR-Iris

Aplicativo iOS para exportar dados HealthKit para o InterSystems IRIS for Health (ou qualquer repositório FHIR)

main

Índice

Objetivo desta demonstração

O objetivo é criar uma demonstração de ponta a ponta do protocolo FHIR.

O que quero dizer com de ponta a ponta é, de uma fonte de informação como um iPhone. Colete seus dados de saúde no formato Apple (HealthKit), transforme-os em FHIR e envie para o repositório InterSystems IRIS for Health.

Essas informações devem ser acessíveis por meio de uma interface web.

iPhone -> InterSystems FHIR -> Página Web.

Como executar esta demonstração

Pré-requisitos

  • Para a parte do cliente (iOS)
    • Xcode 12
  • Para o servidor e aplicativo web
    • Docker

Instale o Xcode

Não há muito o que falar aqui, abra a AppStore, procure por Xcode, instale.

Abra o projeto SwiftUi

Swift é a linguagem de programação da Apple para iOS, Mac, Apple TV e Apple Watch. É o substituto do objective-C.

Clique duas vezes em Swift-FHIR-Iris.xcodeproj

Abra o simulador clicando na seta superior esquerda.

xcode

Configure o simulador

Vá em Health

Clique em Steps

Add Data

simulator

Inicie o servidor InterSystems FHIR

Na pasta raiz deste git, execute o seguinte comando:

docker-compose up -d

No final do processo de construção, você será capaz de se conectar ao repositório FHIR:

http://localhost:32783/fhir/portal/patientlist.html

portal

Este portal foi feito por @diashenrique.

Com algumas modificações para lidar com os passos de atividade da Apple.

Brinque com o aplicativo iOS

O aplicativo primeiro solicitará que você aceite o compartilhamento de algumas informações.

Clique em authorize

authorize

Então você pode testar o servidor FHIR clicando em 'Save and test server'

As configurações padrão apontam para a configuração do docker.

Se tiver sucesso, você pode inserir as informações do seu paciente.

Nome, Sobrenome, Aniversário, Gênero.

Salve o paciente para FHIR. Um pop-up mostrará seu ID FHIR único.

savepatient

Consulte este paciente no portal:

Acesse: http://localhost:32783/fhir/portal/patientlist.html

Podemos ver aqui, que há um novo paciente "toto" com 0 atividades.

patient portal

Envie suas atividades:

Volte para o aplicativo iOS e clique em Step count

Este painel resume a contagem de passos da semana. No nosso caso, 2 entradas.

Agora você pode enviá-los para o InterSystems IRIS FHIR clicando em enviar.

ios send

Consulte as novas atividades no portal:

Podemos ver agora que o Toto tem duas novas observações e atividades.

portal activites

Você pode eventualmente clicar no botão do gráfico para exibi-lo como um gráfico.

portal charts

Como funciona

iOS

A maior parte desta demonstração é construída em SwiftUI.

https://developer.apple.com/xcode/swiftui/

Que é o framework mais recente para iOS e co.

Como verificar a autorização para dados de saúde

Ele está na classe SwiftFhirIrisManager.

Esta classe é um singleton e estará carregando todo o aplicativo com a anotação @EnvironmentObject.

Mais informações em: https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

O método requestAuthorization:

    // Solicita autorização para acessar o HealthKit.
func requestAuthorization() {
// Solicitando autorização.
/// - Tag: RequestAuthorization
    let writeDataTypes: Set<HKSampleType> = dataTypesToWrite()
    let readDataTypes: Set<HKObjectType> = dataTypesToRead()

    // pedido de autorização
    healthStore.requestAuthorization(toShare: writeDataTypes, read: readDataTypes) { (success, error) in
        if !success {
            // Trata o erro aqui.
        } else {

            DispatchQueue.main.async {
                self.authorizedHK = true
            }

        }
    }
}

Onde healthStore é o objeto de HKHealthStore().

O HKHealthStore é como um banco de dados de dados de saúde no iOS.

dataTypesToWrite e dataTypesToRead são os objetos que gostaríamos de consultar no banco de dados.

A autorização precisa de um propósito e isso é feito no arquivo xml Info.plist adicionando:

    <key>NSHealthClinicalHealthRecordsShareUsageDescription</key>
<string>Read data for IrisExporter</string>
<key>NSHealthShareUsageDescription</key>
<string>Send data to IRIS</string>
<key>NSHealthUpdateUsageDescription</key>
<string>Write date for IrisExporter</string>

Como conectar a um repositório FHIR

Para esta parte usei o pacote FHIR do Smart-On-FHIR: https://github.com/smart-on-fhir/Swift-FHIR

A classe usada é a FHIROpenServer.

    private func test() {
    progress = true

    let url = URL(string: self.url)

    swiftIrisManager.fhirServer = FHIROpenServer(baseURL : url! , auth: nil)

    swiftIrisManager.fhirServer.getCapabilityStatement() { FHIRError in

        progress = false
        showingPopup = true

        if FHIRError == nil {
            showingSuccess = true
            textSuccess = "Connected to the fhir repository"
        } else {
            textError = FHIRError?.description ?? "Unknow error"
            showingSuccess = false
        }

        return
    }

}

Isso cria um novo objeto fhirServer no singleton swiftIrisManager.

Em seguida, usamos getCapabilityStatement()

Se pudermos recuperar a declaração de capacidade do servidor FHIR, isso significa que nos conectamos com sucesso ao repositório FHIR.

Este repositório não está em HTTPS, por padrão, a apple bloqueia este tipo de comunicação.

Para permitir o suporte HTTP, o arquivo xml Info.plist pode ser editado assim:

    <key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>

Como salvar um paciente no repositório FHIR

Operação básica verificando primeiro se o paciente já existe no repositório

Patient.search(["family": "(self.lastName)"]).perform(fhirServer)

Isso pesquisa por paciente com o mesmo sobrenome.

Aqui, podemos imaginar outros cenários, como token Oauth2 e JWT para unir o patientid e seu token. Mas para esta demonstração, mantemos as coisas simples.

Em seguida, se o paciente existe, nós o recuperamos, caso contrário, criamos o paciente:

    func createPatient(callback: @escaping (Patient?, Error?) -> Void) {
// Cria um novo paciente
let patient = Patient.createPatient(given: firstName, family: lastName, dateOfBirth: birthDay, gender: gender)
    patient?.create(fhirServer, callback: { (error) in
        callback(patient, error)
    })
}

Como extrair dados do HealthKit

Isso é feito consultando o healthkit Store (HKHealthStore())

Aqui nós estamos consultando os passos.

Prepare a consulta com o predicado.

        //Semana passada
let startDate = swiftFhirIrisManager.startDate
//Agora
let endDate = swiftFhirIrisManager.endDate
    print("Collecting workouts between \(startDate) and \(endDate)")

    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)

Em seguida, a própria consulta com seu tipo de dados (HKQuantityType.quantityType(forIdentifier: .stepCount)) e o predicado.

func queryStepCount(){
    //Semana passada
    let startDate = swiftFhirIrisManager.startDate
    //Agora
    let endDate = swiftFhirIrisManager.endDate

    print("Collecting workouts between \(startDate) and \(endDate)")

    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)

    let query = HKSampleQuery(sampleType: HKQuantityType.quantityType(forIdentifier: .stepCount)!, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { (query, results, error) in

        guard let results = results as? [HKQuantitySample] else {
               return
        }

        process(results, type: .stepCount)

    }

    healthStore.execute(query)

}

Como transformar dados HealthKit em FHIR

Para esta parte, usamos o pacote Microsoft HealthKitToFHIR

https://github.com/microsoft/healthkit-to-fhir

Este é um pacote útil que oferece factories para transformar HKQuantitySample em FHIR Observation

     let observation = try! ObservationFactory().observation(from: item)
let patientReference = try! Reference(json: ["reference" : "Patient/(patientId)"])
observation.category = try! [CodeableConcept(json: [
"coding": [
[
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "activity",
"display": "Activity"
]
]
])]
observation.subject = patientReference
observation.status = .final
print(observation)
observation.create(self.fhirServer,callback: { (error) in
if error != nil {
completion(error)
}
})

Onde item é um HKQuantitySample, em nosso caso, um tipo stepCount.

O factory faz a maioria do trabalho de converter 'unit' e 'type' para FHIR codeableConcept e 'value' para FHIR valueQuantity.

A referência ao PatientId é feita manualmente, lançando uma referência json fhir.

let patientReference = try! Reference(json: ["reference" : "Patient/(patientId)"])

O mesmo é feito para a categoria:

      observation.category = try! [CodeableConcept(json: [
"coding": [
[
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "activity",
"display": "Activity"
]
]
])]

Por fim, a observação é criada no repositório fhir:

      observation.create(self.fhirServer,callback: { (error) in
if error != nil {
completion(error)
}
})

Backend (FHIR)

Não há muito a dizer, é baseado no modelo fhir da comunidade InterSystems:

https://openexchange.intersystems.com/package/iris-fhir-template

Frontend

É baseado no trabalho de Henrique, que é um bom front end para repositórios FHIR feitos em jquery.

https://openexchange.intersystems.com/package/iris-fhir-portal