@flybits/react-native-concierge 0.0.0 → 0.1.7

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.
@@ -0,0 +1,158 @@
1
+
2
+ import React
3
+ import FlybitsConcierge
4
+
5
+
6
+ @objc(ConciergeViewManager)
7
+ class ConciergeViewManager: RCTViewManager {
8
+
9
+ override func view() -> (ConciergeView) {
10
+
11
+
12
+ return ConciergeView()
13
+ }
14
+
15
+ @objc override static func requiresMainQueueSetup() -> Bool {
16
+ return true
17
+ }
18
+
19
+ }
20
+
21
+ class ConciergeView : UIView {
22
+
23
+ var fb: UIViewController?
24
+ @objc var onConciergeChange: RCTBubblingEventBlock?
25
+ @objc var zoneReferenceIDs: Array<String>?
26
+
27
+ @objc var actionlink: Dictionary<String, Any>?
28
+
29
+ @objc var onActionEvent: RCTBubblingEventBlock?
30
+ @objc var onAnalyticsEvent: RCTBubblingEventBlock?
31
+ @objc var onContentArrived: RCTBubblingEventBlock?
32
+
33
+ deinit {
34
+ fb?.view.removeFromSuperview()
35
+ fb?.removeFromParent()
36
+ fb?.didMove(toParent: nil)
37
+ }
38
+
39
+ override func didMoveToSuperview() {
40
+ super.didMoveToSuperview()
41
+
42
+ var delegate: AnyConciergeEventsDelegate<ConciergeEventsDelegate>?
43
+
44
+ let cDelegate = ConciergeViewManagerConciergeDelegate()
45
+ delegate = Concierge.delegate(from: cDelegate)
46
+
47
+ var urlComponent = URLComponents()
48
+
49
+ if let actionlink = actionlink {
50
+ for (key, value) in actionlink {
51
+ switch key {
52
+ case "action": urlComponent.scheme = value as? String ?? "concierge://"
53
+ case "params":
54
+ guard let value = value as? Dictionary<String, Any> else { return }
55
+
56
+ var queryItems = [URLQueryItem]()
57
+ for (key, value) in value {
58
+ let queryItem = URLQueryItem(name: key, value: value as? String)
59
+ queryItems.append(queryItem)
60
+ }
61
+
62
+ urlComponent.queryItems = queryItems
63
+
64
+ default:
65
+ break
66
+ }
67
+ }
68
+ }
69
+
70
+ var concierge: UIViewController!
71
+
72
+ if let url = urlComponent.url {
73
+ concierge = Concierge.handleActionableLink(url, with: [], requestEvents: delegate) ?? Concierge.viewController(.configured, params: [], options: [])
74
+ } else {
75
+ concierge = Concierge.viewController(.configured, params: [], options: [])
76
+ }
77
+
78
+
79
+ self.addSubview(concierge.view)
80
+
81
+ concierge.view.translatesAutoresizingMaskIntoConstraints = false
82
+
83
+ concierge.view.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
84
+ concierge.view.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor).isActive = true
85
+ concierge.view.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
86
+ concierge.view.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
87
+
88
+ self.fb = concierge
89
+
90
+ }
91
+ }
92
+
93
+ class ConciergeViewManagerConciergeDelegate: ConciergeEventsDelegate {
94
+
95
+ func conciergeWillDisplayContent(_ hasContent: Bool, identifier: String) {
96
+ let emitter = ConciergeViewManagerEvents.shared
97
+ if((emitter?.hasListeners) != nil) {
98
+ emitter?.sendEvent(withName: "Concierge-Event", body: ["body": ["type": "conciergeContentFetch", "hasContent": hasContent, "identifier": identifier]])
99
+ }
100
+ }
101
+
102
+ func event(_ actionlink: URL, identifier: String) {
103
+ guard let components = URLComponents(url: actionlink, resolvingAgainstBaseURL: false) else { return }
104
+
105
+ var scheme = "concierge"
106
+ if var tmp = components.scheme {
107
+ scheme = tmp.last == ":" ? String(tmp.remove(at: tmp.index(before: tmp.endIndex))) : tmp
108
+ }
109
+
110
+ let emitter = ConciergeViewManagerEvents.shared
111
+ if((emitter?.hasListeners) != nil) {
112
+ emitter?.sendEvent(withName: "Concierge-Event", body: ["body": ["type": "conciergeAction", "actionableLink": ["action": scheme, "params":components.queryItems?.reduce([:], { partialResult, queryItem in
113
+ var newResult = partialResult
114
+ newResult[queryItem.name] = queryItem.value
115
+ return newResult
116
+ }) ?? [:] ], "identifier": identifier]])
117
+ }
118
+ }
119
+
120
+ func analyticsEvent(_ exportedAnalyticsEvent: ExportedAnalyticsEvent, identifier: String) {
121
+ let emitter = ConciergeViewManagerEvents.shared
122
+ if((emitter?.hasListeners) != nil) {
123
+ emitter?.sendEvent(withName: "Concierge-Event", body: ["body": ["type": "conciergeAnalytics", "analyticsType": exportedAnalyticsEvent.type, "contentData": exportedAnalyticsEvent.contentData ?? [:], "contentInfo": exportedAnalyticsEvent.contentInfo]])
124
+ }
125
+ }
126
+ }
127
+
128
+ @objc(ConciergeView)
129
+ class ConciergeViewManagerEvents: RCTEventEmitter {
130
+ public static var shared: ConciergeViewManagerEvents?
131
+ var hasListeners: Bool = false
132
+
133
+ override init() {
134
+ super.init()
135
+ ConciergeViewManagerEvents.shared = self
136
+ }
137
+
138
+ deinit {
139
+ }
140
+
141
+ @objc(supportedEvents)
142
+ override func supportedEvents() -> [String]! {
143
+ return [
144
+ "Concierge-Event"
145
+ ]
146
+ }
147
+ @objc(startObserving)
148
+ override func startObserving() {
149
+ super.startObserving()
150
+ hasListeners = true
151
+ }
152
+
153
+ @objc(stopObserving)
154
+ override func stopObserving() {
155
+ super.stopObserving()
156
+ hasListeners = false
157
+ }
158
+ }
@@ -0,0 +1,489 @@
1
+ import FlybitsConcierge
2
+ import FlybitsContextSDK
3
+ import FlybitsCoreConcierge
4
+ import FlybitsSDK
5
+ import React
6
+
7
+ struct ErrorDetails: Codable {
8
+ let code: Int
9
+ let description: String
10
+ let action: String
11
+ }
12
+
13
+ struct ResultItem: Codable {
14
+ let errorCode: Int
15
+ let details: ErrorDetails
16
+ }
17
+
18
+ @objc(FlybitsModule)
19
+ class FlybitsModuleSwift: NSObject {
20
+ var pushNetwork: ConciergePushNetwork = .fcm
21
+
22
+ static let UndefinedError = -1
23
+ static let InvalidFormatError = -2
24
+
25
+ @objc(configureFlybits:resolved:rejected:)
26
+ func configureFlybits(configuration: [String: Any],
27
+ resolved: @escaping RCTPromiseResolveBlock,
28
+ rejected: @escaping RCTPromiseRejectBlock) {
29
+
30
+ DispatchQueue.main.sync {
31
+
32
+ var contextPlugins = [ContextManager.PluginItem]()
33
+ var locales = [Locale]()
34
+
35
+ guard
36
+ let projectId = configuration["projectId"] as? String,
37
+ let gateway = configuration["gateway"] as? String,
38
+ let configPushNetwork = configuration["pushNetwork"] as? String,
39
+ !projectId.isEmpty,
40
+ !gateway.isEmpty,
41
+ !configPushNetwork.isEmpty
42
+ else {
43
+ let error = NSError(domain: "com.flybits",
44
+ code: 1,
45
+ userInfo: ["description": "Not enough information provided to configure Concierge. Please, provide the project Identifier, the gateway, the push network."])
46
+ rejected(error.localizedDescription, "\(error.code)", error)
47
+ return
48
+ }
49
+
50
+ pushNetwork = ConciergePushNetwork.convertToObj(configPushNetwork.lowercased())
51
+
52
+ var config = FlybitsConciergeConfiguration.Builder()
53
+ .setProjectId(projectId)
54
+ .setGatewayUrl(gateway)
55
+ .setPushNetwork(pushNetwork)
56
+ .setWebService(configuration["webService"] as? String ?? "https://static-files-concierge.demo.flybits.com/latest")
57
+
58
+ if let webPoolSize = configuration["webPoolSize"] as? Int {
59
+ config = config.setWebviewPoolingSize(webPoolSize)
60
+ }
61
+
62
+ if let _ = configuration["locationPlugin"] {
63
+ contextPlugins.append(.location)
64
+ }
65
+
66
+ if let _ = configuration["setAutoRegisterForLocation"] {
67
+ config = config.setAutoRegisterForLocation(true)
68
+ }
69
+
70
+ if let value = configuration["timeToRefreshInterval"] as? Int {
71
+ config = config.setTimeToRefreshInterval(value)
72
+ }
73
+
74
+ if let value = configuration["timeToRefreshNoDataInterval"] as? Int {
75
+ config = config.setTimeToRefreshInterval(value)
76
+ }
77
+
78
+ if let locales = configuration["locales"] as? String,
79
+ let decoded = try? JSONDecoder().decode(Array<String>.self, from: Data(locales.utf8))
80
+ {
81
+ for locale in decoded {
82
+ let newLocale = Locale(identifier: locale)
83
+ locales.append(newLocale)
84
+ }
85
+ }
86
+
87
+ Concierge.configure(configuration: config.build(),
88
+ contextPlugins: contextPlugins,
89
+ customLanguages: locales)
90
+
91
+ if let logging = configuration["logLevel"] as? String, let level = logLevel(logLevel: logging) {
92
+ Concierge.enableLogging(forLevel: level)
93
+ }
94
+
95
+ FlybitsModuleSwift.completionHandler(with: nil, resolved: resolved, rejected: rejected)
96
+ }
97
+ }
98
+
99
+ @objc(anonymousConnect:rejected:)
100
+ func anonymousConnect(
101
+ resolved: @escaping RCTPromiseResolveBlock, rejected: @escaping RCTPromiseRejectBlock
102
+ ) {
103
+ Concierge.connect(with: AnonymousConciergeIDP()) { error in
104
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
105
+ }
106
+ }
107
+
108
+ @objc(authenticateJwtSignIn:resolved:rejected:)
109
+ func authenticateJwtSignIn(
110
+ jwt: String, resolved: @escaping RCTPromiseResolveBlock,
111
+ rejected: @escaping RCTPromiseRejectBlock
112
+ ) {
113
+ Concierge.connect(with: JwtLoginConciergeIDP(jwtLoginToken: jwt)) { error in
114
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
115
+ }
116
+ }
117
+
118
+ @objc(disconnect:rejected:)
119
+ func disconnect(
120
+ resolved: @escaping RCTPromiseResolveBlock, rejected: @escaping RCTPromiseRejectBlock
121
+ ) {
122
+ Concierge.disconnect { error in
123
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
124
+ }
125
+ }
126
+
127
+ @objc(isConnected:rejected:)
128
+ func isConnected(resolved: RCTPromiseResolveBlock, rejected: RCTPromiseRejectBlock) {
129
+ if Concierge.isConnected {
130
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "true", resolved: resolved, rejected: rejected)
131
+ } else {
132
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "false", resolved: resolved, rejected: rejected)
133
+ }
134
+ }
135
+
136
+ @objc(actionlink:resolved:rejected:)
137
+ func actionlink(userInfo: [String: Any],
138
+ resolved: @escaping RCTPromiseResolveBlock,
139
+ rejected: @escaping RCTPromiseRejectBlock
140
+ ) {
141
+ if let data = userInfo["data"] as? [String: Any], let push = Concierge.handlePush(data) {
142
+ let value: String = Concierge.actionableLink(from: push).absoluteString
143
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: value, resolved: resolved, rejected: rejected)
144
+ } else {
145
+ let error = NSError(domain: "com.flybits",
146
+ code: 2,
147
+ userInfo: ["description": "Provided userInfo can NOT be converted into ConciergePush object."])
148
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
149
+ }
150
+ }
151
+
152
+ @objc(actionTypeSchemeParser:resolved:rejected:)
153
+ func actionTypeSchemeParser(actionableLink: String,
154
+ resolved: @escaping RCTPromiseResolveBlock,
155
+ rejected: @escaping RCTPromiseRejectBlock
156
+ ) {
157
+ let successDictionary = FlybitsModuleSwift.fromSuccessArray(actionableLink)
158
+
159
+ guard let realActionableLink: String = successDictionary["result"] as? String else {
160
+ let error = NSError(domain: "com.flybits",
161
+ code: 3,
162
+ userInfo: ["description": "Not possible to parse the provided actionableLink"])
163
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
164
+ return
165
+ }
166
+
167
+ let actionScheme = ActionTypeSchemeParser.parse(realActionableLink)
168
+ let actionableLinkDict: [String: Any] = [
169
+ "actionTypeIntent": actionScheme.intent.toString(),
170
+ "data": actionScheme.data,
171
+ ]
172
+
173
+ do {
174
+ let jsonData = try JSONSerialization.data(withJSONObject: actionableLinkDict, options: [])
175
+
176
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
177
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: jsonString, resolved: resolved, rejected: rejected)
178
+ } else {
179
+ let error = NSError(domain: "com.flybits",
180
+ code: 4,
181
+ userInfo: ["description": "Not possible to parse the provided actionableLink"])
182
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
183
+ }
184
+ } catch {
185
+ rejected(error.localizedDescription, "\((error as NSError).code)", error)
186
+ }
187
+ }
188
+
189
+ func extractActionableLink(from input: [String: Any]) -> String {
190
+ guard
191
+ let detailsDict = input["details"] as? [String: Any],
192
+ let descriptionString = detailsDict["description"] as? String
193
+ else {
194
+ return ""
195
+ }
196
+
197
+ return descriptionString
198
+ }
199
+
200
+ @objc(isFlybitsPush:resolved:rejected:)
201
+ func isFlybitsPush(notification: NSDictionary,
202
+ resolved: @escaping RCTPromiseResolveBlock,
203
+ rejected: @escaping RCTPromiseRejectBlock
204
+ ) {
205
+ var notificationDict = notification
206
+
207
+ if case .fcm = pushNetwork {
208
+
209
+ if let firstLayer = notification["data"] as? [String: String],
210
+ let base64String = firstLayer["data"],
211
+ let decodedData = Data(base64Encoded: base64String),
212
+ let jsonObject = try? JSONSerialization.jsonObject(with: decodedData, options: []),
213
+ let dict = jsonObject as? NSDictionary
214
+ {
215
+ notificationDict = dict
216
+ } else {
217
+ let error = NSError(domain: "com.flybits",
218
+ code: FlybitsModuleSwift.InvalidFormatError,
219
+ userInfo: ["description": "Invalid FCM Push notification body. The Flybits slice should be inside the root/data key"])
220
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
221
+ }
222
+ }
223
+
224
+ if let provider = notificationDict["provider"] as? String, provider.lowercased() == "flybits" {
225
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "true", resolved: resolved, rejected: rejected)
226
+ } else {
227
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "false", resolved: resolved, rejected: rejected)
228
+ }
229
+ }
230
+
231
+ @objc(notificationStatus:rejected:)
232
+ func notificationStatus(
233
+ resolved: @escaping RCTPromiseResolveBlock, rejected: @escaping RCTPromiseRejectBlock
234
+ ) {
235
+
236
+ switch Concierge.pushTokenUploadStatus() {
237
+ case .unknown:
238
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "unknown", resolved: resolved, rejected: rejected)
239
+ case .deleted:
240
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "deleted", resolved: resolved, rejected: rejected)
241
+ case .sent:
242
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "sent", resolved: resolved, rejected: rejected)
243
+ default:
244
+ FlybitsModuleSwift.completionHandler(with: nil, successInput: "unknown", resolved: resolved, rejected: rejected)
245
+ }
246
+ }
247
+
248
+ @objc(addToken:resolved:rejected:)
249
+ func addToken(
250
+ token: String, resolved: @escaping RCTPromiseResolveBlock,
251
+ rejected: @escaping RCTPromiseRejectBlock
252
+ ) {
253
+ if let data = token.data(using: .utf8) {
254
+ Concierge.sendPush(token: data)
255
+ FlybitsModuleSwift.completionHandler(with: nil, resolved: resolved, rejected: rejected)
256
+ } else {
257
+ let error = NSError(domain: "com.flybits",
258
+ code: 5,
259
+ userInfo: ["description": "Token not convertable to data"])
260
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
261
+ }
262
+ }
263
+
264
+ @objc(removeToken:rejected:)
265
+ func removeToken(
266
+ resolved: @escaping RCTPromiseResolveBlock, rejected: @escaping RCTPromiseRejectBlock
267
+ ) {
268
+ Concierge.deletePushToken { error in
269
+ FlybitsModuleSwift.completionHandler(with: error, resolved: resolved, rejected: rejected)
270
+ }
271
+ }
272
+
273
+ private func logLevel(logLevel: String?) -> ConciergeLoggerLevel? {
274
+ guard let logLevel = logLevel else {
275
+ return nil
276
+ }
277
+
278
+ switch logLevel.lowercased() {
279
+ case "debug":
280
+ return .debug
281
+ default:
282
+ return .none
283
+ }
284
+ }
285
+
286
+ private static func convertToSuccessJSON(_ input: String) -> String {
287
+ let inputDictionary: [String: Any] = ["success": true, "result": input]
288
+
289
+ return convertToJSON(inputDictionary)
290
+ }
291
+
292
+ private static func convertToErrorJSON(_ input: NSError) -> String {
293
+ let inputDictionary: [String: Any] = ["success": false, "code": input.code, "message": input.localizedDescription]
294
+
295
+ return convertToJSON(inputDictionary)
296
+ }
297
+
298
+ private static func convertToErrorJSON(_ input: String) -> String {
299
+ let error = NSError(
300
+ domain: "com.flybits",
301
+ code: FlybitsModuleSwift.UndefinedError,
302
+ userInfo: ["description": input])
303
+ return convertToErrorJSON(error)
304
+ }
305
+
306
+ private static func convertToJSON(_ input: [String: Any]) -> String {
307
+ do {
308
+ let jsonData = try JSONSerialization.data(withJSONObject: input, options: [])
309
+
310
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
311
+ return jsonString
312
+ } else {
313
+ return ""
314
+ }
315
+ } catch {
316
+ return ""
317
+ }
318
+ }
319
+
320
+ private static func completionHandler(with error: NSError?, successInput: String = "ok", resolved: RCTPromiseResolveBlock, rejected: RCTPromiseRejectBlock) {
321
+ if let error = error {
322
+ let errorJSON = FlybitsModuleSwift.convertToErrorJSON(error)
323
+
324
+ if errorJSON.isEmpty {
325
+ rejected(error.code.description, error.localizedDescription, error)
326
+ } else {
327
+ resolved(errorJSON)
328
+ }
329
+ } else {
330
+ let successJSON = convertToSuccessJSON(successInput)
331
+
332
+ if successJSON.isEmpty {
333
+ let error = NSError(
334
+ domain: "com.flybits",
335
+ code: FlybitsModuleSwift.UndefinedError,
336
+ userInfo: ["description": "Supposedly success scenario but JSON parsing failed."])
337
+ rejected(error.code.description, error.localizedDescription, error)
338
+ } else {
339
+ resolved(successJSON)
340
+ }
341
+ }
342
+ }
343
+
344
+ private static func fromSuccessArray(_ jsonInput: String) -> [String: Any] {
345
+ guard let data = jsonInput.data(using: .utf8) else {
346
+ return [String: Any]()
347
+ }
348
+
349
+ do {
350
+ if let dict = try JSONSerialization.jsonObject(with: data, options: [])
351
+ as? [String: Any]
352
+ {
353
+ return dict
354
+ }
355
+ } catch {
356
+ }
357
+ return [String: Any]()
358
+ }
359
+ }
360
+
361
+ @objc(Auth)
362
+ class ConciergeViewManagerAuth: RCTEventEmitter, FlybitsScope {
363
+
364
+ var hasListeners: Bool = false
365
+
366
+ override init() {
367
+ super.init()
368
+ }
369
+
370
+ deinit {
371
+
372
+ }
373
+
374
+ @objc(supportedEvents)
375
+ override func supportedEvents() -> [String]! {
376
+ return [
377
+ "Authentication-onStart"
378
+ ]
379
+ }
380
+ @objc(startObserving)
381
+ override func startObserving() {
382
+ super.startObserving()
383
+ hasListeners = true
384
+ FlybitsManager.add(
385
+ scope: self, forkey: "ConciergeViewManagerAuth+\(ObjectIdentifier(self))")
386
+ }
387
+
388
+ @objc(stopObserving)
389
+ override func stopObserving() {
390
+ super.stopObserving()
391
+ FlybitsManager.removeScope(forKey: "ConciergeViewManagerAuth+\(ObjectIdentifier(self))")
392
+ hasListeners = false
393
+ }
394
+
395
+ func onStart(currentActiveUserIsOptedIn: Bool, authState: FlybitsSDK.ScopeAuthState) {
396
+ if hasListeners {
397
+ self.sendEvent(
398
+ withName: "Authentication-onStart",
399
+ body: ["body": ["authState": scopeAuthStateAsString(authState)]])
400
+ }
401
+ }
402
+
403
+ func onStop() {
404
+ }
405
+
406
+ func onConnected(user: FlybitsSDK.FlybitsUser) {
407
+ if hasListeners {
408
+ self.sendEvent(
409
+ withName: "Authentication-onStart",
410
+ body: ["body": ["authState": scopeAuthStateAsString(.connected)]])
411
+ }
412
+ }
413
+
414
+ func onDisconnected() {
415
+ if hasListeners {
416
+ self.sendEvent(
417
+ withName: "Authentication-onStart",
418
+ body: ["body": ["authState": scopeAuthStateAsString(.disconnected)]])
419
+ }
420
+ }
421
+
422
+ func onOptIn() {
423
+
424
+ }
425
+
426
+ func onOptOut() {
427
+
428
+ }
429
+
430
+ var identifier: String = ""
431
+
432
+ private func scopeAuthStateAsString(_ authState: FlybitsSDK.ScopeAuthState) -> String {
433
+ switch authState {
434
+ case .connected: return "connected"
435
+ case .connectedOptOut: return "connectedOptOut"
436
+ case .connecting: return "connecting"
437
+ case .disconnected: return "disconnected"
438
+ default: return "unknown"
439
+ }
440
+ }
441
+
442
+ }
443
+
444
+ extension ActionTypeIntent {
445
+ func toString() -> String {
446
+ let result: String
447
+ switch self {
448
+ case .unknown:
449
+ result = "unknown"
450
+ case .concierge:
451
+ result = "concierge"
452
+ case .web:
453
+ result = "web"
454
+ case .browser:
455
+ result = "browser"
456
+ case .universalLink:
457
+ result = "universalLink"
458
+ case .mail:
459
+ result = "mail"
460
+ case .phone:
461
+ result = "phone"
462
+ case .app:
463
+ result = "app"
464
+ case .details:
465
+ result = "details"
466
+ case .settings:
467
+ result = "settings"
468
+ case .optIn:
469
+ result = "optIn"
470
+ @unknown default:
471
+ result = "notMapped"
472
+ }
473
+
474
+ return result
475
+ }
476
+ }
477
+
478
+ extension ConciergePushNetwork {
479
+ static func convertToObj(_ str: String) -> ConciergePushNetwork {
480
+ switch(str.lowercased()) {
481
+ case "fcm":
482
+ return ConciergePushNetwork.fcm
483
+ case "apns":
484
+ return ConciergePushNetwork.apns
485
+ default:
486
+ return ConciergePushNetwork.apns
487
+ }
488
+ }
489
+ }