From 4145f1ea6004ab76ddd793e75efc4d4cc80c4401 Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sat, 2 Aug 2025 08:44:39 -0700 Subject: [PATCH 1/2] feat: add Swift formatting configuration and formatted swift files --- .swift-format | 79 ++ ios/RNIterableAPI/ReactIterableAPI.swift | 1365 ++++++++++++---------- ios/RNIterableAPI/Serialization.swift | 415 +++---- 3 files changed, 1003 insertions(+), 856 deletions(-) create mode 100644 .swift-format diff --git a/.swift-format b/.swift-format new file mode 100644 index 000000000..545beaa17 --- /dev/null +++ b/.swift-format @@ -0,0 +1,79 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentConditionalCompilationBlocks" : true, + "indentSwitchCaseLabels" : false, + "indentation" : { + "spaces" : 2 + }, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : false, + "lineBreakBeforeEachGenericRequirement" : false, + "lineBreakBetweenDeclarationAttributes" : false, + "lineLength" : 100, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "reflowMultilineStringLiterals" : { + "never" : { + + } + }, + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : true, + "AmbiguousTrailingClosureOverload" : true, + "AvoidRetroactiveConformances" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyLinesOpeningClosingBraces" : false, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : false, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : true, + "UseLetInEveryBoundCaseVariable" : true, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + }, + "spacesAroundRangeFormationOperators" : false, + "spacesBeforeEndOfLineComments" : 2, + "tabWidth" : 8, + "version" : 1 +} diff --git a/ios/RNIterableAPI/ReactIterableAPI.swift b/ios/RNIterableAPI/ReactIterableAPI.swift index 4db314f20..d5a0b3827 100644 --- a/ios/RNIterableAPI/ReactIterableAPI.swift +++ b/ios/RNIterableAPI/ReactIterableAPI.swift @@ -4,683 +4,750 @@ // import Foundation - import IterableSDK @objc(ReactIterableAPI) class ReactIterableAPI: RCTEventEmitter { - deinit { - NotificationCenter.default.removeObserver(self) - } - - // MARK: - React Native Functions - - @objc static override func moduleName() -> String! { - return "RNIterableAPI" - } - - override var methodQueue: DispatchQueue! { - _methodQueue - } - - @objc override static func requiresMainQueueSetup() -> Bool { - false - } - - enum EventName: String, CaseIterable { - case handleUrlCalled - case handleCustomActionCalled - case handleInAppCalled - case handleAuthCalled - case receivedIterableInboxChanged - case handleAuthSuccessCalled - case handleAuthFailureCalled - } - - override func supportedEvents() -> [String]! { - var result = [String]() - - EventName.allCases.forEach { - result.append($0.rawValue) - } - - return result - } - - override func startObserving() { - ITBInfo() - - shouldEmit = true - } - - override func stopObserving() { - ITBInfo() - - shouldEmit = false - } - - // MARK: - Native SDK Functions - - @objc(initializeWithApiKey:config:version:resolver:rejecter:) - func initialize(apiKey: String, - config configDict: [AnyHashable: Any], - version: String, - resolver: @escaping RCTPromiseResolveBlock, - rejecter: @escaping RCTPromiseRejectBlock) { - ITBInfo() - - initialize(withApiKey: apiKey, - config: configDict, - version: version, - resolver: resolver, - rejecter: rejecter) - } - - @objc(initialize2WithApiKey:config:apiEndPointOverride:version:resolver:rejecter:) - func initialize2(apiKey: String, - config configDict: [AnyHashable: Any], - version: String, - apiEndPointOverride: String, - resolver: @escaping RCTPromiseResolveBlock, - rejecter: @escaping RCTPromiseRejectBlock) { - ITBInfo() - - initialize(withApiKey: apiKey, - config: configDict, - version: version, - apiEndPointOverride: apiEndPointOverride, - resolver: resolver, - rejecter: rejecter) - } - - @objc(setEmail:) - func set(email: String?) { - ITBInfo() - - IterableAPI.email = email - } - - @objc(setEmail:authToken:) - func set(email: String?, authToken: String?) { - ITBInfo() - - IterableAPI.setEmail(email, authToken) - } - - @objc(getEmail:rejecter:) - func getEmail(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.email) - } - - @objc(setUserId:) - func set(userId: String?) { - ITBInfo() - - IterableAPI.userId = userId - } - - @objc(setUserId:authToken:) - func set(userId: String?, authToken: String?) { - ITBInfo() - - IterableAPI.setUserId(userId, authToken) - } - - @objc(getUserId:rejecter:) - func getUserId(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.userId) - } - - // MARK: - Iterable API Request Functions - - @objc(setInAppShowResponse:) - func set(inAppShowResponse number: NSNumber) { - ITBInfo() - - self.inAppShowResponse = InAppShowResponse.from(number: number) - - inAppHandlerSemaphore.signal() - } - - @objc(disableDeviceForCurrentUser) - func disableDeviceForCurrentUser() { - ITBInfo() - - IterableAPI.disableDeviceForCurrentUser() - } - - @objc(getLastPushPayload:rejecter:) - func getLastPushPayload(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.lastPushPayload) - } - - @objc(getAttributionInfo:rejecter:) - func getAttributionInfo(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.attributionInfo.map(SerializationUtil.encodableToDictionary)) - } - - @objc(setAttributionInfo:) - func set(attributionInfo dict: [AnyHashable: Any]?) { - ITBInfo() - - guard let dict = dict else { - IterableAPI.attributionInfo = nil - return - } - - IterableAPI.attributionInfo = SerializationUtil.dictionaryToDecodable(dict: dict) - } - - @objc(trackPushOpenWithCampaignId:templateId:messageId:appAlreadyRunning:dataFields:) - func trackPushOpen(campaignId: NSNumber, - templateId: NSNumber?, - messageId: String, - appAlreadyRunning: Bool, - dataFields: [AnyHashable: Any]?) { - ITBInfo() - - IterableAPI.track(pushOpen: campaignId, - templateId: templateId, - messageId: messageId, - appAlreadyRunning: appAlreadyRunning, - dataFields: dataFields) - } - - @objc(updateCart:) - func updateCart(items: [[AnyHashable: Any]]) { - ITBInfo() - - IterableAPI.updateCart(items: items.compactMap(CommerceItem.from(dict:))) - } - - @objc(trackPurchase:items:dataFields:) - func trackPurchase(total: NSNumber, - items: [[AnyHashable: Any]], - dataFields: [AnyHashable: Any]?) { - ITBInfo() - - IterableAPI.track(purchase: total, - items: items.compactMap(CommerceItem.from(dict:)), - dataFields: dataFields) - } - - @objc(trackInAppOpen:location:) - func trackInAppOpen(messageId: String, - location locationNumber: NSNumber) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - IterableAPI.track(inAppOpen: message, location: InAppLocation.from(number: locationNumber)) - } - - @objc(trackInAppClick:location:clickedUrl:) - func trackInAppClick(messageId: String, - location locationNumber: NSNumber, - clickedUrl: String) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - IterableAPI.track(inAppClick: message, location: InAppLocation.from(number: locationNumber), clickedUrl: clickedUrl) - } - - @objc(trackInAppClose:location:source:clickedUrl:) - func trackInAppClose(messageId: String, - location locationNumber: NSNumber, - source sourceNumber: NSNumber, - clickedUrl: String?) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - if let inAppCloseSource = InAppCloseSource.from(number: sourceNumber) { - IterableAPI.track(inAppClose: message, - location: InAppLocation.from(number: locationNumber), - source: inAppCloseSource, - clickedUrl: clickedUrl) - } else { - IterableAPI.track(inAppClose: message, - location: InAppLocation.from(number: locationNumber), - clickedUrl: clickedUrl) - } + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: - React Native Functions + + @objc static override func moduleName() -> String! { + return "RNIterableAPI" + } + + override var methodQueue: DispatchQueue! { + _methodQueue + } + + @objc override static func requiresMainQueueSetup() -> Bool { + false + } + + enum EventName: String, CaseIterable { + case handleUrlCalled + case handleCustomActionCalled + case handleInAppCalled + case handleAuthCalled + case receivedIterableInboxChanged + case handleAuthSuccessCalled + case handleAuthFailureCalled + } + + override func supportedEvents() -> [String]! { + var result = [String]() + + EventName.allCases.forEach { + result.append($0.rawValue) } - - @objc(inAppConsume:location:source:) - func inAppConsume(messageId: String, - location locationNumber: NSNumber, - source sourceNumber: NSNumber) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - if let inAppDeleteSource = InAppDeleteSource.from(number: sourceNumber) { - IterableAPI.inAppConsume(message: message, - location: InAppLocation.from(number: locationNumber), - source: inAppDeleteSource) - } else { - IterableAPI.inAppConsume(message: message, - location: InAppLocation.from(number: locationNumber)) - } + + return result + } + + override func startObserving() { + ITBInfo() + + shouldEmit = true + } + + override func stopObserving() { + ITBInfo() + + shouldEmit = false + } + + // MARK: - Native SDK Functions + + @objc(initializeWithApiKey:config:version:resolver:rejecter:) + func initialize( + apiKey: String, + config configDict: [AnyHashable: Any], + version: String, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock + ) { + ITBInfo() + + initialize( + withApiKey: apiKey, + config: configDict, + version: version, + resolver: resolver, + rejecter: rejecter) + } + + @objc(initialize2WithApiKey:config:apiEndPointOverride:version:resolver:rejecter:) + func initialize2( + apiKey: String, + config configDict: [AnyHashable: Any], + version: String, + apiEndPointOverride: String, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock + ) { + ITBInfo() + + initialize( + withApiKey: apiKey, + config: configDict, + version: version, + apiEndPointOverride: apiEndPointOverride, + resolver: resolver, + rejecter: rejecter) + } + + @objc(setEmail:) + func set(email: String?) { + ITBInfo() + + IterableAPI.email = email + } + + @objc(setEmail:authToken:) + func set(email: String?, authToken: String?) { + ITBInfo() + + IterableAPI.setEmail(email, authToken) + } + + @objc(getEmail:rejecter:) + func getEmail(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.email) + } + + @objc(setUserId:) + func set(userId: String?) { + ITBInfo() + + IterableAPI.userId = userId + } + + @objc(setUserId:authToken:) + func set(userId: String?, authToken: String?) { + ITBInfo() + + IterableAPI.setUserId(userId, authToken) + } + + @objc(getUserId:rejecter:) + func getUserId(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.userId) + } + + // MARK: - Iterable API Request Functions + + @objc(setInAppShowResponse:) + func set(inAppShowResponse number: NSNumber) { + ITBInfo() + + self.inAppShowResponse = InAppShowResponse.from(number: number) + + inAppHandlerSemaphore.signal() + } + + @objc(disableDeviceForCurrentUser) + func disableDeviceForCurrentUser() { + ITBInfo() + + IterableAPI.disableDeviceForCurrentUser() + } + + @objc(getLastPushPayload:rejecter:) + func getLastPushPayload(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.lastPushPayload) + } + + @objc(getAttributionInfo:rejecter:) + func getAttributionInfo(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.attributionInfo.map(SerializationUtil.encodableToDictionary)) + } + + @objc(setAttributionInfo:) + func set(attributionInfo dict: [AnyHashable: Any]?) { + ITBInfo() + + guard let dict = dict else { + IterableAPI.attributionInfo = nil + return } - - @objc(getHtmlInAppContentForMessage:resolver:rejecter:) - func getHtmlInAppContent(messageId: String, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - rejecter("", "Could not find message with id: \(messageId)", nil) - return - } - - guard let content = message.content as? IterableHtmlInAppContent else { - ITBError("Could not parse message content as HTML") - rejecter("", "Could not parse message content as HTML", nil) - return - } - - resolver(content.toDict()) - } - - @objc(trackEvent:dataFields:) - func trackEvent(name: String, dataFields: [AnyHashable: Any]?) { - ITBInfo() - - IterableAPI.track(event: name, dataFields: dataFields) - } - - @objc(updateUser:mergeNestedObjects:) - func updateUser(dataFields: [AnyHashable: Any], mergeNestedObjects: Bool) { - ITBInfo() - - IterableAPI.updateUser(dataFields, mergeNestedObjects: mergeNestedObjects) - } - - @objc(updateEmail:authToken:) - func updateEmail(email: String, with authToken: String?) { - ITBInfo() - - if let authToken = authToken { - IterableAPI.updateEmail(email, withToken: authToken, onSuccess: nil, onFailure: nil) - } else { - IterableAPI.updateEmail(email, onSuccess: nil, onFailure: nil) - } + + IterableAPI.attributionInfo = SerializationUtil.dictionaryToDecodable(dict: dict) + } + + @objc(trackPushOpenWithCampaignId:templateId:messageId:appAlreadyRunning:dataFields:) + func trackPushOpen( + campaignId: NSNumber, + templateId: NSNumber?, + messageId: String, + appAlreadyRunning: Bool, + dataFields: [AnyHashable: Any]? + ) { + ITBInfo() + + IterableAPI.track( + pushOpen: campaignId, + templateId: templateId, + messageId: messageId, + appAlreadyRunning: appAlreadyRunning, + dataFields: dataFields) + } + + @objc(updateCart:) + func updateCart(items: [[AnyHashable: Any]]) { + ITBInfo() + + IterableAPI.updateCart(items: items.compactMap(CommerceItem.from(dict:))) + } + + @objc(trackPurchase:items:dataFields:) + func trackPurchase( + total: NSNumber, + items: [[AnyHashable: Any]], + dataFields: [AnyHashable: Any]? + ) { + ITBInfo() + + IterableAPI.track( + purchase: total, + items: items.compactMap(CommerceItem.from(dict:)), + dataFields: dataFields) + } + + @objc(trackInAppOpen:location:) + func trackInAppOpen( + messageId: String, + location locationNumber: NSNumber + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return } - - @objc(handleAppLink:resolver:rejecter:) - func handle(appLink: String, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - if let url = URL(string: appLink) { - resolver(IterableAPI.handle(universalLink: url)) - } else { - rejecter("", "invalid URL", nil) - } + + IterableAPI.track(inAppOpen: message, location: InAppLocation.from(number: locationNumber)) + } + + @objc(trackInAppClick:location:clickedUrl:) + func trackInAppClick( + messageId: String, + location locationNumber: NSNumber, + clickedUrl: String + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return } - - // MARK: - SDK In-App Manager Functions - - @objc(getInAppMessages:rejecter:) - func getInAppMessages(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.inAppManager.getMessages().map { $0.toDict() }) - } - - @objc(getInboxMessages:rejecter:) - func getInboxMessages(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.inAppManager.getInboxMessages().map{ $0.toDict() }) - } - - @objc(getUnreadInboxMessagesCount:rejecter:) - func getUnreadInboxMessagesCount(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - resolver(IterableAPI.inAppManager.getUnreadInboxMessagesCount()) - } - - @objc(showMessage:consume:resolver:rejecter:) - func show(messageId: String, consume: Bool, resolver: @escaping RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - IterableAPI.inAppManager.show(message: message, consume: consume) { (url) in - resolver(url.map({$0.absoluteString})) - } + + IterableAPI.track( + inAppClick: message, location: InAppLocation.from(number: locationNumber), + clickedUrl: clickedUrl) + } + + @objc(trackInAppClose:location:source:clickedUrl:) + func trackInAppClose( + messageId: String, + location locationNumber: NSNumber, + source sourceNumber: NSNumber, + clickedUrl: String? + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return } - - @objc(removeMessage:location:source:) - func remove(messageId: String, location locationNumber: NSNumber, source sourceNumber: NSNumber) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - if let inAppDeleteSource = InAppDeleteSource.from(number: sourceNumber) { - IterableAPI.inAppManager.remove(message: message, - location: InAppLocation.from(number: locationNumber), - source: inAppDeleteSource) - } else { - IterableAPI.inAppManager.remove(message: message, - location: InAppLocation.from(number: locationNumber)) - } + + if let inAppCloseSource = InAppCloseSource.from(number: sourceNumber) { + IterableAPI.track( + inAppClose: message, + location: InAppLocation.from(number: locationNumber), + source: inAppCloseSource, + clickedUrl: clickedUrl) + } else { + IterableAPI.track( + inAppClose: message, + location: InAppLocation.from(number: locationNumber), + clickedUrl: clickedUrl) + } + } + + @objc(inAppConsume:location:source:) + func inAppConsume( + messageId: String, + location locationNumber: NSNumber, + source sourceNumber: NSNumber + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return } - - @objc(updateSubscriptions:unsubscribedChannelIds:unsubscribedMessageTypeIds:subscribedMessageTypeIds:campaignId:templateId:) - func updateSubscriptions(emailListIds: [NSNumber]?, - unsubscribedChannelIds: [NSNumber]?, - unsubscribedMessageTypeIds: [NSNumber]?, - subscribedMessageTypeIds: [NSNumber]?, - campaignId: NSNumber, - templateId: NSNumber) { - ITBInfo() - - let finalCampaignId: NSNumber? = campaignId.intValue <= 0 ? nil : campaignId - let finalTemplateId: NSNumber? = templateId.intValue <= 0 ? nil : templateId - - IterableAPI.updateSubscriptions(emailListIds, - unsubscribedChannelIds: unsubscribedChannelIds, - unsubscribedMessageTypeIds: unsubscribedMessageTypeIds, - subscribedMessageTypeIds: subscribedMessageTypeIds, - campaignId: finalCampaignId, - templateId: finalTemplateId) - } - - @objc(setReadForMessage:read:) - func setRead(for messageId: String, read: Bool) { - ITBInfo() - - guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { - ITBError("Could not find message with id: \(messageId)") - return - } - - IterableAPI.inAppManager.set(read: read, forMessage: message) - } - - @objc(setAutoDisplayPaused:) - func set(autoDisplayPaused: Bool) { - ITBInfo() - - DispatchQueue.main.async { - IterableAPI.inAppManager.isAutoDisplayPaused = autoDisplayPaused - } + + if let inAppDeleteSource = InAppDeleteSource.from(number: sourceNumber) { + IterableAPI.inAppConsume( + message: message, + location: InAppLocation.from(number: locationNumber), + source: inAppDeleteSource) + } else { + IterableAPI.inAppConsume( + message: message, + location: InAppLocation.from(number: locationNumber)) + } + } + + @objc(getHtmlInAppContentForMessage:resolver:rejecter:) + func getHtmlInAppContent( + messageId: String, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + rejecter("", "Could not find message with id: \(messageId)", nil) + return } - - // MARK: - SDK Inbox Session Tracking Functions - - @objc(startSession:) - func startSession(visibleRows: [[AnyHashable: Any]]) { - let serializedRows = InboxImpressionTracker.RowInfo.rowInfos(from: visibleRows) - - inboxSessionManager.startSession(visibleRows: serializedRows) - } - - @objc(endSession) - func endSession() { - guard let sessionInfo = inboxSessionManager.endSession() else { - ITBError("Could not find session info") - return - } - - let inboxSession = IterableInboxSession(id: sessionInfo.startInfo.id, - sessionStartTime: sessionInfo.startInfo.startTime, - sessionEndTime: Date(), - startTotalMessageCount: sessionInfo.startInfo.totalMessageCount, - startUnreadMessageCount: sessionInfo.startInfo.unreadMessageCount, - endTotalMessageCount: IterableAPI.inAppManager.getInboxMessages().count, - endUnreadMessageCount: IterableAPI.inAppManager.getUnreadInboxMessagesCount(), - impressions: sessionInfo.impressions.map { $0.toIterableInboxImpression() }) - - IterableAPI.track(inboxSession: inboxSession) - } - - @objc(updateVisibleRows:) - func updateVisibleRows(visibleRows: [[AnyHashable: Any]]) { - let serializedRows = InboxImpressionTracker.RowInfo.rowInfos(from: visibleRows) - - inboxSessionManager.updateVisibleRows(visibleRows: serializedRows) - } - - // MARK: - SDK Auth Manager Functions - - @objc(passAlongAuthToken:) - func passAlong(authToken: String?) { - ITBInfo() - - passedAuthToken = authToken - - authHandlerSemaphore.signal() - } - - // MARK: Private - private var shouldEmit = false - private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) - - // Handling in-app delegate - private var inAppShowResponse = InAppShowResponse.show - private var inAppHandlerSemaphore = DispatchSemaphore(value: 0) - - private var passedAuthToken: String? - private var authHandlerSemaphore = DispatchSemaphore(value: 0) - - private let inboxSessionManager = InboxSessionManager() - - private func initialize(withApiKey apiKey: String, - config configDict: [AnyHashable: Any], - version: String, - apiEndPointOverride: String? = nil, - resolver: @escaping RCTPromiseResolveBlock, - rejecter: @escaping RCTPromiseRejectBlock) { - ITBInfo() - - let launchOptions = createLaunchOptions() - let iterableConfig = IterableConfig.from(dict: configDict) - - if let urlHandlerPresent = configDict["urlHandlerPresent"] as? Bool, urlHandlerPresent == true { - iterableConfig.urlDelegate = self - } - - if let customActionHandlerPresent = configDict["customActionHandlerPresent"] as? Bool, customActionHandlerPresent == true { - iterableConfig.customActionDelegate = self - } - - if let inAppHandlerPresent = configDict["inAppHandlerPresent"] as? Bool, inAppHandlerPresent == true { - iterableConfig.inAppDelegate = self - } - - if let authHandlerPresent = configDict["authHandlerPresent"] as? Bool, authHandlerPresent { - iterableConfig.authDelegate = self - } - - // connect new inbox in-app payloads to the RN SDK - NotificationCenter.default.addObserver(self, selector: #selector(receivedIterableInboxChanged), name: Notification.Name.iterableInboxChanged, object: nil) - - DispatchQueue.main.async { - IterableAPI.initialize2(apiKey: apiKey, - launchOptions: launchOptions, - config: iterableConfig, - apiEndPointOverride: apiEndPointOverride) { result in - resolver(result) - } - - IterableAPI.setDeviceAttribute(name: "reactNativeSDKVersion", value: version) - } + + guard let content = message.content as? IterableHtmlInAppContent else { + ITBError("Could not parse message content as HTML") + rejecter("", "Could not parse message content as HTML", nil) + return } - - @objc(receivedIterableInboxChanged) - private func receivedIterableInboxChanged() { - guard shouldEmit else { - return - } - - sendEvent(withName: EventName.receivedIterableInboxChanged.rawValue, body: nil) + + resolver(content.toDict()) + } + + @objc(trackEvent:dataFields:) + func trackEvent(name: String, dataFields: [AnyHashable: Any]?) { + ITBInfo() + + IterableAPI.track(event: name, dataFields: dataFields) + } + + @objc(updateUser:mergeNestedObjects:) + func updateUser(dataFields: [AnyHashable: Any], mergeNestedObjects: Bool) { + ITBInfo() + + IterableAPI.updateUser(dataFields, mergeNestedObjects: mergeNestedObjects) + } + + @objc(updateEmail:authToken:) + func updateEmail(email: String, with authToken: String?) { + ITBInfo() + + if let authToken = authToken { + IterableAPI.updateEmail(email, withToken: authToken, onSuccess: nil, onFailure: nil) + } else { + IterableAPI.updateEmail(email, onSuccess: nil, onFailure: nil) } - - private func createLaunchOptions() -> [UIApplication.LaunchOptionsKey: Any]? { - guard let bridge = bridge else { - return nil - } - - return ReactIterableAPI.createLaunchOptions(bridgeLaunchOptions: bridge.launchOptions) - } - - private static func createLaunchOptions(bridgeLaunchOptions: [AnyHashable: Any]?) -> [UIApplication.LaunchOptionsKey: Any]? { - guard let bridgeLaunchOptions = bridgeLaunchOptions, - let remoteNotification = bridgeLaunchOptions[UIApplication.LaunchOptionsKey.remoteNotification.rawValue] else { - return nil - } - - var result = [UIApplication.LaunchOptionsKey: Any]() - result[UIApplication.LaunchOptionsKey.remoteNotification] = remoteNotification - - return result + } + + @objc(handleAppLink:resolver:rejecter:) + func handle(appLink: String, resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + if let url = URL(string: appLink) { + resolver(IterableAPI.handle(universalLink: url)) + } else { + rejecter("", "invalid URL", nil) + } + } + + // MARK: - SDK In-App Manager Functions + + @objc(getInAppMessages:rejecter:) + func getInAppMessages(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.inAppManager.getMessages().map { $0.toDict() }) + } + + @objc(getInboxMessages:rejecter:) + func getInboxMessages(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) { + ITBInfo() + + resolver(IterableAPI.inAppManager.getInboxMessages().map { $0.toDict() }) + } + + @objc(getUnreadInboxMessagesCount:rejecter:) + func getUnreadInboxMessagesCount( + resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock + ) { + ITBInfo() + + resolver(IterableAPI.inAppManager.getUnreadInboxMessagesCount()) + } + + @objc(showMessage:consume:resolver:rejecter:) + func show( + messageId: String, consume: Bool, resolver: @escaping RCTPromiseResolveBlock, + rejecter: RCTPromiseRejectBlock + ) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return + } + + IterableAPI.inAppManager.show(message: message, consume: consume) { (url) in + resolver(url.map({ $0.absoluteString })) + } + } + + @objc(removeMessage:location:source:) + func remove(messageId: String, location locationNumber: NSNumber, source sourceNumber: NSNumber) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return + } + + if let inAppDeleteSource = InAppDeleteSource.from(number: sourceNumber) { + IterableAPI.inAppManager.remove( + message: message, + location: InAppLocation.from(number: locationNumber), + source: inAppDeleteSource) + } else { + IterableAPI.inAppManager.remove( + message: message, + location: InAppLocation.from(number: locationNumber)) + } + } + + @objc( + updateSubscriptions:unsubscribedChannelIds:unsubscribedMessageTypeIds:subscribedMessageTypeIds: + campaignId:templateId: + ) + func updateSubscriptions( + emailListIds: [NSNumber]?, + unsubscribedChannelIds: [NSNumber]?, + unsubscribedMessageTypeIds: [NSNumber]?, + subscribedMessageTypeIds: [NSNumber]?, + campaignId: NSNumber, + templateId: NSNumber + ) { + ITBInfo() + + let finalCampaignId: NSNumber? = campaignId.intValue <= 0 ? nil : campaignId + let finalTemplateId: NSNumber? = templateId.intValue <= 0 ? nil : templateId + + IterableAPI.updateSubscriptions( + emailListIds, + unsubscribedChannelIds: unsubscribedChannelIds, + unsubscribedMessageTypeIds: unsubscribedMessageTypeIds, + subscribedMessageTypeIds: subscribedMessageTypeIds, + campaignId: finalCampaignId, + templateId: finalTemplateId) + } + + @objc(setReadForMessage:read:) + func setRead(for messageId: String, read: Bool) { + ITBInfo() + + guard let message = IterableAPI.inAppManager.getMessage(withId: messageId) else { + ITBError("Could not find message with id: \(messageId)") + return + } + + IterableAPI.inAppManager.set(read: read, forMessage: message) + } + + @objc(setAutoDisplayPaused:) + func set(autoDisplayPaused: Bool) { + ITBInfo() + + DispatchQueue.main.async { + IterableAPI.inAppManager.isAutoDisplayPaused = autoDisplayPaused + } + } + + // MARK: - SDK Inbox Session Tracking Functions + + @objc(startSession:) + func startSession(visibleRows: [[AnyHashable: Any]]) { + let serializedRows = InboxImpressionTracker.RowInfo.rowInfos(from: visibleRows) + + inboxSessionManager.startSession(visibleRows: serializedRows) + } + + @objc(endSession) + func endSession() { + guard let sessionInfo = inboxSessionManager.endSession() else { + ITBError("Could not find session info") + return + } + + let inboxSession = IterableInboxSession( + id: sessionInfo.startInfo.id, + sessionStartTime: sessionInfo.startInfo.startTime, + sessionEndTime: Date(), + startTotalMessageCount: sessionInfo.startInfo.totalMessageCount, + startUnreadMessageCount: sessionInfo.startInfo.unreadMessageCount, + endTotalMessageCount: IterableAPI.inAppManager.getInboxMessages().count, + endUnreadMessageCount: IterableAPI.inAppManager.getUnreadInboxMessagesCount(), + impressions: sessionInfo.impressions.map { $0.toIterableInboxImpression() }) + + IterableAPI.track(inboxSession: inboxSession) + } + + @objc(updateVisibleRows:) + func updateVisibleRows(visibleRows: [[AnyHashable: Any]]) { + let serializedRows = InboxImpressionTracker.RowInfo.rowInfos(from: visibleRows) + + inboxSessionManager.updateVisibleRows(visibleRows: serializedRows) + } + + // MARK: - SDK Auth Manager Functions + + @objc(passAlongAuthToken:) + func passAlong(authToken: String?) { + ITBInfo() + + passedAuthToken = authToken + + authHandlerSemaphore.signal() + } + + // MARK: Private + private var shouldEmit = false + private let _methodQueue = DispatchQueue(label: String(describing: ReactIterableAPI.self)) + + // Handling in-app delegate + private var inAppShowResponse = InAppShowResponse.show + private var inAppHandlerSemaphore = DispatchSemaphore(value: 0) + + private var passedAuthToken: String? + private var authHandlerSemaphore = DispatchSemaphore(value: 0) + + private let inboxSessionManager = InboxSessionManager() + + private func initialize( + withApiKey apiKey: String, + config configDict: [AnyHashable: Any], + version: String, + apiEndPointOverride: String? = nil, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock + ) { + ITBInfo() + + let launchOptions = createLaunchOptions() + let iterableConfig = IterableConfig.from(dict: configDict) + + if let urlHandlerPresent = configDict["urlHandlerPresent"] as? Bool, urlHandlerPresent == true { + iterableConfig.urlDelegate = self + } + + if let customActionHandlerPresent = configDict["customActionHandlerPresent"] as? Bool, + customActionHandlerPresent == true + { + iterableConfig.customActionDelegate = self + } + + if let inAppHandlerPresent = configDict["inAppHandlerPresent"] as? Bool, + inAppHandlerPresent == true + { + iterableConfig.inAppDelegate = self + } + + if let authHandlerPresent = configDict["authHandlerPresent"] as? Bool, authHandlerPresent { + iterableConfig.authDelegate = self + } + + // connect new inbox in-app payloads to the RN SDK + NotificationCenter.default.addObserver( + self, selector: #selector(receivedIterableInboxChanged), + name: Notification.Name.iterableInboxChanged, object: nil) + + DispatchQueue.main.async { + IterableAPI.initialize2( + apiKey: apiKey, + launchOptions: launchOptions, + config: iterableConfig, + apiEndPointOverride: apiEndPointOverride + ) { result in + resolver(result) + } + + IterableAPI.setDeviceAttribute(name: "reactNativeSDKVersion", value: version) } + } + + @objc(receivedIterableInboxChanged) + private func receivedIterableInboxChanged() { + guard shouldEmit else { + return + } + + sendEvent(withName: EventName.receivedIterableInboxChanged.rawValue, body: nil) + } + + private func createLaunchOptions() -> [UIApplication.LaunchOptionsKey: Any]? { + guard let bridge = bridge else { + return nil + } + + return ReactIterableAPI.createLaunchOptions(bridgeLaunchOptions: bridge.launchOptions) + } + + private static func createLaunchOptions(bridgeLaunchOptions: [AnyHashable: Any]?) + -> [UIApplication.LaunchOptionsKey: Any]? + { + guard let bridgeLaunchOptions = bridgeLaunchOptions, + let remoteNotification = bridgeLaunchOptions[ + UIApplication.LaunchOptionsKey.remoteNotification.rawValue] + else { + return nil + } + + var result = [UIApplication.LaunchOptionsKey: Any]() + result[UIApplication.LaunchOptionsKey.remoteNotification] = remoteNotification + + return result + } } extension ReactIterableAPI: IterableURLDelegate { - func handle(iterableURL url: URL, inContext context: IterableActionContext) -> Bool { - ITBInfo() - - guard shouldEmit else { - return false - } - - let contextDict = ReactIterableAPI.contextToDictionary(context: context) - sendEvent(withName: EventName.handleUrlCalled.rawValue, - body: ["url": url.absoluteString, - "context": contextDict] as [String : Any]) - - return true - } - - private static func contextToDictionary(context: IterableActionContext) -> [AnyHashable: Any] { - var result = [AnyHashable: Any]() - - let actionDict = actionToDictionary(action: context.action) - result["action"] = actionDict - result["source"] = context.source.rawValue - - return result - } - - private static func actionToDictionary(action: IterableAction) -> [AnyHashable: Any] { - var actionDict = [AnyHashable: Any]() - - actionDict["type"] = action.type - - if let data = action.data { - actionDict["data"] = data - } - - if let userInput = action.userInput { - actionDict["userInput"] = userInput - } - - return actionDict + func handle(iterableURL url: URL, inContext context: IterableActionContext) -> Bool { + ITBInfo() + + guard shouldEmit else { + return false + } + + let contextDict = ReactIterableAPI.contextToDictionary(context: context) + sendEvent( + withName: EventName.handleUrlCalled.rawValue, + body: [ + "url": url.absoluteString, + "context": contextDict, + ] as [String: Any]) + + return true + } + + private static func contextToDictionary(context: IterableActionContext) -> [AnyHashable: Any] { + var result = [AnyHashable: Any]() + + let actionDict = actionToDictionary(action: context.action) + result["action"] = actionDict + result["source"] = context.source.rawValue + + return result + } + + private static func actionToDictionary(action: IterableAction) -> [AnyHashable: Any] { + var actionDict = [AnyHashable: Any]() + + actionDict["type"] = action.type + + if let data = action.data { + actionDict["data"] = data } + + if let userInput = action.userInput { + actionDict["userInput"] = userInput + } + + return actionDict + } } extension ReactIterableAPI: IterableCustomActionDelegate { - func handle(iterableCustomAction action: IterableAction, inContext context: IterableActionContext) -> Bool { - ITBInfo() - - let actionDict = ReactIterableAPI.actionToDictionary(action: action) - let contextDict = ReactIterableAPI.contextToDictionary(context: context) - - sendEvent(withName: EventName.handleCustomActionCalled.rawValue, - body: ["action": actionDict, - "context": contextDict]) - - return true - } + func handle(iterableCustomAction action: IterableAction, inContext context: IterableActionContext) + -> Bool + { + ITBInfo() + + let actionDict = ReactIterableAPI.actionToDictionary(action: action) + let contextDict = ReactIterableAPI.contextToDictionary(context: context) + + sendEvent( + withName: EventName.handleCustomActionCalled.rawValue, + body: [ + "action": actionDict, + "context": contextDict, + ]) + + return true + } } extension ReactIterableAPI: IterableInAppDelegate { - func onNew(message: IterableInAppMessage) -> InAppShowResponse { - ITBInfo() - - guard shouldEmit else { - return .show - } - - sendEvent(withName: EventName.handleInAppCalled.rawValue, - body: message.toDict()) - - let timeoutResult = inAppHandlerSemaphore.wait(timeout: .now() + 2.0) - - if timeoutResult == .success { - ITBInfo("inAppShowResponse: \(inAppShowResponse == .show)") - return inAppShowResponse - } else { - ITBInfo("timed out") - return .show - } + func onNew(message: IterableInAppMessage) -> InAppShowResponse { + ITBInfo() + + guard shouldEmit else { + return .show } + + sendEvent( + withName: EventName.handleInAppCalled.rawValue, + body: message.toDict()) + + let timeoutResult = inAppHandlerSemaphore.wait(timeout: .now() + 2.0) + + if timeoutResult == .success { + ITBInfo("inAppShowResponse: \(inAppShowResponse == .show)") + return inAppShowResponse + } else { + ITBInfo("timed out") + return .show + } + } } extension ReactIterableAPI: IterableAuthDelegate { - func onAuthTokenRequested(completion: @escaping AuthTokenRetrievalHandler) { - ITBInfo() - - DispatchQueue.global(qos: .userInitiated).async { - self.sendEvent(withName: EventName.handleAuthCalled.rawValue, - body: nil) - - let authTokenRetrievalResult = self.authHandlerSemaphore.wait(timeout: .now() + 30.0) - - if authTokenRetrievalResult == .success { - ITBInfo("authTokenRetrieval successful") - - DispatchQueue.main.async { - completion(self.passedAuthToken) - } - - self.sendEvent(withName: EventName.handleAuthSuccessCalled.rawValue, - body: nil) - } else { - ITBInfo("authTokenRetrieval timed out") - - DispatchQueue.main.async { - completion(nil) - } - - self.sendEvent(withName: EventName.handleAuthFailureCalled.rawValue, - body: nil) - } + func onAuthTokenRequested(completion: @escaping AuthTokenRetrievalHandler) { + ITBInfo() + + DispatchQueue.global(qos: .userInitiated).async { + self.sendEvent( + withName: EventName.handleAuthCalled.rawValue, + body: nil) + + let authTokenRetrievalResult = self.authHandlerSemaphore.wait(timeout: .now() + 30.0) + + if authTokenRetrievalResult == .success { + ITBInfo("authTokenRetrieval successful") + + DispatchQueue.main.async { + completion(self.passedAuthToken) } + + self.sendEvent( + withName: EventName.handleAuthSuccessCalled.rawValue, + body: nil) + } else { + ITBInfo("authTokenRetrieval timed out") + + DispatchQueue.main.async { + completion(nil) + } + + self.sendEvent( + withName: EventName.handleAuthFailureCalled.rawValue, + body: nil) + } } - - func onTokenRegistrationFailed(_ reason: String?) { - - } + } + + func onTokenRegistrationFailed(_ reason: String?) { + + } } diff --git a/ios/RNIterableAPI/Serialization.swift b/ios/RNIterableAPI/Serialization.swift index cb27026be..478262924 100644 --- a/ios/RNIterableAPI/Serialization.swift +++ b/ios/RNIterableAPI/Serialization.swift @@ -4,265 +4,266 @@ // import Foundation - import IterableSDK struct SerializationUtil { - static func dateToInt(date: Date) -> Int { - Int(date.timeIntervalSince1970 * 1000) - } - - static func intToDate(int: Int) -> Date { - let seconds = Double(int) / 1000.0 // ms -> seconds - - return Date(timeIntervalSince1970: seconds) - } - - static func encodableToDictionary(encodable: T) -> [String: Any]? where T: Encodable { - guard let data = try? JSONEncoder().encode(encodable) else { - return nil - } - - return try? JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] + static func dateToInt(date: Date) -> Int { + Int(date.timeIntervalSince1970 * 1000) + } + + static func intToDate(int: Int) -> Date { + let seconds = Double(int) / 1000.0 // ms -> seconds + + return Date(timeIntervalSince1970: seconds) + } + + static func encodableToDictionary(encodable: T) -> [String: Any]? where T: Encodable { + guard let data = try? JSONEncoder().encode(encodable) else { + return nil } - - static func dictionaryToDecodable(dict: [AnyHashable: Any]) -> T? where T: Decodable { - guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else { - return nil - } - - return try? JSONDecoder().decode(T.self, from: data) + + return try? JSONSerialization.jsonObject(with: data, options: [.allowFragments]) + as? [String: Any] + } + + static func dictionaryToDecodable(dict: [AnyHashable: Any]) -> T? where T: Decodable { + guard let data = try? JSONSerialization.data(withJSONObject: dict, options: []) else { + return nil } + + return try? JSONDecoder().decode(T.self, from: data) + } } extension IterableConfig { - static func from(dict: [AnyHashable: Any]?) -> IterableConfig { - let config = IterableConfig() - - guard let dict = dict else { - return config - } - - if let allowedProtocols = dict["allowedProtocols"] as? [String] { - config.allowedProtocols = allowedProtocols - } - - if let pushIntegrationName = dict["pushIntegrationName"] as? String { - config.pushIntegrationName = pushIntegrationName - } - - if let pushPlatform = dict["pushPlatform"] as? NSNumber { - switch pushPlatform { - case 0: - config.pushPlatform = .sandbox - case 1: - config.pushPlatform = .production - default: - config.pushPlatform = .auto - } - } - - if let autoPushRegistration = dict["autoPushRegistration"] as? Bool { - config.autoPushRegistration = autoPushRegistration - } - - if let inAppDisplayInterval = dict["inAppDisplayInterval"] as? Double { - config.inAppDisplayInterval = inAppDisplayInterval - } - - if let expiringAuthTokenRefreshPeriod = dict["expiringAuthTokenRefreshPeriod"] as? TimeInterval { - config.expiringAuthTokenRefreshPeriod = expiringAuthTokenRefreshPeriod - } - - if let logLevelNumber = dict["logLevel"] as? NSNumber { - config.logDelegate = createLogDelegate(logLevelNumber: logLevelNumber) - } - - if let useInMemoryStorageForInApp = dict["useInMemoryStorageForInApps"] as? Bool { - config.useInMemoryStorageForInApps = useInMemoryStorageForInApp - } - - if let dataRegion = dict["dataRegion"] as? NSNumber { - switch dataRegion { - case 0: - config.dataRegion = IterableDataRegion.US - case 1: - config.dataRegion = IterableDataRegion.EU - default: - config.dataRegion = IterableDataRegion.US - } - } - - - return config + static func from(dict: [AnyHashable: Any]?) -> IterableConfig { + let config = IterableConfig() + + guard let dict = dict else { + return config } - - private static func createLogDelegate(logLevelNumber: NSNumber) -> IterableLogDelegate { - DefaultLogDelegate(minLogLevel: LogLevel.from(number: logLevelNumber)) + + if let allowedProtocols = dict["allowedProtocols"] as? [String] { + config.allowedProtocols = allowedProtocols + } + + if let pushIntegrationName = dict["pushIntegrationName"] as? String { + config.pushIntegrationName = pushIntegrationName + } + + if let pushPlatform = dict["pushPlatform"] as? NSNumber { + switch pushPlatform { + case 0: + config.pushPlatform = .sandbox + case 1: + config.pushPlatform = .production + default: + config.pushPlatform = .auto + } + } + + if let autoPushRegistration = dict["autoPushRegistration"] as? Bool { + config.autoPushRegistration = autoPushRegistration + } + + if let inAppDisplayInterval = dict["inAppDisplayInterval"] as? Double { + config.inAppDisplayInterval = inAppDisplayInterval + } + + if let expiringAuthTokenRefreshPeriod = dict["expiringAuthTokenRefreshPeriod"] as? TimeInterval + { + config.expiringAuthTokenRefreshPeriod = expiringAuthTokenRefreshPeriod + } + + if let logLevelNumber = dict["logLevel"] as? NSNumber { + config.logDelegate = createLogDelegate(logLevelNumber: logLevelNumber) + } + + if let useInMemoryStorageForInApp = dict["useInMemoryStorageForInApps"] as? Bool { + config.useInMemoryStorageForInApps = useInMemoryStorageForInApp } + + if let dataRegion = dict["dataRegion"] as? NSNumber { + switch dataRegion { + case 0: + config.dataRegion = IterableDataRegion.US + case 1: + config.dataRegion = IterableDataRegion.EU + default: + config.dataRegion = IterableDataRegion.US + } + } + + return config + } + + private static func createLogDelegate(logLevelNumber: NSNumber) -> IterableLogDelegate { + DefaultLogDelegate(minLogLevel: LogLevel.from(number: logLevelNumber)) + } } extension CommerceItem { - static func from(dict: [AnyHashable: Any]) -> CommerceItem? { - guard let id = dict["id"] as? String else { - return nil - } - - guard let name = dict["name"] as? String else { - return nil - } - - guard let price = dict["price"] as? NSNumber else { - return nil - } - - guard let quantity = dict["quantity"] as? UInt else { - return nil - } - - let sku = dict["sku"] as? String - let description = dict["description"] as? String - let url = dict["url"] as? String - let imageUrl = dict["imageUrl"] as? String - let categories = dict["categories"] as? [String] - let dataFields = dict["dataFields"] as? [AnyHashable: Any] - - return CommerceItem(id: id, - name: name, - price: price, - quantity: quantity, - sku: sku, - description: description, - url: url, - imageUrl: imageUrl, - categories: categories, - dataFields: dataFields) + static func from(dict: [AnyHashable: Any]) -> CommerceItem? { + guard let id = dict["id"] as? String else { + return nil + } + + guard let name = dict["name"] as? String else { + return nil } + + guard let price = dict["price"] as? NSNumber else { + return nil + } + + guard let quantity = dict["quantity"] as? UInt else { + return nil + } + + let sku = dict["sku"] as? String + let description = dict["description"] as? String + let url = dict["url"] as? String + let imageUrl = dict["imageUrl"] as? String + let categories = dict["categories"] as? [String] + let dataFields = dict["dataFields"] as? [AnyHashable: Any] + + return CommerceItem( + id: id, + name: name, + price: price, + quantity: quantity, + sku: sku, + description: description, + url: url, + imageUrl: imageUrl, + categories: categories, + dataFields: dataFields) + } } extension IterableInAppTrigger { - func toDict() -> [AnyHashable: Any] { - var dict = [AnyHashable: Any]() - dict["type"] = self.type.rawValue - return dict - } + func toDict() -> [AnyHashable: Any] { + var dict = [AnyHashable: Any]() + dict["type"] = self.type.rawValue + return dict + } } extension UIEdgeInsets { - func toDict() -> [AnyHashable: Any] { - var dict = [AnyHashable: Any]() - dict["top"] = top - dict["left"] = left - dict["bottom"] = bottom - dict["right"] = right - return dict - } + func toDict() -> [AnyHashable: Any] { + var dict = [AnyHashable: Any]() + dict["top"] = top + dict["left"] = left + dict["bottom"] = bottom + dict["right"] = right + return dict + } } extension IterableInboxMetadata { - func toDict() -> [AnyHashable: Any]? { - var dict = [AnyHashable: Any]() - dict["title"] = title - dict["subtitle"] = subtitle - dict["icon"] = icon - return dict - } + func toDict() -> [AnyHashable: Any]? { + var dict = [AnyHashable: Any]() + dict["title"] = title + dict["subtitle"] = subtitle + dict["icon"] = icon + return dict + } } extension IterableInAppMessage { - func toDict() -> [AnyHashable: Any] { - var dict = [AnyHashable: Any]() - dict["messageId"] = messageId - dict["campaignId"] = campaignId - dict["trigger"] = trigger.toDict() - dict["createdAt"] = createdAt.map (SerializationUtil.dateToInt(date:)) - dict["expiresAt"] = expiresAt.map (SerializationUtil.dateToInt(date:)) - dict["saveToInbox"] = saveToInbox - dict["inboxMetadata"] = inboxMetadata?.toDict() ?? nil - dict["customPayload"] = customPayload - dict["read"] = read - dict["priorityLevel"] = priorityLevel - - return dict - } + func toDict() -> [AnyHashable: Any] { + var dict = [AnyHashable: Any]() + dict["messageId"] = messageId + dict["campaignId"] = campaignId + dict["trigger"] = trigger.toDict() + dict["createdAt"] = createdAt.map(SerializationUtil.dateToInt(date:)) + dict["expiresAt"] = expiresAt.map(SerializationUtil.dateToInt(date:)) + dict["saveToInbox"] = saveToInbox + dict["inboxMetadata"] = inboxMetadata?.toDict() ?? nil + dict["customPayload"] = customPayload + dict["read"] = read + dict["priorityLevel"] = priorityLevel + + return dict + } } extension IterableHtmlInAppContent { - func toDict() -> [AnyHashable: Any] { - var dict = [AnyHashable: Any]() - dict["type"] = type.rawValue - dict["edgeInsets"] = edgeInsets.toDict() - dict["html"] = html - return dict - } + func toDict() -> [AnyHashable: Any] { + var dict = [AnyHashable: Any]() + dict["type"] = type.rawValue + dict["edgeInsets"] = edgeInsets.toDict() + dict["html"] = html + return dict + } } extension InAppLocation { - static func from(number: NSNumber) -> InAppLocation { - if let value = number as? Int { - return InAppLocation(rawValue: value) ?? .inApp - } else { - return .inApp - } + static func from(number: NSNumber) -> InAppLocation { + if let value = number as? Int { + return InAppLocation(rawValue: value) ?? .inApp + } else { + return .inApp } + } } extension InAppCloseSource { - static func from(number: NSNumber) -> InAppCloseSource? { - guard let value = number as? Int else { - return nil - } - - return InAppCloseSource(rawValue: value) + static func from(number: NSNumber) -> InAppCloseSource? { + guard let value = number as? Int else { + return nil } + + return InAppCloseSource(rawValue: value) + } } extension InAppDeleteSource { - static func from(number: NSNumber) -> InAppDeleteSource? { - guard let value = number as? Int else { - return nil - } - - return InAppDeleteSource(rawValue: value) + static func from(number: NSNumber) -> InAppDeleteSource? { + guard let value = number as? Int else { + return nil } + + return InAppDeleteSource(rawValue: value) + } } extension InAppShowResponse { - static func from(number: NSNumber) -> InAppShowResponse { - if let value = number as? Int { - return InAppShowResponse(rawValue: value) ?? .show - } else { - return .show - } + static func from(number: NSNumber) -> InAppShowResponse { + if let value = number as? Int { + return InAppShowResponse(rawValue: value) ?? .show + } else { + return .show } + } } extension LogLevel { - static func from(number: NSNumber) -> LogLevel { - if let value = number as? Int { - return LogLevel(rawValue: value) ?? .info - } else { - return .info - } + static func from(number: NSNumber) -> LogLevel { + if let value = number as? Int { + return LogLevel(rawValue: value) ?? .info + } else { + return .info } + } } extension InboxImpressionTracker.RowInfo { - static func from(dict: [AnyHashable: Any]) -> InboxImpressionTracker.RowInfo? { - guard let messageId = dict["messageId"] as? String else { - return nil - } - - guard let silentInbox = dict["silentInbox"] as? Bool else { - return nil - } - - let rowInfo = InboxImpressionTracker.RowInfo(messageId: messageId, silentInbox: silentInbox) - - return rowInfo + static func from(dict: [AnyHashable: Any]) -> InboxImpressionTracker.RowInfo? { + guard let messageId = dict["messageId"] as? String else { + return nil } - - static func rowInfos(from rows: [[AnyHashable: Any]]) -> [InboxImpressionTracker.RowInfo] { - return rows.compactMap(InboxImpressionTracker.RowInfo.from(dict:)) + + guard let silentInbox = dict["silentInbox"] as? Bool else { + return nil } + + let rowInfo = InboxImpressionTracker.RowInfo(messageId: messageId, silentInbox: silentInbox) + + return rowInfo + } + + static func rowInfos(from rows: [[AnyHashable: Any]]) -> [InboxImpressionTracker.RowInfo] { + return rows.compactMap(InboxImpressionTracker.RowInfo.from(dict:)) + } } From 144b5ea41d4502f40e8b6f1a6248317f2eae5eef Mon Sep 17 00:00:00 2001 From: Loren Posen Date: Sat, 2 Aug 2025 09:32:57 -0700 Subject: [PATCH 2/2] chore: add .clang-format configuration and update Swift/cpp files for consistent formatting --- .clang-format | 30 ++++ .../ReactNativeSdkExample-Bridging-Header.h | 3 +- .../ReactNativeSdkExample/AppDelegate.swift | 15 +- ...actNativeSdkExampleTests-Bridging-Header.h | 4 +- .../RNIterable-Bridging-Header.h | 3 +- ios/RNIterableAPI/RNIterableAPI.h | 1 - ios/RNIterableAPI/RNIterableAPI.mm | 160 +++++++++--------- 7 files changed, 119 insertions(+), 97 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..1def6583b --- /dev/null +++ b/.clang-format @@ -0,0 +1,30 @@ +--- +BasedOnStyle: LLVM +Language: Cpp +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +BinPackArguments: false +BinPackParameters: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 2 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Never +ReflowComments: true +SortIncludes: false +SpaceBeforeParens: Never +SpaceInEmptyParentheses: false +UseTab: Never + +... diff --git a/example/ios/ReactNativeSdkExample-Bridging-Header.h b/example/ios/ReactNativeSdkExample-Bridging-Header.h index e11d920b1..339994e93 100644 --- a/example/ios/ReactNativeSdkExample-Bridging-Header.h +++ b/example/ios/ReactNativeSdkExample-Bridging-Header.h @@ -1,3 +1,4 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // diff --git a/example/ios/ReactNativeSdkExample/AppDelegate.swift b/example/ios/ReactNativeSdkExample/AppDelegate.swift index 1f3fbca7c..5b9504eb5 100644 --- a/example/ios/ReactNativeSdkExample/AppDelegate.swift +++ b/example/ios/ReactNativeSdkExample/AppDelegate.swift @@ -5,10 +5,10 @@ // Created by Loren Posen on 6/11/25. // -import UIKit import React -import React_RCTAppDelegate import ReactAppDependencyProvider +import React_RCTAppDelegate +import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -46,11 +46,10 @@ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { } override func bundleURL() -> URL? { -#if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") -#else - Bundle.main.url(forResource: "main", withExtension: "jsbundle") -#endif + #if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") + #else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") + #endif } } - diff --git a/example/ios/ReactNativeSdkExampleTests-Bridging-Header.h b/example/ios/ReactNativeSdkExampleTests-Bridging-Header.h index 1b2cb5d6d..339994e93 100644 --- a/example/ios/ReactNativeSdkExampleTests-Bridging-Header.h +++ b/example/ios/ReactNativeSdkExampleTests-Bridging-Header.h @@ -1,4 +1,4 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // - diff --git a/ios/RNIterableAPI/RNIterable-Bridging-Header.h b/ios/RNIterableAPI/RNIterable-Bridging-Header.h index 35adb4320..01c1c3fb6 100644 --- a/ios/RNIterableAPI/RNIterable-Bridging-Header.h +++ b/ios/RNIterableAPI/RNIterable-Bridging-Header.h @@ -1,5 +1,6 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // #import diff --git a/ios/RNIterableAPI/RNIterableAPI.h b/ios/RNIterableAPI/RNIterableAPI.h index 26bbf81fe..6cecb9d4f 100644 --- a/ios/RNIterableAPI/RNIterableAPI.h +++ b/ios/RNIterableAPI/RNIterableAPI.h @@ -6,4 +6,3 @@ // Copyright © 2025 Iterable. All rights reserved. // #import - diff --git a/ios/RNIterableAPI/RNIterableAPI.mm b/ios/RNIterableAPI/RNIterableAPI.mm index dc40a6e12..f35b32188 100644 --- a/ios/RNIterableAPI/RNIterableAPI.mm +++ b/ios/RNIterableAPI/RNIterableAPI.mm @@ -4,136 +4,128 @@ // #import "RNIterableAPI.h" -@interface RCT_EXTERN_REMAP_MODULE(RNIterableAPI, ReactIterableAPI, NSObject) +@interface RCT_EXTERN_REMAP_MODULE (RNIterableAPI, ReactIterableAPI, NSObject) // MARK: - Native SDK Functions -RCT_EXTERN_METHOD(initializeWithApiKey: (nonnull NSString *) apiKey - config: (nonnull NSDictionary *) config - version: (nonnull NSString *) version - resolver: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(initializeWithApiKey : (nonnull NSString *)apiKey config : ( + nonnull NSDictionary *)config version : (nonnull NSString *) + version resolver : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(initialize2WithApiKey: (nonnull NSString *) apiKey - config: (nonnull NSDictionary *) config - apiEndPointOverride: (nonnull NSString *) apiEndPoint - version: (nonnull NSString *) version - resolver: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(initialize2WithApiKey : (nonnull NSString *)apiKey config : ( + nonnull NSDictionary *)config apiEndPointOverride : (nonnull NSString *) + apiEndPoint version : (nonnull NSString *) + version resolver : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(setEmail: (NSString *) email - authToken: (NSString *) authToken) +RCT_EXTERN_METHOD(setEmail : (NSString *)email authToken : (NSString *) + authToken) -RCT_EXTERN_METHOD(getEmail: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getEmail : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(setUserId: (NSString *) userId - authToken: (NSString *) authToken) +RCT_EXTERN_METHOD(setUserId : (NSString *)userId authToken : (NSString *) + authToken) -RCT_EXTERN_METHOD(getUserId: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getUserId : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) // MARK: - Iterable API Request Functions RCT_EXTERN_METHOD(disableDeviceForCurrentUser) -RCT_EXTERN_METHOD(setInAppShowResponse: (nonnull NSNumber *) inAppShowResponse) +RCT_EXTERN_METHOD(setInAppShowResponse : (nonnull NSNumber *)inAppShowResponse) -RCT_EXTERN_METHOD(getLastPushPayload: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getLastPushPayload : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(getAttributionInfo: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getAttributionInfo : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(setAttributionInfo: (NSDictionary *) attributionInfo) +RCT_EXTERN_METHOD(setAttributionInfo : (NSDictionary *)attributionInfo) -RCT_EXTERN_METHOD(trackPushOpenWithCampaignId: (nonnull NSNumber *) campaignId - templateId: (nonnull NSNumber *) templateId - messageId: (nonnull NSString *) messageId - appAlreadyRunning: (BOOL) appAlreadyRunning - dataFields: (NSDictionary *) dataFields) +RCT_EXTERN_METHOD( + trackPushOpenWithCampaignId : (nonnull NSNumber *)campaignId templateId : ( + nonnull NSNumber *)templateId messageId : (nonnull NSString *) + messageId appAlreadyRunning : (BOOL) + appAlreadyRunning dataFields : (NSDictionary *)dataFields) -RCT_EXTERN_METHOD(updateCart: (NSArray *) items) +RCT_EXTERN_METHOD(updateCart : (NSArray *)items) -RCT_EXTERN_METHOD(trackPurchase: (nonnull NSNumber *) total - items: (NSArray *) items - dataFields: (NSDictionary *) dataFields) +RCT_EXTERN_METHOD(trackPurchase : (nonnull NSNumber *)total items : (NSArray *) + items dataFields : (NSDictionary *)dataFields) -RCT_EXTERN_METHOD(trackInAppOpen: (NSString *) messageId - location: (nonnull NSNumber *) location) +RCT_EXTERN_METHOD(trackInAppOpen : (NSString *) + messageId location : (nonnull NSNumber *)location) -RCT_EXTERN_METHOD(trackInAppClick: (nonnull NSString *) messageId - location: (nonnull NSNumber *) location - clickedUrl: (nonnull NSString *) clickedUrl) +RCT_EXTERN_METHOD(trackInAppClick : (nonnull NSString *)messageId location : ( + nonnull NSNumber *)location clickedUrl : (nonnull NSString *)clickedUrl) -RCT_EXTERN_METHOD(trackInAppClose: (nonnull NSString *) messageId - location: (nonnull NSNumber *) location - source: (nonnull NSNumber *) source - clickedUrl: (NSString *) clickedUrl) +RCT_EXTERN_METHOD(trackInAppClose : (nonnull NSString *)messageId location : ( + nonnull NSNumber *)location source : (nonnull NSNumber *) + source clickedUrl : (NSString *)clickedUrl) -RCT_EXTERN_METHOD(inAppConsume: (nonnull NSString *) messageId - location: (nonnull NSNumber *) location - source: (nonnull NSNumber *) source) +RCT_EXTERN_METHOD(inAppConsume : (nonnull NSString *)messageId location : ( + nonnull NSNumber *)location source : (nonnull NSNumber *)source) -RCT_EXTERN_METHOD(trackEvent: (nonnull NSString *) name - dataFields: (NSDictionary *) dataFields) +RCT_EXTERN_METHOD(trackEvent : (nonnull NSString *) + name dataFields : (NSDictionary *)dataFields) -RCT_EXTERN_METHOD(updateUser: (nonnull NSDictionary *) dataFields - mergeNestedObjects: (BOOL) mergeNestedObjects) +RCT_EXTERN_METHOD(updateUser : (nonnull NSDictionary *) + dataFields mergeNestedObjects : (BOOL)mergeNestedObjects) -RCT_EXTERN_METHOD(updateEmail: (nonnull NSString *) email - authToken: (NSString *) authToken) +RCT_EXTERN_METHOD(updateEmail : (nonnull NSString *) + email authToken : (NSString *)authToken) -RCT_EXTERN_METHOD(handleAppLink: (nonnull NSString *) appLink - resolver: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(handleAppLink : (nonnull NSString *)appLink resolver : ( + RCTPromiseResolveBlock)resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(updateSubscriptions: (NSArray *) emailListIds - unsubscribedChannelIds: (NSArray *) unsubscribedChannelIds - unsubscribedMessageTypeIds: (NSArray *) unsubscribedMessageTypeIds - subscribedMessageTypeIds: (NSArray *) subscribedMessageTypeIds - campaignId: (nonnull NSNumber *) campaignId - templateId: (nonnull NSNumber *) templateId) +RCT_EXTERN_METHOD( + updateSubscriptions : (NSArray *)emailListIds unsubscribedChannelIds : ( + NSArray *) + unsubscribedChannelIds unsubscribedMessageTypeIds : (NSArray *) + unsubscribedMessageTypeIds subscribedMessageTypeIds : (NSArray *) + subscribedMessageTypeIds campaignId : (nonnull NSNumber *) + campaignId templateId : (nonnull NSNumber *)templateId) // MARK: - SDK In-App Manager Functions -RCT_EXTERN_METHOD(getInAppMessages: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getInAppMessages : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(getHtmlInAppContentForMessage: (nonnull NSString *) messageId - resolver: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getHtmlInAppContentForMessage : (nonnull NSString *) + messageId resolver : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(getInboxMessages: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getInboxMessages : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(getUnreadInboxMessagesCount: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(getUnreadInboxMessagesCount : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(showMessage: (nonnull NSString *) messageId - consume: (nonnull BOOL) consume - resolver: (RCTPromiseResolveBlock) resolve - rejecter: (RCTPromiseRejectBlock) reject) +RCT_EXTERN_METHOD(showMessage : (nonnull NSString *)messageId consume : ( + nonnull BOOL)consume resolver : (RCTPromiseResolveBlock) + resolve rejecter : (RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(removeMessage: (nonnull NSString *) messageId - location: (nonnull NSNumber *) location - source: (nonnull NSNumber *) source) +RCT_EXTERN_METHOD(removeMessage : (nonnull NSString *)messageId location : ( + nonnull NSNumber *)location source : (nonnull NSNumber *)source) -RCT_EXTERN_METHOD(setReadForMessage: (nonnull NSString *) messageId - read: (BOOL) read) +RCT_EXTERN_METHOD(setReadForMessage : (nonnull NSString *) + messageId read : (BOOL)read) -RCT_EXTERN_METHOD(setAutoDisplayPaused: (BOOL) paused) +RCT_EXTERN_METHOD(setAutoDisplayPaused : (BOOL)paused) // MARK: - SDK Inbox Session Tracking Functions -RCT_EXTERN_METHOD(startSession: (nonnull NSArray *) visibleRows) +RCT_EXTERN_METHOD(startSession : (nonnull NSArray *)visibleRows) RCT_EXTERN_METHOD(endSession) -RCT_EXTERN_METHOD(updateVisibleRows: (nonnull NSArray *) visibleRows) +RCT_EXTERN_METHOD(updateVisibleRows : (nonnull NSArray *)visibleRows) // MARK: - SDK Auth Manager Functions -RCT_EXTERN_METHOD(passAlongAuthToken: (NSString *) authToken) +RCT_EXTERN_METHOD(passAlongAuthToken : (NSString *)authToken) @end