Giter Club home page Giter Club logo

swift-smart's Introduction

Swift-SMART is a full client implementation of the 🔥FHIR specification for building apps that interact with healthcare data through SMART on FHIR. Written in Swift 5.0 it is compatible with iOS 11 and macOS 10.13 and newer and requires Xcode 10.2 or newer.

Versioning

Due to the complications of combining two volatile technologies, here's an overview of which version numbers use which Swift and FHIR versions.

  • The master branch should always compile and is on (point releases of) these main versions.
  • The develop branch should be on versions corresponding to the latest freezes and may be updated from time to time with the latest and greatest CI build.

See tags/releases.

Version Swift FHIR  
4.2 5.0 Package 4.0.0-a53ec6ee1b R4
4.1 5.0 4.0.0-a53ec6ee1b R4
4.0 4.2 4.0.0-a53ec6ee1b R4
3.2 3.2 3.0.0.11832 STU 3
3.0 3.0 3.0.0.11832 STU 3
2.9 3.0 1.6.0.9663 STU 3 Ballot, Sep 2016
2.8 3.0 1.0.2.7202 DSTU 2 (+ technical errata)
2.4 2.2 1.6.0.9663 STU 3 Ballot, Sep 2016
2.3 2.3 1.0.2.7202 DSTU 2 (+ technical errata)
2.2.3 2.2 1.0.2.7202 DSTU 2 (+ technical errata)
2.2 2.0-2.2 1.0.2.7202 DSTU 2 (+ technical errata)
2.1 2.0-2.2 1.0.1.7108 DSTU 2
2.0 2.0-2.2 0.5.0.5149 DSTU 2 Ballot, May 2015
1.0 1.2 0.5.0.5149 DSTU 2 Ballot, May 2015
0.2 1.1 0.5.0.5149 DSTU 2 Ballot, May 2015
0.1 1.0 0.0.81.2382 DSTU 1

Resources

QuickStart

See the programming guide for more code examples and details.

The following is the minimal setup working against our reference implementation. It is assuming that you don't have a client_id and on first authentication will register the client with our server, then proceed to retrieve a token. If you know your client-id you can specify it in the settings dict. The app must also register the redirect URL scheme so it can be notified when authentication completes.

import SMART

// create the client
let smart = Client(
    baseURL: URL(string: "https://fhir-api-dstu2.smarthealthit.org")!,
    settings: [
        //"client_id": "my_mobile_app",       // if you have one
        "redirect": "smartapp://callback",    // must be registered
    ]
)

// authorize, then search for prescriptions
smart.authorize() { patient, error in
    if nil != error || nil == patient {
        // report error
    }
    else {
        MedicationOrder.search(["patient": patient!.id])
        .perform(smart.server) { bundle, error in
            if nil != error {
                // report error
            }
            else {
                var meds = bundle?.entry?
                    .filter() { return $0.resource is MedicationOrder }
                    .map() { return $0.resource as! MedicationOrder }
                
                // now `meds` holds all the patient's orders (or is nil)
            }
        }
    }
}

For authorization to work with Safari/SFViewController, you also need to:

  1. register the scheme (such as smartapp in the example here) in your app's Info.plist and
  2. intercept the callback in your app delegate, like so:
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ app: UIApplication, open url: URL,
        options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
        
        // "smart" is your SMART `Client` instance
        if smart.awaitingAuthCallback {
            return smart.didRedirect(to: url)
        }
        return false
    }
}

Installation

The suggested approach is to add Swift-SMART as a git submodule to your project. Find detailed instructions on how this is done on the Installation page.

The framework can also be installed via Carthage and is also available via CocoaPods under the name “SMART”.

License

This work is Apache 2 licensed: NOTICE.txt. FHIR® is the registered trademark of HL7 and is used with the permission of HL7.

swift-smart's People

Contributors

drdavec avatar p2 avatar xmlmodeling avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swift-smart's Issues

Using Client class in XCTest

I need to run some Xcode unit tests based on the XCTest class. In SMART_on_FHIR_iOSTests.swift you have a simple example

class ClientTests: XCTestCase {

    func testInit() {
        let client = Client(baseURL: "https://api.io", settings: ["cliend_id": "client", "redirect": "oauth://callback"])
        XCTAssertTrue(client.server.baseURL.absoluteString == "https://api.io/")

//      //XCTAssertNil(client.auth.clientId, "clientId will only be queryable once we have an OAuth2 instance")
        client.ready { error in
            XCTAssertNil(error)
        }
    }
}

I notice that the client_id parameter is misspelled to cliend_id. Is this intentional ?

Can this be used to test against one of the SMART-on-FHIR test servers such as fhir-open-api-dstu2.smarthealthit.org ?

If so, what would the input settings to Client init( baseURL:, settings:, title: ) be ?

Custom Headers for Client/Server

Is there a way to input custom headers when going through the Auth workflow? A few test servers I'm hitting still return XML by default.

Error: Failed to parse JSON: The data couldn’t be read because it isn’t in the correct format.

Making PatientListViewController.onPatientSelect() -> public

Describing a use case:

  1. Practitioner logs in on a Tablet (SMART iOS App)
  2. Practitioner Selects Patient.
  3. Practitioner /Patient do their thing .....
  4. Practitioner writes new FHIR Resources to Server for that Patient.
  5. Practitioner moves onto the next patient, requires selecting the patient from the list.
  6. repeat steps

In the second last step, is Practitioner re-authorization always required to select a new patient?

  • if Yes: simple way forward
  • if NO: I could use PatientListViewController instead of making my own. (onPatientSelect()) could be public.

What do you think?

Cannot authenticate with SMART FHIR sandbox server

Disclaimer: I am very early in learning the FHIR stack, so please consider this issue with a noob in mind.

When trying to authenticate with the Sandbox server, I am getting error:

CapabilityStatement.readFrom() did not return a CapabilityStatement instance but <Resource> https://fhir-api-dstu2.smarthealthit.org/

The SoF-Demo app (master and develop) is also running in to the same issue.

simulator screen shot apr 7 2017 3 28 19 pm

What am I doing incorrectly? Is the issue that the SMART server is dstu2 while the Swift SDK is targeting stu3?

EXC_BAD_ACCESS

Hi!

When using the SMART classes, I encounter a EXC_BAD_ACCESS.

bildschirmfoto 2018-05-10 um 14 13 12

This happens both in the Demo project (regardless of the data source) and with a small own test app:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myEndpoint = "https://hapi.fhir.org/baseDstu3"
        //var myFhirUrl = myEndpoint + "/Observation?patient=3352608&category=laboratory"
        
        let smart = Client(
            baseURL: URL(string: myEndpoint)!,
            settings: [:
            ]
        )
        
        smart.authProperties.granularity = .tokenOnly

        smart.authorize() { patient, error in
            if nil != error { //} || nil == patient {
            }
            else {
                Observation.search(["patient": "3352608", "category": "laboratory"])
                    .perform(smart.server) { bundle, error in
                        if nil != error {
                        }
                        else {
                        }
                }
            }
        }
    }
}

Swift Compiler Error

Hi when I try to build the current master, I keep getting a Swift Compiler Error:

Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1

Swift 4 Support

Need to migrate to Swift 4 version and new pod release for the same.

Simulator will not work

I downloaded the app and ran it in Xcode 9.3 with the build succeeding and without errors, however, no simulator appeared. I am brand new to SMART FHIR so I would appreciate any assistance you can offer.
Thanks !

Update authContext

The authContext in Auth+iOS.swift is set like this: authContext = UIApplication.shared.keyWindow?.rootViewController. This does not always work to get the top visible view controller and can result in a failure to present the authentication web view.

Expected authority at index 16 - Could not create URI

Hi, I could configure a demo app for iOS in the Smart sandbox. I am using STU3 and everything works fine with auth until the user taps to allow the app to access permissions. Then it shows this error :

simulator screen shot - iphone 6s - 2017-12-29 at 17 01 20

The redirectUrl is correct and the app in the sandbox contains the same url.

Thanks!

AppDelegate to handle OAuth Redirect in an OSX app

I hope this is an appropriate place to raise this issue.

I'm trying to build an OSX app using the Swift-SMART framework but I'm struggling to understand what I need to put in the AppDelegate to handle the OAuth Redirect. The sample app is for iOS but OSX would appear to require different code in the AppDelegate to intercept and manage the redirect.

Putting the URL Scheme in the plist is straightforward enough but the OSX app uses the NSApplicationDelegate protocol which supports different function implementations to UIApplicationDelegate. Is anyone able to provide a suitable AppDelegate for use with OSX apps which use the Swift-SMART framework?

Finally, I assume what is required in the AppDelegate differs depending on whether the OS browser (Safari) is used or the 'embedded' browser - if so, how does it differ?

John N

Login as patient without list.

I am probably missing something in the documentation; however, I'm having a lot of trouble figuring out how to log in as a patient and get the logged in patient's info. I've logged in via the browser popup and call authorize each time; however, there is no patient object for the patient id that I logged in with. How do I access that? This app cannot present a list of other patients and I need to only access the info of the user logging in.

image

Flow for Practitioner/Provider (User) Login

I am working on a standalone Practitioner Login through SMART-on-FHIR.

I was wondering what was the recommended way of a Practitioner user login flow. The SMART.Client Authorization works as expected, I do get the Patient as a resource but not the Practitioner.

At the moment I extended SMART.Server to support a callback with Patient and Practitioner, but that requires some tinkering within this codebase. Is there something I'm missing?

Problem with FHIR Token type parameters containing '|' (pipe) characters

I've managed to query the Cerner endpoints and pull back a patient using an '_id' I found in their Google group. Subsequently I tried using that same patients MRN to retrieve the patient using the 'identifier' search parameter and got an http 400 error back with a helpful message pointing out I need to supply the 'system' part of the identifier as well as the value part. Looking at Cerner's documentation, it suggested I need to quote an oid to identify the system part of the parameter value; eg "urn:oid:1.1.1.1.1.1|10002701".

When I tried this, Swift-SMART declined to make the url call at all. After some tracing through the code I discovered it was being prevented by the call to absoluteURL() in FHIROpenServer -

func absoluteURL(for path: String, handler: FHIRServerRequestHandler) -> URL? {
return URL(string: path, relativeTo: baseURL)

This was returning nil, presumably because the url contains unencoded reserved characters. So I sort of conclude that some part of url construction in Swift-SMART is not encoding all relevant characters. To test this hypothesis, I substituted %7C for the pipe character in the call to Patient.search as follows-
Patient.search(["identifier": "urn:oid:1.1.1.1.1.1%7C10002701"]).perform(...

. . . .and it worked - it quite happily retrieved the required patient.

So the question is - is this an issue with parameter/url encoding in Swift-SMART or is there a syntax for expressing this search in the Patient.search initialiser which might avoid the problem?

john n

I can't get authenticated

Whenever I try to authorize it's giving me an invalid address error.

Here is my func

func testSomeClasses(){
print("testSomeClasses")
let smart = Client(
baseURL: "https://fhir-api-dstu2.smarthealthit.org",
settings: [
//"client_id": "my_mobile_app",
"redirect": "smartapp://callback", // must be registered
]
)
print("testSomeClasses 2")
smart.authProperties.embedded = true
smart.authProperties.granularity = .TokenOnly // same with .PatientSelectNative
print("testSomeClasses 3")
smart.authorize() { patient, error in
print("testSomeClasses 4")
if let error = error {
// there was an error
print("testSomeClasses error ", error)
}
else {
print("hello")
print(patient?.name)
// patient is a "Patient" resource on success, unless you've used
// the TokenOnly granularity
}
}

}

Console prints:
testSomeClasses
testSomeClasses 2
testSomeClasses 3

screen shot 2016-09-14 at 4 31 39 pm

Search related EXC_BAD_ACCESS

Facing an EXC_BAD_ACCESS, something to do when only using .search() query on a QuestionnaireResponse with ValueSetCompose. This is strange, .read() works just fine for the same resource, but .search() is a problem. (STU3)

let server = Server(baseURL: URL(string: "https://r3.smarthealthit.org/")!)

QuestionnaireResponse.read("161921", server: server) { (resource, error) in
 }

QuestionnaireResponse.search(["_id" : "SMART-PROMs-1-QR4"]).perform(server) { (bundle, error) in
}

//ERROR
QuestionnaireResponse.search(["_id" : "161921"]).perform(server) { (bundle, error) in
             
}

note: QR: 161921 might not exist every day: create this resource this to check against

Refresh token

Hi, I'm working on a Practitioner facing app for iOS. The problem is that after 10 minutes, the requests start failing because of authentication. Is there a way to refresh the access token?

Thanks in advance!
Fede.

'$' is not an identifier; use backticks to escape it

When installing Swift-SMART 3.2.0 with cocoapods, the following error appears and the project does not build. The file is FHIROperation.swift, line 184:

/// Alias to FHIROperation for neat operation usage.
public typealias $ = FHIROperation

The message is the following: '$' is not an identifier; use backticks to escape it.

This does not happen when installing as embedded framework.

Incorrect navigation during authentication

For SMART sandbox sometimes, there was weird behavior during authentication.
Steps

  1. Call authentication on smart client.
  2. Web view will open asking for credentials.
  3. After successful login, instead of showing next screen ie. approval asking for access to data. it comes back to app and then push blank viewcontroller.
  4. It does not present patient list even after I have selected granularity as patientSelectNative for client.

On removing app from simulator/device and installing fresh build it works fine.
I think there is some issue with token u r storing for webservice calls.

Error when using _summary=count

I have a Server running with a couple of images on it. When I run the following code with postman:

....Media?modality:text=Sonstige&_summary=count

I am shown the amount of images I have with the modality:text=Sonstige

{
  "resourceType": "Bundle",
  "id": "8e41e384-6c1a-4da2-919f-b6b4622a8b7e",
  "meta": {
    "lastUpdated": "2020-03-14T12:37:26.558+00:00",
    "tag": [
      {
        "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue",
        "code": "SUBSETTED",
        "display": "Resource encoded in summary mode"
      }
    ]
  },
  "total": 2
}

But when I try to replicate the same call in smart-swift, I get an Error in my client application.
I am using this call:
let search = Media.search(["modality":["$text": type],"_summary": "count"])
and I get the following Error in the Xcode Debugger:

ERROR

Optional(Failed to validate resource: Bundle.type: mandatory property “type” is missing)

When I call: print(search.construct()) the generated call is the same I used in postman:

Media?_summary=count&modality:text=Sonstige

To me, it looks like swift-SMART can't handle the server response, because the returned Bundle does not have a resource type, but this type of search does in fact not have one.
Is there an other way to use the _summary - search modifier in swift-SMART?

DateTime wrapper rounds to nearest second

Given this example

func testExample() throws {
  let start = DateTime(
    date: FHIRDate(string: "1990-08-17")!,
    time: FHIRTime(hour: 12, minute: 8, second: 20.99999),
    timeZone: TimeZone.current
  )
  print("Start date as json: \(start.description)")
  print("Start date as NSDate: \(start.nsDate)")
}

I get the following output

Start date as json: 1990-08-17T12:08:21+02:00
Start date as NSDate: 1990-08-17 10:08:20 +0000

I'm wondering why the DateTime rounds up to the nearest second (it has a precision of 4 decimals of milliseconds), when the NSDate doesn't. Is this an issue (it could potentially mean that something happen a second earlier/later then expected)?

I'm using Swift-SMART version 4.2.0

Current Practitioner

Hi, I'm working on a Practitioner facing app. I can log in as a practitioner and select a patient. I can get the information about the selected patient but I do not know how to access the current logged in Practitioner information.

Is there a way to achieve this?

Thanks!

library not loaded on IPhoneOS

I import Swift-SMART into my project with Installation in a wiki.

But happen error like this:
2017-07-26 10 20 09

Finally, I uncheck Copy only when installing in Embed Frameworks section,
The problem has resolved.

Processing Bundled resources

Using the code below, I've been trying to understand how to process a bundle of resources (which for test purposes I've loaded from a file). The bundle in question contains a Composition and a set of other resources which the Composition references internally. Stepping through the code in debug, at step //1 above I appear to have a valid Bundle resource (cofeolBundle). In it I can see its 'entry' array contains 62 SMART.BundlEntry(s). These represent the expected resources in the bundle, the first of which I know to be a Composition resource (through visual inspection of the json file).

At step //2, the XCode variables view shows 'anentry' is of type BundleEntry and further inspection of its 'resource' element shows it has the expected content of a Composition resource; attester, author, confidentiality, custodian, date, encounter etc etc. I can even see its 'section' array - which has 10 elements.

Clearly XCode is able to discern that anentry is a Composition resource but here's the problem - I dont seem to be able to refer to the anentry.resource.resourceType element in code!! This begs the question of how I step through and process this Composition and its associated bundled resources if I cant determine what type of resource is present in each BundleEntry?

I assume I'm missing some fundamental concept - either about Swift-FHIR or about how FHIR resource bundles should be processed in general. Any suggestions and observations would be much appreciated - its driving me nuts!

    let filename = "/Users/johnneal/somefile.json"
    let path: URL = URL(fileURLWithPath: filename)
    do {
        let data2 = try Data(contentsOf: path)
        do {
            let cofeolJSON = try JSONSerialization.jsonObject(with: data2, options: []) as? [String: Any]
        
            // 1
	let cofeolBundle = Bundle(json: cofeolJSON as FHIRJSON!)
	// 1

	// 2
            let anentry = cofeolBundle.entry![0]
            // 2          
        
        }
        catch{}
    }
    catch{}

Reference resolution for Composition Bundle

Sorry if this is a naive newbie question. I'm loading a Composition bundle from a file but when processing the sections, I'm running into problems resolving the section references to other resources in the bundle. In the Bundle I'm provided with, the references in the Sections have the form "resource_type/guid" - eg
"reference": "Condition/98d29b5a-da2b-4f48-99b8-53924642c9fd".

If I call the resolve() method on the section.entry[n], eg;

sec.entry![0].resolve(SMART.Condition, callback: { (resolvedResource: Resource?) -> Void in

I get the following from SwiftFHIR -

SwiftFHIR [Reference+Resolving.swift:128] resolve(_:callback:) WARNING: reference does not have a server instance, cannot resolve «Condition/e3d96a2e-abbe-4e78-93dd-e5870fc4f796»

Is this because the bundle has been loaded directly from a file - using bundle.instantiate() - rather than being loaded using the SMART.Client approach etc???

Removing the 'Condition/' from the reference in the file JSON makes no difference - the resolve() method still expects to find a server associated with the bundle.

So, is this just a current constraint of the Swift-FHIR library when processing file based bundles?

Investigating this also made me wonder how I would set the type: parameter of the resolve() method IF the reference only contained a GUID reference to the bundled resource. The Composition.section.references I'm provided with are of the form below but if they didn't have the 'resource type' part how would I set the 'expected type' parameter in the call to resolve() ???

"entry": [
{
"reference": "Condition/e3d96a2e-abbe-4e78-93dd-e5870fc4f796",
"display": "COFE-Diagnosis-Condition-Example-1"
},
{
"reference": "Condition/98d29b5a-da2b-4f48-99b8-53924642c9fd",
"display": "COFE-Diagnosis-Condition-Example-2"
}
]

And, by extension, does this mean that for a file based bundle, the section references should really be provided as 'contained' references/resources using the # prefix?

As always, the wisdom of the experienced would be appreciated
john

Swift 3

Any intentions on going to Swift 3? If so, any ETA?

Thanks!

Asking again credentials while refreshing the Token Using Smart client object.

Hi
I am doing a Cerner login using this library.
It's working fine and I am getting the idToken using (smartClient?.server.idToken).

But after 10 min when I am trying to refresh the token I am using the same function i.e.
smartClient.authorize(callback: { ( response, err ) in
})

but when this method gets called for refreshing the token
It's asking us again to entering the Cerner credentials(username, password )
and then updating the token.
But that is not correct, It should refresh the idToken or accessToken without asking the credentials again.

And when I am trying to refreshing the token after 9 mins then it's returning the same as previous because it's still a valid token.

So, Is there any way to refresh the token without asking for credentials again?

Thanks in advance !!!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.