-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1196 lines (1174 loc) · 52.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.16">
<meta name="application-name" content="data4life">
<meta name="description" content="PHDP SDK Reference documentation">
<meta name="keywords" content="SDK iOS Swift FHIR">
<meta name="author" content="© 2019 D4L data4life gGmbH. All rights reserved.">
<title>PHDP SDK reference documentation</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<link rel="stylesheet" href="styles/asciidoctor.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css">
</head>
<body class="book toc2 toc-left">
<div id="header">
<h1>PHDP SDK reference documentation</h1>
<div class="details">
<span id="author" class="author">© 2019 D4L data4life gGmbH. All rights reserved.</span><br>
<span id="revnumber">version v1.17.0</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_using_the_ios_sdk">Using the iOS SDK</a>
<ul class="sectlevel3">
<li><a href="#_updating_and_validating_versions">Updating and validating versions</a></li>
<li><a href="#_software_requirements">Software requirements</a></li>
<li><a href="#_installing_dependency_managers">Installing dependency managers</a>
<ul class="sectlevel3">
<li><a href="#_swift_package_manager">Swift Package Manager</a></li>
<li><a href="#_dependencies_of_the_sdk">Dependencies of the SDK</a></li>
</ul>
</li>
<li><a href="#_setting_up_the_sdk">Setting up the SDK</a></li>
</ul>
</li>
<li><a href="#_the_ios_sdk_and_the_personal_health_data_platform">The iOS SDK and the Personal Health Data Platform</a>
<ul class="sectlevel2">
<li><a href="#_encrypting_and_decrypting_your_data">Encrypting and decrypting your data</a></li>
<li><a href="#_providing_local_data_storage_and_encryption">Providing local data storage and encryption</a></li>
</ul>
</li>
<li><a href="#_about_data_models">About data models</a>
<ul class="sectlevel2">
<li><a href="#_the_fhirrecord_data_model">The FHIRRecord data model</a></li>
<li><a href="#_the_appdatarecord_data_model">The AppDataRecord data model</a></li>
<li><a href="#_the_metadata_data_model">The Metadata data model</a></li>
</ul>
</li>
<li><a href="#_about_the_data4life_api">About the data4life API</a>
<ul class="sectlevel2">
<li><a href="#_authentication_and_authorization">Authentication and authorization</a>
<ul class="sectlevel3">
<li><a href="#_displaying_the_login_screen">Displaying the login screen</a></li>
<li><a href="#_displaying_the_login_screen_with_custom_oauth_2_0_scopes">Displaying the login screen with custom OAuth 2.0 scopes</a></li>
<li><a href="#_using_additional_parameters_for_the_login">Using additional parameters for the login</a></li>
<li><a href="#_logging_out_users">Logging out users</a></li>
<li><a href="#_checking_if_a_user_is_logged_in">Checking if a user is logged in</a></li>
<li><a href="#_receiving_updates_about_the_session_state">Receiving updates about the session state</a></li>
<li><a href="#_retrieving_user_account_identifier">Retrieving user account identifier</a></li>
</ul>
</li>
<li><a href="#_using_debug_logging">Using debug logging</a></li>
<li><a href="#_handling_response_threads">Handling response threads</a></li>
<li><a href="#_managing_records">Managing records</a>
<ul class="sectlevel4">
<li><a href="#_use_of_annotations">Use of annotations</a></li>
<li><a href="#_creating_a_new_fhir_record">Creating a new FHIR record</a></li>
<li><a href="#_creating_new_fhir_records">Creating new FHIR records</a></li>
<li><a href="#_fetching_a_fhir_record_by_its_id">Fetching a FHIR record by its ID</a></li>
<li><a href="#_fetching_multiple_fhir_records_with_ids">Fetching multiple FHIR records with IDs</a></li>
<li><a href="#_fetching_multiple_fhir_records_matching_filters">Fetching multiple FHIR records matching filters</a></li>
<li><a href="#_updating_a_fhir_record">Updating a FHIR record</a></li>
<li><a href="#_updating_several_fhir_records">Updating several FHIR records</a></li>
<li><a href="#_deleting_a_fhir_record_by_its_id">Deleting a FHIR record by its ID</a></li>
<li><a href="#_deleting_multiple_fhir_records_by_their_ids">Deleting multiple FHIR records by their IDs</a></li>
<li><a href="#_counting_fhir_records">Counting FHIR records</a></li>
<li><a href="#_creating_a_new_appdata_record">Creating a new AppData record</a></li>
<li><a href="#_fetching_an_appdata_record_by_its_id">Fetching an AppData record by its ID</a></li>
<li><a href="#_updating_an_appdata_record">Updating an AppData record</a></li>
<li><a href="#_deleting_an_appdata_record_by_its_id">Deleting an AppData record by its ID</a></li>
<li><a href="#_counting_appdata_records">Counting AppData records</a></li>
</ul>
</li>
<li><a href="#_managing_resources_with_attachments">Managing resources with attachments</a>
<ul class="sectlevel3">
<li><a href="#_downloading_all_resources_attachments_with_their_data_payloads">Downloading all resource’s attachments with their data payloads</a></li>
<li><a href="#_downloading_multiple_resource_records_and_all_their_attachments_with_their_data_payloads">Downloading multiple resource records and all their attachments with their data payloads</a></li>
</ul>
</li>
<li><a href="#_handling_attachments">Handling attachments</a>
<ul class="sectlevel3">
<li><a href="#_downloading_attachment_data">Downloading attachment data</a></li>
<li><a href="#_using_different_size_versions">Using different size versions</a></li>
<li><a href="#_cancelling_the_request_in_progress">Cancelling the request in progress</a></li>
<li><a href="#_observing_the_download_progress">Observing the download progress</a></li>
<li><a href="#_downloading_a_single_attachment_with_data_payload">Downloading a single attachment with data payload</a></li>
<li><a href="#_downloading_a_list_of_attachments_with_data_payloads">Downloading a list of attachments with data payloads</a></li>
</ul>
</li>
<li><a href="#_storing_custom_identifiers">Storing custom identifiers</a></li>
</ul>
</li>
<li><a href="#_copyright_and_license">Appendix A: Copyright and license</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This document covers the iOS SDK project for the Personal Health Data Platform.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_using_the_ios_sdk">Using the iOS SDK</h2>
<div class="sectionbody">
<div class="paragraph">
<p>As part of our integration efforts with our partners, data4life provides SDKs for the following platforms:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>iOS (Swift)</p>
</li>
<li>
<p>Android (Java)</p>
</li>
<li>
<p>Web (JavaScript)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The data4life SDKs encapsulate different aspects of communication with the backend servers of the data4life Personal Health Data Platform.
The SDKs let integration partners store sensitive health data on our secure platform.
The SDKs also enable sharing the data with authorized parties and other applications in an easy and secure way.</p>
</div>
<div class="sect3">
<h4 id="_updating_and_validating_versions">Updating and validating versions</h4>
<div class="paragraph">
<p>Data4Life can deprecate or unsupport old SDK versions that aren’t anymore secure or stay occassioning bugs. In that case the user will get notifications from the SDK:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>If the current version is <strong>deprecated</strong>, the user will get a notification in the console asking to update to a more recent version from the SDK. The SDK will still be able to be runned and used.</p>
</li>
<li>
<p>If the current version is <strong>unsupported</strong>, the user will get an error by using the SDK and will need to update to a new version in order to continue using the SDK.</p>
</li>
<li>
<p>If for some reason the version validation can’t happen, the user will get a notification in the console warning that the current version validation is <strong>unknown</strong>. The SDK will still work, but it might crash.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_software_requirements">Software requirements</h3>
<div class="paragraph">
<p>The iOS SDK has the following software requirements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Xcode 12.5 or later</p>
</li>
<li>
<p>iOS 13.0 or later</p>
</li>
<li>
<p>Swift 5.3 or later</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_installing_dependency_managers">Installing dependency managers</h3>
<div class="paragraph">
<p>This section describes how you get the SDK up and running.
Use the following for dependency management for the iOS SDK:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Swift Package Manager</strong> is a dependency manager from Apple included in XCode.
It builds your dependencies and provides you with binary frameworks while you retain control over your project structure and setup.</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="_swift_package_manager">Swift Package Manager</h4>
<div class="paragraph">
<p>To install with Swift package manager, select your project’s Swift Packages tab, and add our repository url, either as ssh or https url:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>https://github.com/d4l-data4life/d4l-sdk-ios.git
OR
git@github.com:d4l-data4life/d4l-sdk-ios.git</pre>
</div>
</div>
<div class="paragraph">
<p>In the next step, select the latest version, and then import the <code>Data4LifeSDK</code> libraries in your target.</p>
</div>
</div>
<div class="sect3">
<h4 id="_dependencies_of_the_sdk">Dependencies of the SDK</h4>
<div class="paragraph">
<p>The iOS SDK has the following dependencies:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/Alamofire/Alamofire">Alamofire</a> <span class="icon"><i class="fa fa-external-link"></i></span> – HTTP networking library written in Swift</p>
</li>
<li>
<p><a href="https://github.com/openid/AppAuth-iOS">AppAuth</a> <span class="icon"><i class="fa fa-external-link"></i></span> – iOS and macOS SDK for communicating with OAuth 2.0 and OpenID Connect providers</p>
</li>
<li>
<p><a href="https://github.com/d4l-data4life/d4l-fhir-ios">Data4LifeFHIR</a> <span class="icon"><i class="fa fa-external-link"></i></span> – data4life minimal FHIR standard models and data types for iOS</p>
</li>
<li>
<p><a href="https://github.com/d4l-data4life/d4l-utils-ios">Data4LifeSDKUtils</a> <span class="icon"><i class="fa fa-external-link"></i></span> – data4life Set of private utils used in data4Life Frameworks</p>
</li>
<li>
<p><a href="https://github.com/d4l-data4life/d4l-crypto-ios">Data4LifeCrypto</a> <span class="icon"><i class="fa fa-external-link"></i></span> – data4life Set of private utils used in data4Life Frameworks</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_setting_up_the_sdk">Setting up the SDK</h3>
<div class="paragraph">
<p>To set up the iOS SDK, follow these steps:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Configure the client information</p>
</li>
<li>
<p>Handle the OAuth 2.0 redirect URL</p>
</li>
<li>
<p>Display the login screen</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>This section describes the steps in more detail.</p>
</div>
<div class="olist arabic">
<ol class="arabic" start="1">
<li>
<p>Prepare the SDK for use by configuring the client information.
This must be the first SDK call or the client crashes.</p>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">import UIKit
import Data4LifeSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let clientId = "client-id#ios"
let secret = "secret"
let redirectURLString = "app://oauth"
let environment = .staging
Data4LifeClient.configureWith(clientId: clientId,
clientSecret: secret,
redirectURLString: redirectURLString,
environment: environment)
return true
}
}</code></pre>
</div>
</div>
</li>
<li>
<p>Handle the OAuth 2.0 redirect URL in the <code>AppDelegate</code> class.</p>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">import UIKit
import Data4LifeSDK
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
Data4LifeClient.default.handle(url: url)
return true
}
}</code></pre>
</div>
</div>
</li>
<li>
<p>Display the login screen.
Afterwards, you can use it throughout the app with the default client by providing a view controller to present.</p>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">let viewController = UIApplication.shared.keyWindow?.rootViewController
Data4LifeClient.default.presentLogin(on: viewController, animated: true) { result in
switch result {
case .success:
// Handle success
case .failure(let error):
// Handle error
}
}</code></pre>
</div>
</div>
</li>
<li>
<p>Optional: To use the SDK inside extensions, provide the <code>keychainGroupId</code> identifier when you configure the SDK and enable the <code>KeychainSharing</code> capability in the Xcode project.
The SDK also requires the <code>AppGroups</code> capability with the same setup.</p>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let clientId = "client-id#ios"
let secret = "secret"
let redirectURLString = "app://oauth"
let environment = .staging
let teamId = "TEAMDID"
let groupId = "Group1"
let keychainGroupId = "\(teamId).\(groupId)"
let appGroupId= "group.unique.id"
Data4LifeClient.configureWith(clientId: clientId,
clientSecret: secret,
redirectURLString: redirectURLString,
environment: .staging,
keychainGroupId: keychainGroupId,
appGroupId: appGroupId,
environment: environment)
return true
}</code></pre>
</div>
</div>
</li>
</ol>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_ios_sdk_and_the_personal_health_data_platform">The iOS SDK and the Personal Health Data Platform</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section gives an overview of how you manage data with the data4life Personal Health Data Platform (PHDP).</p>
</div>
<div class="sect2">
<h3 id="_encrypting_and_decrypting_your_data">Encrypting and decrypting your data</h3>
<div class="paragraph">
<p>The data4life SDK automatically handles data encryption.
The data4life Personal Health Data Platform uses a privacy by design approach, optimizing for the strictest possible privacy-preserving settings.
As an integrator, only you have access to the data that your application sends to the platform.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
Client-side encryption lays in the hands of the integrator application, so you are responsible for providing a proper <a href="#Providing local data storage">storage</a>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_providing_local_data_storage_and_encryption">Providing local data storage and encryption</h3>
<div class="paragraph">
<p>data4life doesn’t offer any client-side data storage solution. You can use the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><span class="icon"><i class="fa fa-external-link"></i></span> <a href="http://realm.io">Realm</a> as an encrypted database</p>
</li>
<li>
<p><span class="icon"><i class="fa fa-external-link"></i></span> <a href="https://github.com/krzyzanowskim/CryptoSwift">CryptoSwift</a> for general-purpose encryption</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Save the cryptographic keys to the Keychain on the phone of the user.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_about_data_models">About data models</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section gives you an overview of data models used by the iOS SDK.
The smallest unit of data that can be stored and referenced by the data4life platform is called a <em>record</em>. A record contains the following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Payload</strong> of either medical data in the form of an encrypted FHIR resource (Fast Healthcare Interoperability Resources) or generic data</p>
</li>
<li>
<p><strong>Metadata</strong> that’s needed to properly associate a record with a user</p>
</li>
<li>
<p><strong>Annotations</strong> Custom tags saved as strings that the user can use in order to filter or identify the existing resources. These cannot contain empty strings, and uppercased characters will be always lowercased, due to some internal functionality, so it’s recommended to use lowercased ones.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>A record can contain:
1) anything that can be modeled by a FHIR (STU3 or R4) resource. From a single vital sign measurement, such as body temperature, to a complex document linking to or containing multiple attachments and measuring many megabytes in size.
2) Generic Data (called App Data)</p>
</div>
<div class="paragraph">
<p>For these two cases we have two types of <em>records</em>:</p>
</div>
<div class="sect2">
<h3 id="_the_fhirrecord_data_model">The FHIRRecord data model</h3>
<div class="paragraph">
<p>The <code>FhirRecord</code> data model holds resource, metadata and additional metadata. <code>AnyFhirResource</code> is a protocol restricted to <code>Data4LifeFHIR.DomainResource</code> or <code>ModelsR4.DomainResource</code> type.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">struct FhirRecord<R: AnyFhirResource> {
public var id: String
public var fhirResource: R
public var metadata: Metadata
public var annotations: [String]
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_appdatarecord_data_model">The AppDataRecord data model</h3>
<div class="paragraph">
<p>The <code>AppDataRecord</code> data model like the FHIR one holds resource, metadata and additional metadata.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">struct AppDataRecord {
public var id: String
public var data: Data
public var metadata: Metadata
public var annotations: [String]
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_metadata_data_model">The Metadata data model</h3>
<div class="paragraph">
<p>The <code>Metadata</code> data model holds read-only information about records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">struct Metadata {
var updatedDate: Date
var createdDate: Date
var status: Status // can be active, pending, or deleted
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_about_the_data4life_api">About the data4life API</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This section gives you an overview of the data4life API.
The SDK handles all communication with the backend servers.
This abstracts away much of the know-how needed to communicate with the servers and exposes a number of lean custom models.
Integration partners and developers can rely on these models and the exposed methods to interact with the data4life Personal Health Data Platform.</p>
</div>
<div class="paragraph">
<p>Only logged-in users can run queries and perform actions.
When a request is made without a valid access token and a refresh token, the SDK throws an <code>unauthorized</code> exception.</p>
</div>
<div class="sect2">
<h3 id="_authentication_and_authorization">Authentication and authorization</h3>
<div class="paragraph">
<p>This section covers the authorization project features of the SDK.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Authentication</strong> is the process of verifying who users are.</p>
</li>
<li>
<p><strong>Authorization</strong> is the process of verifying what users have access to.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The SDK automatically handles all authentication and user management tasks. The user login is managed by the data4life auth app to ensure the safety of the user’s credentials. When the <code>login</code> functionality is invoked, the SDK opens a web view with the necessary pages. Or redirects in the case of a web-based app.</p>
</div>
<div class="sect3">
<h4 id="_displaying_the_login_screen">Displaying the login screen</h4>
<div class="paragraph">
<p>To display the login screen to users.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">Data4LifeClient.default.presentLogin(on: self, animated: true) { result in
switch result {
case .success:
// Handle result
case .failure(let error):
// Handle error
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_displaying_the_login_screen_with_custom_oauth_2_0_scopes">Displaying the login screen with custom OAuth 2.0 scopes</h4>
<div class="paragraph">
<p>Scopes are a mechanism in the OAuth 2.0 protocol to limit an application’s access to a user account.
The scope information is displayed to the user in the login screen.</p>
</div>
<div class="paragraph">
<p>To display the login screen with additional scopes to users, use the <code>presentLogin</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">let scopes = ["example", "scope"]
Data4LifeClient.default.presentLogin(on: self, animated: true, scopes: scopes) { result in
switch result {
case .success:
// Handle result
case .failure(let error):
// Handle error
}
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_using_additional_parameters_for_the_login">Using additional parameters for the login</h4>
<div class="paragraph">
<p>To display the login screen with additional parameters, for example, with the <code>loginCompletion</code> callback, use the following example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func presentLogin(on viewController: UIViewController, animated: Bool, scopes: [String]? = nil, presentationCompletion: (() -> Void)? = nil, loginCompletion: @escaping DefaultResultBlock)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_logging_out_users">Logging out users</h4>
<div class="paragraph">
<p>To log the currently logged-in user out of the current session, use the <code>logout</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func logout(completion: @escaping DefaultResultBlock)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_checking_if_a_user_is_logged_in">Checking if a user is logged in</h4>
<div class="paragraph">
<p>To check if a user is logged in, use the <code>userLoggedIn</code> method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func userLoggedIn(_ completion: @escaping DefaultResultBlock)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_receiving_updates_about_the_session_state">Receiving updates about the session state</h4>
<div class="paragraph">
<p>To receive updates about the session state, use the <code>sessionStateDidChange</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func sessionStateDidChange(completion: @escaping (Bool) -> Void)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_retrieving_user_account_identifier">Retrieving user account identifier</h4>
<div class="paragraph">
<p>It is possible to retrieve the User Identifier, which is unique to the account.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">public func getUserId(completion: @escaping ResultBlock<String>)</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_using_debug_logging">Using debug logging</h3>
<div class="paragraph">
<p>The SDK supports logging to console for debug configurations.
It is disabled by default. To enable it, set the <code>isLoggingEnabled</code> flag on the client to <code>true</code>.
However, release configuration is always silent.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">Data4LifeClient.default.isLoggingEnabled = true</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_handling_response_threads">Handling response threads</h3>
<div class="paragraph">
<p>All SDK calls accept <code>DispatchQueue</code> as a parameter.
The defined queue is used to return results, the <code>main</code> queue is the default.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">let queue = DispatchQueue.global(qos: .background)
Data4LifeClient.default.creteRecord(document, queue: queue) { result in
// Handle result
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_managing_records">Managing records</h3>
<div class="paragraph">
<p>The following sections describe how you perform queries and other actions for documents and records.</p>
</div>
<div class="sect4">
<h5 id="_use_of_annotations">Use of annotations</h5>
<div class="paragraph">
<p>The <code>create</code>, <code>update</code>, <code>search</code> and <code>count</code> methods can optionally use <code>annotations</code> as a parameter.
This parameter allows to tag records with custom information saved as a list of strings. Annotations can be filtered inside the <code>search</code> and <code>count</code> methods.
These annotations cannot contain empty strings, and uppercased characters will be always lowercased, due to some internal functionality, so it’s recommended to use lowercased ones.</p>
</div>
</div>
<div class="sect3">
<h4 id="_creating_a_new_fhir_record">Creating a new FHIR record</h4>
<div class="paragraph">
<p>To create a new record, use the <code>createFhirStu3Record</code> or <code>createFhirR4Record</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func createFhirStu3Record<R: FhirStu3Resource>(_ resource: R,
annotations: [String]? = nil,
completion: @escaping ResultBlock<Record<R>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func createFhirR4Record<R: FhirR4Resource>(_ resource: R,
annotations: [String]? = nil,
completion: @escaping ResultBlock<Record<R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_creating_new_fhir_records">Creating new FHIR records</h4>
<div class="paragraph">
<p>To create several new records, use the <code>createFhirStu3Records</code> or <code>createFhirR4Records</code> method. The annotations will be added to all the created records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func createFhirStu3Records<R: FhirStu3Resource>(_ resources: [R], annotations: [String] = [], completion: @escaping ResultBlock<BatchResult<Record<R>, R>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func createFhirR4Records<R: FhirR4Resource>(_ resources: [R], annotations: [String] = [], completion: @escaping ResultBlock<BatchResult<Record<R>, R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_fetching_a_fhir_record_by_its_id">Fetching a FHIR record by its ID</h4>
<div class="paragraph">
<p>To fetch records for the given ID, use the <code>fetchFhirStu3Record</code> or <code>fetchFhirR4Record</code> method with the <code>identifier</code> parameter of the record.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirStu3Record<R: FhirStu3Resource>(withId identifier: String, of type: R.Type = R.self, completion: @escaping ResultBlock<Record<R>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirR4Record<R: FhirR4Resource>(withId identifier: String, of type: R.Type = R.self, completion: @escaping ResultBlock<Record<R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_fetching_multiple_fhir_records_with_ids">Fetching multiple FHIR records with IDs</h4>
<div class="paragraph">
<p>To fetch one or more records for the given IDs, use the <code>fetchFhirStu3Records</code> or <code>fetchFhirR4Records</code> method with the <code>identifiers</code> parameters of the records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirStu3Records<R: FhirStu3Resource>(withIds identifiers: [String], of type: R.Type = R.self, completion: @escaping ResultBlock<BatchResult<Record<R>, String>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirR4Records<R: FhirR4Resource>(withIds identifiers: [String], of type: R.Type = R.self, completion: @escaping ResultBlock<BatchResult<Record<R>, String>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_fetching_multiple_fhir_records_matching_filters">Fetching multiple FHIR records matching filters</h4>
<div class="paragraph">
<p>To fetch more records matching a set of optional filters, use the <code>fetchFhirStu3Records</code> or <code>fetchFhirR4Records</code> method with the filter parameters.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirStu3Records<R: FhirStu3Resource>(of type: R.Type = R.self,
size: Int = 10,
page: Int = 1,
from: Date? = nil,
to: Date? = nil,
updatedFrom: Date? = nil,
updatedTo: Date? = nil,
includingDeleted: Bool = false,
annotations: [String] = [],
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<[FhirRecord<R>]>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchFhirR4Records<R: FhirR4Resource>(of type: R.Type = R.self,
size: Int = 10,
page: Int = 1,
from: Date? = nil,
to: Date? = nil,
updatedFrom: Date? = nil,
updatedTo: Date? = nil,
includingDeleted: Bool = false,
annotations: [String] = [],
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<[FhirRecord<R>]>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_updating_a_fhir_record">Updating a FHIR record</h4>
<div class="paragraph">
<p>To update a record, use the <code>updateFhirStu3Record</code> or <code>updateFhirR4Record</code> method.
If annotations are set to nil, existing annotations won’t change, otherwise they will override existing ones. If you only need to append new annotations, pass them as a parameter including the old ones in order to maintain them.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">public func updateFhirStu3Record<R: FhirStu3Resource>(_ resource: R,
annotations: [String]? = nil,
queue: DispatchQueue = responseQueue, completion: @escaping
ResultBlock<Record<R>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">public func updateFhirR4Record<R: FhirR4Resource>(_ resource: R,
annotations: [String]? = nil,
queue: DispatchQueue = responseQueue, completion: @escaping
ResultBlock<Record<R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_updating_several_fhir_records">Updating several FHIR records</h4>
<div class="paragraph">
<p>To update several records, use the <code>updateFhirStu3Records</code> or <code>updateFhirR4Records</code> method. If annotations are set to nil, existing annotations won’t change, otherwise they will override existing ones for all updated records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func updateFhirStu3Records<R: FhirStu3Resource>(_ resources: [R], annotations: [String]? = nil, completion: @escaping ResultBlock<BatchResult<Record<R>, R>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func updateFhirR4Records<R: FhirR4Resource>(_ resources: [R], annotations: [String]? = nil, completion: @escaping ResultBlock<BatchResult<Record<R>, R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_deleting_a_fhir_record_by_its_id">Deleting a FHIR record by its ID</h4>
<div class="paragraph">
<p>To delete a record with its given ID, use the <code>deleteFhirStu3Record</code> or <code>deleteFhirR4Record</code> method with the <code>identifier</code> parameter of the record.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func deleteFhirStu3Record(withId identifier: String, completion: @escaping ResultBlock<Void>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func deleteFhirR4Record(withId identifier: String, completion: @escaping ResultBlock<Void>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_deleting_multiple_fhir_records_by_their_ids">Deleting multiple FHIR records by their IDs</h4>
<div class="paragraph">
<p>To delete multiple records with their given IDs, use the <code>deleteFhirStu3Records</code> or <code>deleteFhirR4Records</code> method with the <code>identifiers</code> parameters of the records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func deleteFhirStu3Records(withIds identifiers: [String], completion: @escaping ResultBlock<BatchResult<String, String>>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func deleteFhirR4Records(withIds identifiers: [String], completion: @escaping ResultBlock<BatchResult<String, String>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_counting_fhir_records">Counting FHIR records</h4>
<div class="paragraph">
<p>To count the stored records per record type, use the <code>countFhirStu3Records</code> or <code>countFhirR4Records</code> method with the given <code>type</code> parameter.
If you don’t provide a record type, the client returns the count of all available records of that Fhir Version.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func countFhirStu3Records<R: FhirStu3Resource>(of type: R.Type?,
annotations: [String] = [],
completion: @escaping ResultBlock<Int>)</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func countFhirR4Records<R: FhirR4Resource>(of type: R.Type?,
annotations: [String] = [],
completion: @escaping ResultBlock<Int>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_creating_a_new_appdata_record">Creating a new AppData record</h4>
<div class="paragraph">
<p>To create a new AppData record, use the <code>createAppDataRecord</code> method or the <code>createCodableAppDataRecord</code> method. The annotations parameter allows to tag records with custom information saved as a list of strings. Annotations can be filtered inside the <code>search</code> and <code>count</code> methods.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func createAppDataRecord(_ data: Data,
annotations: [String] = []],
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<AppDataRecord>)
func createCodableAppDataRecord<D: Codable>(_ codable: D,
annotations: [String] = [],
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<AppDataRecord>)</code></pre>
</div>
</div>
<div class="paragraph">
<p>If the codable version of the create is used, the <code>AppDataRecord</code> has a convenient function to get the resource back:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">extension AppDataRecord {
func getDecodableResource<D: Decodable>(of type: D.Type = D.self) throws -> D
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_fetching_an_appdata_record_by_its_id">Fetching an AppData record by its ID</h4>
<div class="paragraph">
<p>To fetch AppData records for the given ID, use the <code>fetchAppDataRecord</code> method with the <code>identifier</code> parameter of the record.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func fetchAppDataRecord(withId identifier: String,
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<AppDataRecord>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_updating_an_appdata_record">Updating an AppData record</h4>
<div class="paragraph">
<p>To update an AppData record, use the <code>updateAppDataRecord</code> method or the <code>updateCodableAppDataRecord</code> method.
If annotations are set to nil, existing annotations won’t change, otherwise they will override existing ones. If you only need to append new annotations, pass them as a parameter including the old ones in order to maintain them.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func updateAppDataRecord(_ data: Data,
recordId: String,
annotations: [String]? = nil,
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<AppDataRecord>)
func updateCodableAppDataRecord<D: Codable>(_ codable: D,
recordId: String,
annotations: [String]? = nil,
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<AppDataRecord>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_deleting_an_appdata_record_by_its_id">Deleting an AppData record by its ID</h4>
<div class="paragraph">
<p>To delete an AppData record with its given ID, use the <code>deleteAppDataRecord</code> method with the <code>identifier</code> parameter of the record.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">public func deleteAppDataRecord(withId identifier: String,
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<Void>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_counting_appdata_records">Counting AppData records</h4>
<div class="paragraph">
<p>To count the stored AppData records, use the <code>countAppDataRecords</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func countAppDataRecords(annotations: [String] = [],
queue: DispatchQueue = responseQueue,
completion: @escaping ResultBlock<Int>)</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_managing_resources_with_attachments">Managing resources with attachments</h3>
<div class="paragraph">
<p>In FHIR, some resources can index a document, clinical note, and other binary objects to make them available to a healthcare system. At the moment attachment which can contain attachment are:
- <code>DocumentReference</code>
- <code>DiagnosticReport</code>
- <code>Medication</code>
- <code>Practitioner</code>
- <code>Patient</code>
- <code>Observation</code> (including its component attachments)
- <code>Questionnaire</code> (including its nested items attachments)
- <code>QuestionnaireResponse</code> (including its nested items and answers attachments)</p>
</div>
<div class="sect3">
<h4 id="_downloading_all_resources_attachments_with_their_data_payloads">Downloading all resource’s attachments with their data payloads</h4>
<div class="paragraph">
<p>If you want a record to be downloaded with its given ID and its attachments, use the <code>downloadStu3Record</code> method and the <code>identifier</code> parameter of the record.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func downloadStu3Record<R: FhirStu3Resource>(withId identifier: String, completion: @escaping ResultBlock<Record<R>>)</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_downloading_multiple_resource_records_and_all_their_attachments_with_their_data_payloads">Downloading multiple resource records and all their attachments with their data payloads</h4>
<div class="paragraph">
<p>If you want one or more records to be downloaded with their given IDs and their attachments, use the <code>downloadStu3Records</code> method and the <code>identifiers</code> parameters of the records.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">func downloadStu3Records<R: FhirStu3Resource>(withIds identifiers: [String], of type: R.Type = R.self, completion: @escaping ResultBlock<BatchResult<Record<R>, String>>)</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_handling_attachments">Handling attachments</h3>
<div class="sect3">
<h4 id="_downloading_attachment_data">Downloading attachment data</h4>
<div class="paragraph">
<p>If a <code>FhirStu3Resource</code> with attachments is fetched using the <code>fetchFhirStu3Record</code> method, all of the attachments only have metadata (for example, <code>title</code> and <code>contentType</code>) but no data payload. To download an attachment including the data payload, use the <code>downloadStu3Attachment</code> method or the <code>downloadStu3Attachments</code> method.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="swift">Data4LifeClient.default.downloadStu3Record(withId: "identifier", of: DocumentReference.self) { result in
guard let document = result.value?.resource else {
return
}
guard let attachments = document.getAttachments() else {
return
}
let data = attachments.first?.getData()
}</code></pre>
</div>