react-native-persona 2.2.30 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,53 +7,98 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## [v2.4.0] - 2023-02-24
11
+
12
+ ### Changed
13
+
14
+ - Upgrade to iOS Inquiry SDK 2.7.0
15
+ - Upgrade to Android Inquiry SDK 2.4.0
16
+ - Deprecated the iosTheme(themeObject: Object) functions. The iosThemeToUse(themeObject: Object, themeSource: ThemeSource)
17
+ functions should be used instead. Pass in a themeSource of ThemeSource.SERVER to use theming set in the Persona Dashboard.
18
+ Pass in ThemeSource.CLIENT to keep current behavior that uses the client. Note that ThemeSource.CLIENT is also marked
19
+ as deprecated. Also note that the Android theme source will be derived from this ThemeSource as well. If iosTheme is
20
+ called instead of iosThemeToUse, ThemeSource.Client will be used. If neither iosThemeToUse nor iosTheme is called,
21
+ ThemeSource.SERVER will be used by default. The themeObject can still be used to control certain properties
22
+ like the initial loading icon even when the theme source is set to ThemeSource.SERVER.
23
+
24
+ ## [v2.3.0] - 2023-02-16
25
+
26
+ ### Added
27
+
28
+ - Added optional support to receive data collected during inquiry flow on completion.
29
+
30
+ ### Changed
31
+
32
+ - Upgrade to iOS Inquiry SDK 2.6.2
33
+ - Upgrade to Android Inquiry SDK 2.3.6
34
+
10
35
  ## [v2.2.30] - 2023-01-25
11
36
 
37
+ ### Changed
38
+
12
39
  - Upgrade to iOS Inquiry SDK 2.5.11
13
40
  - Upgrade to Android Inquiry SDK 2.3.5
14
41
 
15
42
  ## [v2.2.29] - 2022-12-15
16
43
 
44
+ ### Changed
45
+
17
46
  - Upgrade to iOS Inquiry SDK 2.5.4
18
47
  - Upgrade to Android Inquiry SDK 2.3.0
19
48
 
20
49
  ## [v2.2.28] - 2022-11-30
21
50
 
51
+ ### Changed
52
+
22
53
  - Upgrade to iOS Inquiry SDK 2.5.3
23
54
  - Upgrade to Android Inquiry SDK 2.2.45
24
55
 
25
56
  ## [v2.2.27] - 2022-11-16
26
57
 
58
+ ### Changed
59
+
27
60
  - Upgrade to iOS Inquiry SDK 2.5.0
28
61
  - Upgrade to Android Inquiry SDK 2.2.43
29
62
 
30
63
  ## [v2.2.26] - 2022-10-20
31
64
 
65
+ ### Changed
66
+
32
67
  - Upgrade to iOS Inquiry SDK 2.4.3
33
68
  - Upgrade to Android Inquiry SDK 2.2.41
34
69
 
35
70
  ## [v2.2.25] - 2022-10-06
36
71
 
72
+ ### Changed
73
+
37
74
  - Upgrade to iOS Inquiry SDK 2.4.2
38
75
  - Upgrade to Android Inquiry SDK 2.2.40
39
76
 
40
77
  ## [v2.2.24] - 2022-09-22
41
78
 
79
+ ### Changed
80
+
42
81
  - Upgrade to iOS Inquiry SDK 2.4.0
43
82
  - Upgrade to Android Inquiry SDK 2.2.37
44
83
 
45
84
  ## [v2.2.23] - 2022-09-07
46
85
 
86
+ ### Changed
87
+
47
88
  - Upgrade to iOS Inquiry SDK 2.3.9
48
89
  - Upgrade to Android Inquiry SDK 2.2.36
49
90
 
50
91
  ## [v2.2.22] - 2022-08-25
51
92
 
93
+ ### Changed
94
+
52
95
  - Upgrade to iOS Inquiry SDK 2.3.6
53
96
  - Upgrade to Android Inquiry SDK 2.2.31
54
97
 
55
98
  ## [v2.2.21] - 2022-08-18
56
99
 
100
+ ### Fixed
101
+
57
102
  - Fixed a bug where the inquiry flow failed to present on iOS
58
103
 
59
104
  ## [v2.2.20] - 2022-08-15
package/README.md CHANGED
@@ -85,9 +85,12 @@ Inquiry.fromTemplate('itmpl_Ygs16MKTkA6obnF8C3Rb17dm')
85
85
 
86
86
  ### Theming
87
87
 
88
- Theming can be handled universally through our persona-tools CLI provided in this repository.
88
+ Set your own colors, buttons, fonts, and more. This can be done via the Persona Dashboard. For more information on using the theme editor, see our [help article](https://support.withpersona.com/hc/en-us/articles/13775762246547-Tutorial-Configure-a-Theme-with-Inquiry-Template-Editor).
89
89
 
90
- To test themes, use the example app:
90
+
91
+ Note that the following instructions are for deprecated client side theming only.
92
+
93
+ To test legacy client side themes, use the example app:
91
94
 
92
95
  ```
93
96
  cd example
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
22
22
  s.requires_arc = true
23
23
 
24
24
  s.dependency 'React-Core'
25
- s.dependency 'PersonaInquirySDK2', '2.5.11'
25
+ s.dependency 'PersonaInquirySDK2', '2.7.0'
26
26
 
27
27
  s.static_framework = true
28
28
  end
@@ -66,5 +66,5 @@ dependencies {
66
66
  //noinspection GradleDynamicVersion
67
67
  implementation 'com.facebook.react:react-native:+' // From node_modules
68
68
 
69
- implementation 'com.withpersona.sdk2:inquiry:2.3.5'
69
+ implementation 'com.withpersona.sdk2:inquiry:2.4.0'
70
70
  }
@@ -2,15 +2,20 @@ package com.withpersona.sdk2.reactnative;
2
2
 
3
3
  import android.app.Activity;
4
4
  import android.content.Intent;
5
+
5
6
  import androidx.annotation.Nullable;
7
+
6
8
  import com.facebook.react.bridge.ActivityEventListener;
7
9
  import com.facebook.react.bridge.Arguments;
8
10
  import com.facebook.react.bridge.ReactApplicationContext;
9
11
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
12
  import com.facebook.react.bridge.ReactMethod;
13
+ import com.facebook.react.bridge.ReadableArray;
11
14
  import com.facebook.react.bridge.ReadableMap;
15
+ import com.facebook.react.bridge.WritableArray;
12
16
  import com.facebook.react.bridge.WritableMap;
13
17
  import com.facebook.react.modules.core.DeviceEventManagerModule;
18
+ import com.withpersona.sdk2.inquiry.ClientThemeSource;
14
19
  import com.withpersona.sdk2.inquiry.Environment;
15
20
  import com.withpersona.sdk2.inquiry.Fields;
16
21
  import com.withpersona.sdk2.inquiry.Inquiry;
@@ -18,10 +23,19 @@ import com.withpersona.sdk2.inquiry.InquiryBuilder;
18
23
  import com.withpersona.sdk2.inquiry.InquiryField;
19
24
  import com.withpersona.sdk2.inquiry.InquiryResponse;
20
25
  import com.withpersona.sdk2.inquiry.InquiryTemplateBuilder;
26
+ import com.withpersona.sdk2.inquiry.ServerThemeSource;
27
+ import com.withpersona.sdk2.inquiry.types.collected_data.CollectedData;
28
+ import com.withpersona.sdk2.inquiry.types.collected_data.DocumentFile;
29
+ import com.withpersona.sdk2.inquiry.types.collected_data.GovernmentIdCapture;
30
+ import com.withpersona.sdk2.inquiry.types.collected_data.SelfieCapture;
31
+ import com.withpersona.sdk2.inquiry.types.collected_data.StepData;
32
+
33
+ import org.jetbrains.annotations.NotNull;
34
+
21
35
  import java.util.HashMap;
36
+ import java.util.List;
22
37
  import java.util.Map;
23
38
  import java.util.Objects;
24
- import org.jetbrains.annotations.NotNull;
25
39
 
26
40
  public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
27
41
  implements ActivityEventListener {
@@ -35,6 +49,8 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
35
49
  private static final String ENVIRONMENT = "environment";
36
50
  private static final String FIELDS = "fields";
37
51
  private static final String FIELD_ADDITIONAL_FIELDS = "additionalFields";
52
+ private static final String RETURN_COLLECTED_DATA = "returnCollectedData";
53
+ private static final String THEME_SOURCE = "themeSource";
38
54
 
39
55
  private final ReactApplicationContext reactContext;
40
56
 
@@ -92,6 +108,8 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
92
108
  }
93
109
  params.putMap("fields", fields);
94
110
 
111
+ params.putMap("collectedData", collectedDataToMap(complete.getCollectedData()));
112
+
95
113
  jsModule.emit("onComplete", params);
96
114
  } else if (response instanceof InquiryResponse.Cancel) {
97
115
  InquiryResponse.Cancel cancel = (InquiryResponse.Cancel) response;
@@ -109,6 +127,137 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
109
127
  }
110
128
  }
111
129
 
130
+ @Nullable
131
+ private ReadableMap collectedDataToMap(@Nullable CollectedData collectedData) {
132
+ if (collectedData == null) {
133
+ return null;
134
+ }
135
+
136
+ WritableMap collectedDataMap = Arguments.createMap();
137
+
138
+ WritableArray stepDataMap = Arguments.createArray();
139
+ for (StepData stepDatum : collectedData.getStepData()) {
140
+ WritableMap stepDatumMap = Arguments.createMap();
141
+ stepDatumMap.putString("stepName", stepDatum.getStepName());
142
+
143
+ if (stepDatum instanceof StepData.DocumentStepData) {
144
+ WritableArray documentsArr = Arguments.createArray();
145
+
146
+ for (DocumentFile document : ((StepData.DocumentStepData) stepDatum).getDocuments()) {
147
+ WritableMap documentMap = Arguments.createMap();
148
+ documentMap.putString("absoluteFilePath", document.getData().getAbsolutePath());
149
+ documentsArr.pushMap(documentMap);
150
+ }
151
+
152
+ stepDatumMap.putArray("documents", documentsArr);
153
+ stepDatumMap.putString("type", "DocumentStepData");
154
+ } else if (stepDatum instanceof StepData.GovernmentIdStepData) {
155
+ WritableArray capturesArr = Arguments.createArray();
156
+
157
+ for (GovernmentIdCapture capture : ((StepData.GovernmentIdStepData) stepDatum).getCaptures()) {
158
+ WritableMap captureMap = Arguments.createMap();
159
+ captureMap.putString("idClass", capture.getIdClass());
160
+ captureMap.putString("captureMethod", capture.getCaptureMethod().name());
161
+ captureMap.putString("side", capture.getSide().name());
162
+
163
+ WritableArray framesArr = Arguments.createArray();
164
+ for (GovernmentIdCapture.Frame frame : capture.getFrames()) {
165
+ WritableMap frameMap = Arguments.createMap();
166
+ frameMap.putString("absoluteFilePath", frame.getData().getAbsolutePath());
167
+
168
+ framesArr.pushMap(frameMap);
169
+ }
170
+ captureMap.putArray("frames", framesArr);
171
+
172
+ capturesArr.pushMap(captureMap);
173
+ }
174
+
175
+ stepDatumMap.putArray("captures", capturesArr);
176
+ stepDatumMap.putString("type", "GovernmentIdStepData");
177
+ } else if (stepDatum instanceof StepData.SelfieStepData) {
178
+ StepData.SelfieStepData selfieStepData = (StepData.SelfieStepData) stepDatum;
179
+ ReadableMap centerCaptureMap = selfieCaptureToMap(selfieStepData.getCenterCapture());
180
+ ReadableMap leftCaptureMap = selfieCaptureToMap(selfieStepData.getLeftCapture());
181
+ ReadableMap rightCaptureMap = selfieCaptureToMap(selfieStepData.getRightCapture());
182
+
183
+ stepDatumMap.putMap("centerCapture", centerCaptureMap);
184
+ stepDatumMap.putMap("leftCapture", leftCaptureMap);
185
+ stepDatumMap.putMap("rightCapture", rightCaptureMap);
186
+ stepDatumMap.putString("type", "SelfieStepData");
187
+ } else if (stepDatum instanceof StepData.UiStepData) {
188
+ Map<String, Object> componentParams = ((StepData.UiStepData) stepDatum).getComponentParams();
189
+
190
+ stepDatumMap.putMap("componentParams", uiStepParamsMapToMap(componentParams));
191
+ stepDatumMap.putString("type", "UiStepData");
192
+ } else {
193
+ // do nothing
194
+ }
195
+
196
+ stepDataMap.pushMap(stepDatumMap);
197
+ }
198
+
199
+ collectedDataMap.putArray("stepData", stepDataMap);
200
+
201
+ return collectedDataMap;
202
+ }
203
+
204
+ private ReadableMap uiStepParamsMapToMap(Map<?, ?> map) {
205
+ WritableMap resultMap = Arguments.createMap();
206
+ for (Map.Entry<?, ?> param : map.entrySet()) {
207
+ Object key = param.getKey();
208
+ Object value = param.getValue();
209
+
210
+ if (!(key instanceof String)) {
211
+ continue; // All ui step params should have string keys.
212
+ }
213
+
214
+ String keyStr = (String) key;
215
+
216
+ if (value instanceof Map<?, ?>) {
217
+ resultMap.putMap(keyStr, uiStepParamsMapToMap((Map<?, ?>) value));
218
+ } else if (value instanceof List<?>) {
219
+ resultMap.putArray(keyStr, uiStepParamsArrToArr((List<?>) value));
220
+ } else if (value instanceof String) {
221
+ resultMap.putString(keyStr, (String) value);
222
+ } else if (value instanceof Boolean) {
223
+ resultMap.putBoolean(keyStr, (Boolean) value);
224
+ } else if (value instanceof Number) {
225
+ resultMap.putDouble(keyStr, (Double) value);
226
+ }
227
+ }
228
+
229
+ return resultMap;
230
+ }
231
+
232
+ private ReadableArray uiStepParamsArrToArr(List<?> arr) {
233
+ WritableArray resultArr = Arguments.createArray();
234
+
235
+ for (Object element : arr) {
236
+ if (element instanceof String) {
237
+ resultArr.pushString((String) element);
238
+ } else if (element instanceof Boolean) {
239
+ resultArr.pushBoolean((Boolean) element);
240
+ } else if (element instanceof Number) {
241
+ resultArr.pushDouble((Double) element);
242
+ }
243
+ }
244
+
245
+
246
+ return resultArr;
247
+ }
248
+
249
+ private ReadableMap selfieCaptureToMap(@Nullable SelfieCapture selfieCapture) {
250
+ if (selfieCapture == null) {
251
+ return null;
252
+ }
253
+
254
+ WritableMap captureMap = Arguments.createMap();
255
+ captureMap.putString("captureMethod", selfieCapture.getCaptureMethod().name());
256
+ captureMap.putString("absoluteFilePath", selfieCapture.getData().getAbsolutePath());
257
+ return captureMap;
258
+ }
259
+
260
+
112
261
  private static ReadableMap wrapField(String type, String value) {
113
262
  WritableMap map = Arguments.createMap();
114
263
  map.putString("type", type);
@@ -140,7 +289,9 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
140
289
  if (inquiryId != null) {
141
290
  InquiryBuilder builder = Inquiry.fromInquiry(inquiryId);
142
291
 
143
- builder = builder.theme(R.style.Persona_Inquiry2_Theme);
292
+ String themeSource = options.hasKey(THEME_SOURCE) ? options.getString(THEME_SOURCE) : null;
293
+ builder = builder.theme(themeSource != null && themeSource.equals("server") ?
294
+ new ServerThemeSource(R.style.Persona_Inquiry2_Theme) : new ClientThemeSource(R.style.Persona_Inquiry2_Theme));
144
295
 
145
296
  String sessionToken = options.hasKey(ACCESS_TOKEN) ? options.getString(ACCESS_TOKEN) : null;
146
297
  if (sessionToken != null) {
@@ -155,7 +306,9 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
155
306
  if (templateId != null) {
156
307
  InquiryTemplateBuilder builder = Inquiry.fromTemplate(templateId);
157
308
 
158
- builder = builder.theme(R.style.Persona_Inquiry2_Theme);
309
+ String themeSource = options.hasKey(THEME_SOURCE) ? options.getString(THEME_SOURCE) : null;
310
+ builder = builder.theme(themeSource != null && themeSource.equals("server") ?
311
+ new ServerThemeSource(R.style.Persona_Inquiry2_Theme) : new ClientThemeSource(R.style.Persona_Inquiry2_Theme));
159
312
 
160
313
  String referenceId = options.hasKey(REFERENCE_ID) ? options.getString(REFERENCE_ID) : null;
161
314
  if (referenceId != null) {
@@ -201,6 +354,11 @@ public class PersonaInquiryModule2 extends ReactContextBaseJavaModule
201
354
  builder = builder.fields(fieldsBuilder.build());
202
355
  }
203
356
 
357
+ Boolean returnCollectedData = options.hasKey(RETURN_COLLECTED_DATA) ? options.getBoolean(RETURN_COLLECTED_DATA) : null;
358
+ if (returnCollectedData != null) {
359
+ builder = builder.returnCollectedData(returnCollectedData);
360
+ }
361
+
204
362
  if (currentActivity != null) {
205
363
  builder.build().start(currentActivity, PERSONA_INQUIRY_REQUEST_CODE);
206
364
  }
@@ -11,6 +11,8 @@ import Persona2
11
11
  @objc(PersonaInquiry2)
12
12
  class PersonaInquiry2: RCTEventEmitter {
13
13
 
14
+ private var collectedData: InquiryData? = nil
15
+
14
16
  override func supportedEvents() -> [String] {
15
17
  ["onComplete", "onCanceled", "onError"]
16
18
  }
@@ -39,38 +41,41 @@ class PersonaInquiry2: RCTEventEmitter {
39
41
  let environment = options["environment"] as? String
40
42
  let sessionToken = options["sessionToken"] as? String
41
43
  let themeObject = options["iosTheme"] as? [String: String]
44
+ let themeSource = options["themeSource"] as? String
42
45
  let fieldsObject = options["fields"] as? [String: [String: Any]]
43
-
44
- do {
45
- let config = try createConfig(
46
- templateId: templateId,
47
- templateVersion: templateVersion,
48
- inquiryId: inquiryId,
49
- sessionToken: sessionToken,
50
- referenceId: referenceId,
51
- accountId: accountId,
52
- environment: Environment.from(rawValue: environment),
53
- theme: themeFrom(themeObject),
54
- fields: fieldsFrom(fieldsObject)
55
- )
56
- DispatchQueue.main.async {
57
- guard let viewController = self.findTopViewController() else {
58
- self.sendEvent(
59
- withName: "onError",
60
- body: [
61
- "debugMessage": "Couldn't resolve a root view controller"
62
- ]
63
- )
64
- return
65
- }
66
-
67
- Inquiry(
68
- config: config,
69
- delegate: self
70
- ).start(from: viewController)
46
+ let returnCollectedData = options["returnCollectedData"] as? Bool ?? false
47
+
48
+ guard let config = InquiryConfiguration.build(
49
+ inquiryId: inquiryId,
50
+ sessionToken: sessionToken,
51
+ templateVersion: templateVersion,
52
+ templateId: templateId,
53
+ referenceId: referenceId,
54
+ accountId: accountId,
55
+ environment: Environment.from(rawValue: environment),
56
+ fields: fieldsFrom(fieldsObject),
57
+ theme: makeTheme(from: themeObject, themeSource: themeSource),
58
+ nfcAdapter: nil,
59
+ collectionDelegate: returnCollectedData ? self : nil
60
+ ) else {
61
+ print("An error occurred while starting the Inquiry, invalid config.")
62
+ return
63
+ }
64
+ DispatchQueue.main.async {
65
+ guard let viewController = self.findTopViewController() else {
66
+ self.sendEvent(
67
+ withName: "onError",
68
+ body: [
69
+ "debugMessage": "Couldn't resolve a root view controller"
70
+ ]
71
+ )
72
+ return
71
73
  }
72
- } catch {
73
- print("An error occurred while starting the Inquiry: \(error.localizedDescription)")
74
+
75
+ Inquiry(
76
+ config: config,
77
+ delegate: self
78
+ ).start(from: viewController)
74
79
  }
75
80
  }
76
81
 
@@ -85,7 +90,7 @@ class PersonaInquiry2: RCTEventEmitter {
85
90
  }
86
91
  }
87
92
 
88
- // MARK: - Inquiry Delegate Methods
93
+ // MARK: - InquiryDelegate
89
94
 
90
95
  extension PersonaInquiry2: InquiryDelegate {
91
96
  func inquiryComplete(inquiryId: String, status: String, fields: [String: InquiryField]) {
@@ -94,7 +99,8 @@ extension PersonaInquiry2: InquiryDelegate {
94
99
  body: [
95
100
  "inquiryId": inquiryId,
96
101
  "status": status,
97
- "fields": fieldsToDictionary(fields: fields)
102
+ "fields": fieldsToDictionary(fields: fields),
103
+ "collectedData": collectedData?.toDictionary()
98
104
  ]
99
105
  )
100
106
  }
@@ -120,109 +126,117 @@ extension PersonaInquiry2: InquiryDelegate {
120
126
  }
121
127
  }
122
128
 
123
- // MARK: - Helpers
129
+ // MARK: - InquiryCollectionDelegate
124
130
 
125
- extension PersonaInquiry2 {
131
+ extension PersonaInquiry2: InquiryCollectionDelegate {
126
132
 
127
- enum InvalidConfiguration: Error {
128
- case argumentError(String)
133
+ func collectionComplete(data: InquiryData) {
134
+ self.collectedData = data
129
135
  }
136
+ }
130
137
 
131
- /// Creates an InquiryConfiguration based on the passed in parameters
132
- private func createConfig(templateId: String?,
133
- templateVersion: String?,
134
- inquiryId: String? = nil,
135
- sessionToken: String? = nil,
136
- referenceId: String? = nil,
137
- accountId: String? = nil,
138
- environment: Environment? = .production,
139
- theme: InquiryTheme? = nil,
140
- fields: [String: InquiryField]? = nil) throws -> InquiryConfiguration {
141
-
142
- // If we have an inquiry ID there is only one path to take
143
- if let inquiryId = inquiryId {
144
- return InquiryConfiguration(
145
- inquiryId: inquiryId,
146
- sessionToken: sessionToken,
147
- theme: theme
148
- // fields: fields // TODO: Uncomment this once it's enabled
149
- )
150
- }
151
- // Use the template ID if we have it
152
- else if let templateId = templateId {
153
-
154
- // Use the account ID if we have it
155
- if let accountId = accountId {
156
- return InquiryConfiguration(
157
- templateId: templateId,
158
- accountId: accountId,
159
- environment: environment,
160
- fields: fields,
161
- theme: theme
162
- )
163
- }
164
- // Use the reference ID if we have it
165
- else if let referenceId = referenceId {
166
- return InquiryConfiguration(
167
- templateId: templateId,
168
- referenceId: referenceId,
169
- environment: environment,
170
- fields: fields,
171
- theme: theme
172
- )
173
- }
138
+ extension InquiryData {
139
+
140
+ func toDictionary() -> [String: Any?] {
141
+ var inquiryData = [[String: Any?]]()
142
+ for data in self.stepData {
143
+ var currentStepData = [String: Any?]()
144
+
145
+ switch data {
146
+ case .ui(let uiData):
147
+ currentStepData["type"] = "UiStepData"
148
+ currentStepData["stepName"] = uiData.name
149
+ currentStepData["componentParams"] = uiData.componentData.reduce(into: [[String: Any?]]()) { partial, element in
150
+ switch element {
151
+ case .int(let key, let value):
152
+ partial.append([key: value])
153
+ case .bool(let key, let value):
154
+ partial.append([key: value])
155
+ case .string(let key, let value):
156
+ partial.append([key: value])
157
+ case .strings(let key, let value):
158
+ partial.append([key: value])
159
+ case .double(let key, let value):
160
+ partial.append([key: value])
161
+ case .address(let key, let value):
162
+ partial.append([key: value])
163
+ default:
164
+ return
165
+ }
166
+ }
167
+ case .selfie(let selfieData):
168
+ currentStepData["type"] = "SelfieStepData"
169
+ currentStepData["stepName"] = selfieData.name
170
+ if let centerPhoto = selfieData.centerPhoto {
171
+ currentStepData["centerCapture"] = [
172
+ "captureMethod": centerPhoto.captureMethod,
173
+ "absoluteFilePath": centerPhoto.filePath
174
+ ]
175
+ }
174
176
 
175
- // Otherwise only use the template ID
176
- return InquiryConfiguration(
177
- templateId: templateId,
178
- environment: environment,
179
- fields: fields,
180
- theme: theme
181
- )
182
- }
183
- // Otherwise use the template version
184
- else if let templateVersion = templateVersion {
185
-
186
- // Use the account ID if we have it
187
- if let accountId = accountId {
188
- return InquiryConfiguration(
189
- templateVersion: templateVersion,
190
- accountId: accountId,
191
- environment: environment,
192
- fields: fields,
193
- theme: theme
194
- )
195
- }
196
- // Use the reference ID if we have it
197
- else if let referenceId = referenceId {
198
- return InquiryConfiguration(
199
- templateVersion: templateVersion,
200
- referenceId: referenceId,
201
- environment: environment,
202
- fields: fields,
203
- theme: theme
204
- )
205
- }
177
+ if let leftPhoto = selfieData.leftPhoto {
178
+ currentStepData["leftCapture"] = [
179
+ "captureMethod": leftPhoto.captureMethod,
180
+ "absoluteFilePath": leftPhoto.filePath
181
+ ]
182
+ }
206
183
 
207
- // Otherwise only use the template version
208
- return InquiryConfiguration(
209
- templateVersion: templateVersion,
210
- environment: environment,
211
- fields: fields,
212
- theme: theme
213
- )
184
+ if let rightPhoto = selfieData.rightPhoto {
185
+ currentStepData["rightCapture"] = [
186
+ "captureMethod": rightPhoto.captureMethod,
187
+ "absoluteFilePath": rightPhoto.filePath
188
+ ]
189
+ }
190
+
191
+ case .governmentId(let govIdData):
192
+ currentStepData["type"] = "GovernmentIdStepData"
193
+ currentStepData["stepName"] = govIdData.name
194
+
195
+ var captures = [[String: Any]]()
196
+ for file in govIdData.files {
197
+ captures.append([
198
+ "idClass": govIdData.idClass,
199
+ "captureMethod": file.captureMethod,
200
+ "side": file.page,
201
+ "frames": file.frames.map {
202
+ ["absoluteFilePath": $0.filePath]
203
+ }
204
+ ])
205
+ }
206
+ currentStepData["captures"] = captures
207
+ case .document(let docData):
208
+ currentStepData["type"] = "DocumentStepData"
209
+ currentStepData["stepName"] = docData.name
210
+
211
+ var documents = [[String: String]]()
212
+ for file in docData.files {
213
+ documents.append(["absoluteFilePath": file.filePath])
214
+ }
215
+ currentStepData["documents"] = documents
216
+ default:
217
+ continue
218
+ }
219
+ inquiryData.append(currentStepData)
214
220
  }
221
+ return ["stepData": inquiryData]
222
+ }
223
+ }
224
+
225
+ // MARK: - Helpers
226
+
227
+ extension PersonaInquiry2 {
215
228
 
216
- throw InvalidConfiguration.argumentError("Invalid arguments. Please refer to documentation or support for assistance.")
229
+ enum InvalidConfiguration: Error {
230
+ case argumentError(String)
217
231
  }
218
232
 
219
233
  /// Converts a dictionary into a theme
220
- private func themeFrom(_ dictionary: [String: String]? = nil) -> InquiryTheme? {
234
+ private func makeTheme(from dictionary: [String: String]? = nil, themeSource: String? = nil) -> InquiryTheme? {
221
235
  guard let dictionary = dictionary else {
222
236
  return nil
223
237
  }
224
238
 
225
- var theme = InquiryTheme()
239
+ var theme = InquiryTheme(themeSource: themeSource == "server" ? .server : .client)
226
240
 
227
241
  if let color = color(from: dictionary["backgroundColor"]) {
228
242
  theme.backgroundColor = color