Skip to content

Commit

Permalink
Merge branch 'embedded-messaging' into anelson/ootb-banner
Browse files Browse the repository at this point in the history
  • Loading branch information
Evan Greer committed Dec 16, 2023
2 parents 4d44380 + 7b80c53 commit d3a408b
Show file tree
Hide file tree
Showing 30 changed files with 819 additions and 106 deletions.
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,49 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## 6.5.0-beta1

## 6.4.16
### Added
- `sendRequestWithRetries` function added as part of the `NetworkHelperclass`
- For an API request that results in a 500-level error response, the SDK now executes up to five retries. Before each of the final three attempts, there is a two-second delay.

### Changed
- updates `sendRequest` in `RequestProcessorUtil` to retry the API request that resulted in a 401 response upon receipt of a new JWT
- updates `NetworkHelper` class logic to use `sendRequestWithRetries`method which wraps the original `networkSession.makeRequest`
- When an API request fails with a 401 because of an invalid JWT token, the SDK now immediately requests a new JWT token for the signed-in user.

## 6.4.15
### Added
- This release allows you to use projects hosted on Iterable's EU data center. If your project is hosted on Iterable's [European data center (EUDC)](https://support.iterable.com/hc/articles/17572750887444), configure the SDK to use Iterable's EU-based API endpoints:

_Swift_

```swift
let config = IterableConfig()
config.dataRegion = IterableDataRegion.EU
IterableAPI.initialize(apiKey: "<YOUR_API_KEY>", launchOptions: launchOptions, config: config)
```

_Objective-C_

```objectivec
IterableConfig *config = [[IterableConfig alloc] init];
config.dataRegion = IterableDataRegion.EU;
[IterableAPI initializeWithApiKey:@"<YOUR_API_KEY>" launchOptions:launchOptions config:config];
```
### Fixed
- Offline Mode is now off by default. Offline mode components will only load when the `offlineMode` configuration for RequestHandler is set to true.
### Changed
- Offline mode configuration now persists throughout the current app session. Changes will take effect from the next app session.
## 6.4.14
### Added
- Success and Failure handlers can now be passed to following functions:
`InAppManager.remove`, `InAppManager.setRead`, `IterableAPI.setEmail` and `IterableAPI.setUserId`
## 6.4.13
### Added
- `ITBNotificationServiceExtension` has a new optional delegate in the scenario of wanting to receive and pass along push information (e.g. Firebase)
Expand Down
2 changes: 1 addition & 1 deletion Iterable-iOS-AppExtensions.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "Iterable-iOS-AppExtensions"
s.module_name = "IterableAppExtensions"
s.version = "6.4.13"
s.version = "6.5.0-beta1"
s.summary = "App Extensions for Iterable SDK"

s.description = <<-DESC
Expand Down
2 changes: 1 addition & 1 deletion Iterable-iOS-SDK.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "Iterable-iOS-SDK"
s.module_name = "IterableSDK"
s.version = "6.4.13"
s.version = "6.5.0-beta1"
s.summary = "Iterable's official SDK for iOS"

s.description = <<-DESC
Expand Down
24 changes: 22 additions & 2 deletions swift-sdk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@
5B5AA717284F1A6D0093FED4 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5AA710284F1A6D0093FED4 /* MockNetworkSession.swift */; };
5B6C3C1127CE871F00B9A753 /* NavInboxSessionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C3C1027CE871F00B9A753 /* NavInboxSessionUITests.swift */; };
5B88BC482805D09D004016E5 /* NetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88BC472805D09D004016E5 /* NetworkSession.swift */; };
9F76FFFF2B17884900962526 /* EmbeddedHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F76FFFE2B17884900962526 /* EmbeddedHelper.swift */; };
9FF05EAC2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EAD2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EAE2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EAF2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EB02AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EB12AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
9FF05EB22AFEA5FA005311F7 /* MockAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */; };
AC02480822791E2100495FB9 /* IterableInboxNavigationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC02480722791E2100495FB9 /* IterableInboxNavigationViewController.swift */; };
AC02CAA6234E50B5006617E0 /* RegistrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC02CAA5234E50B5006617E0 /* RegistrationTests.swift */; };
AC03094B21E532470003A288 /* InAppPersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC03094A21E532470003A288 /* InAppPersistence.swift */; };
Expand Down Expand Up @@ -598,6 +606,8 @@
5B6C3C1027CE871F00B9A753 /* NavInboxSessionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavInboxSessionUITests.swift; sourceTree = "<group>"; };
5B88BC472805D09D004016E5 /* NetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSession.swift; sourceTree = "<group>"; };
5BFC7CED27FC9AF300E77479 /* inbox-ui-tests-app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "inbox-ui-tests-app.entitlements"; sourceTree = "<group>"; };
9F76FFFE2B17884900962526 /* EmbeddedHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbeddedHelper.swift; sourceTree = "<group>"; };
9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAuthManager.swift; sourceTree = "<group>"; };
AC02480722791E2100495FB9 /* IterableInboxNavigationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IterableInboxNavigationViewController.swift; sourceTree = "<group>"; };
AC02CAA5234E50B5006617E0 /* RegistrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegistrationTests.swift; sourceTree = "<group>"; };
AC03094A21E532470003A288 /* InAppPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPersistence.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -909,6 +919,7 @@
551FA75C2988AC800072D0A9 /* Embedded Messaging */ = {
isa = PBXGroup;
children = (
9F76FFFE2B17884900962526 /* EmbeddedHelper.swift */,
1CCA911F2A27FA8700AEA213 /* MiscEmbeddedClasses.swift */,
1CCA91212A28075A00AEA213 /* EmbeddedSessionManager.swift */,
551FA75D2988AC930072D0A9 /* EmptyEmbeddedManager.swift */,
Expand Down Expand Up @@ -1439,6 +1450,7 @@
5588DF7D28C04494000697D7 /* MockUrlDelegate.swift */,
5588DF8D28C044DE000697D7 /* MockUrlOpener.swift */,
5588DFD528C04683000697D7 /* MockWebView.swift */,
9FF05EAB2AFEA5FA005311F7 /* MockAuthManager.swift */,
);
path = common;
sourceTree = "<group>";
Expand Down Expand Up @@ -2030,6 +2042,7 @@
buildActionMask = 2147483647;
files = (
1CCA91222A28075A00AEA213 /* EmbeddedSessionManager.swift in Sources */,
9F76FFFF2B17884900962526 /* EmbeddedHelper.swift in Sources */,
1CCA91202A27FA8700AEA213 /* MiscEmbeddedClasses.swift in Sources */,
AC31B042232AB53500BE25EB /* InboxImpressionTracker.swift in Sources */,
55D54656239AE5750093ED1E /* IterableLogUtil.swift in Sources */,
Expand Down Expand Up @@ -2179,6 +2192,7 @@
ACA2A91A24AB266F001DFD17 /* Mocks.swift in Sources */,
5588DFAB28C045AE000697D7 /* MockInAppFetcher.swift in Sources */,
AC29D05C24B5A7E000A9E019 /* CI.swift in Sources */,
9FF05EB12AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -2192,6 +2206,7 @@
AC2C668720D3435700D46CC9 /* ActionRunnerTests.swift in Sources */,
00CB31B621096129004ACDEC /* TestUtils.swift in Sources */,
AC89661E2124FBCE0051A6CD /* AutoRegistrationTests.swift in Sources */,
9FF05EAF2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
ACA8D1A921965B7D001B1332 /* InAppTests.swift in Sources */,
5588DFB928C045E3000697D7 /* MockInAppDelegate.swift in Sources */,
5588DFD128C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */,
Expand Down Expand Up @@ -2292,6 +2307,7 @@
buildActionMask = 2147483647;
files = (
5588DFB828C045E3000697D7 /* MockInAppDelegate.swift in Sources */,
9FF05EAE2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
5588DF7028C0442D000697D7 /* MockDateProvider.swift in Sources */,
5588DFF028C046FF000697D7 /* MockMessageViewControllerEventTracker.swift in Sources */,
ACB37AB124026C1E0093A8EA /* SampleInboxViewDelegateImplementations.swift in Sources */,
Expand Down Expand Up @@ -2355,6 +2371,7 @@
ACC6A8502323910D003CC4BE /* UITestsHelper.swift in Sources */,
5588DFAA28C045AE000697D7 /* MockInAppFetcher.swift in Sources */,
ACFF4287246569D300FDF10D /* CommonExtensions.swift in Sources */,
9FF05EB02AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -2381,6 +2398,7 @@
5588DFCE28C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */,
5588DFDE28C046B7000697D7 /* MockLocalStorage.swift in Sources */,
ACA8D1A52196309C001B1332 /* Common.swift in Sources */,
9FF05EAC2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
5588DFB628C045E3000697D7 /* MockInAppDelegate.swift in Sources */,
5588DF8628C044BE000697D7 /* MockCustomActionDelegate.swift in Sources */,
AC995F992166EE490099A184 /* CommonMocks.swift in Sources */,
Expand Down Expand Up @@ -2419,6 +2437,7 @@
5588DF7C28C04463000697D7 /* MockNotificationResponse.swift in Sources */,
5588DFD428C0465E000697D7 /* MockAPNSTypeChecker.swift in Sources */,
5588DFE428C046B7000697D7 /* MockLocalStorage.swift in Sources */,
9FF05EB22AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
ACC362C924D2CA8C002C67BA /* Common.swift in Sources */,
ACC362C524D2C190002C67BA /* TaskProcessorTests.swift in Sources */,
AC2AED4224EBC60C000EE5F3 /* TaskRunnerTests.swift in Sources */,
Expand All @@ -2437,6 +2456,7 @@
5588DFE728C046D7000697D7 /* MockInboxState.swift in Sources */,
ACFF42A424656CCE00FDF10D /* ViewController.swift in Sources */,
5588DF7F28C04494000697D7 /* MockUrlDelegate.swift in Sources */,
9FF05EAD2AFEA5FA005311F7 /* MockAuthManager.swift in Sources */,
5588DF8728C044BE000697D7 /* MockCustomActionDelegate.swift in Sources */,
5588DFD728C04683000697D7 /* MockWebView.swift in Sources */,
5588DFEF28C046FF000697D7 /* MockMessageViewControllerEventTracker.swift in Sources */,
Expand Down Expand Up @@ -2817,7 +2837,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = BP98Z28R86;
INFOPLIST_FILE = "Tests/unit-tests/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -2837,7 +2857,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = BP98Z28R86;
INFOPLIST_FILE = "Tests/unit-tests/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
5 changes: 5 additions & 0 deletions swift-sdk.xcodeproj/xcshareddata/xcschemes/swift-sdk.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@
BlueprintName = "offline-events-tests"
ReferencedContainer = "container:swift-sdk.xcodeproj">
</BuildableReference>
<SkippedTests>
<Test
Identifier = "RequestHandlerTests/testFeatureFlagTurnOnOfflineMode()">
</Test>
</SkippedTests>
</TestableReference>
</Testables>
</TestAction>
Expand Down
8 changes: 7 additions & 1 deletion swift-sdk/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ enum Const {
static let embeddedMessageClick = "embedded-messaging/events/click"
static let embeddedMessageDismiss = "embedded-messaging/events/dismiss"
static let embeddedMessageImpression = "embedded-messaging/events/impression"
static let trackEmbeddedSession = "embedded-messaging/events/impression"
static let trackEmbeddedSession = "embedded-messaging/events/session"
}

public enum UserDefault {
Expand Down Expand Up @@ -289,6 +289,11 @@ enum JsonValue {
}
}

public enum IterableDataRegion {
public static let US = "https://api.iterable.com/api/"
public static let EU = "https://api.eu.iterable.com/api/"
}

public protocol JsonValueRepresentable {
var jsonValue: Any { get }
}
Expand Down Expand Up @@ -374,6 +379,7 @@ enum MobileDeviceType: String, Codable {
case push
case universalLink
case inApp
case embedded
}

// Lowest level that will be logged. By default the LogLevel is set to LogLevel.info.
Expand Down
46 changes: 31 additions & 15 deletions swift-sdk/Internal/DependencyContainerProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ extension DependencyContainerProtocol {

func createEmbeddedManager(config: IterableConfig,
apiClient: ApiClientProtocol) -> IterableEmbeddedManagerProtocol {
IterableEmbeddedManager(apiClient: apiClient)
IterableEmbeddedManager(apiClient: apiClient,
urlDelegate: config.urlDelegate,
customActionDelegate: config.customActionDelegate,
urlOpener: urlOpener,
allowedProtocols: config.allowedProtocols)
}

func createRequestHandler(apiKey: String,
Expand All @@ -75,21 +79,33 @@ extension DependencyContainerProtocol {
networkSession: networkSession,
deviceMetadata: deviceMetadata,
dateProvider: dateProvider)
if let persistenceContextProvider = createPersistenceContextProvider() {
lazy var offlineProcessor: OfflineRequestProcessor? = nil
lazy var healthMonitor: HealthMonitor? = nil
guard let persistenceContextProvider = createPersistenceContextProvider() else {
return RequestHandler(onlineProcessor: onlineProcessor,
offlineProcessor: nil,
healthMonitor: nil,
offlineMode: offlineMode)
}
if offlineMode {

let healthMonitorDataProvider = createHealthMonitorDataProvider(persistenceContextProvider: persistenceContextProvider)
let healthMonitor = HealthMonitor(dataProvider: healthMonitorDataProvider,
dateProvider: dateProvider,
networkSession: networkSession)
let offlineProcessor = OfflineRequestProcessor(apiKey: apiKey,
authProvider: authProvider,
authManager: authManager,
endpoint: endpoint,
deviceMetadata: deviceMetadata,
taskScheduler: createTaskScheduler(persistenceContextProvider: persistenceContextProvider,
healthMonitor: healthMonitor),
taskRunner: createTaskRunner(persistenceContextProvider: persistenceContextProvider,
healthMonitor: healthMonitor),
notificationCenter: notificationCenter)

healthMonitor = HealthMonitor(dataProvider: healthMonitorDataProvider,
dateProvider: dateProvider,
networkSession: networkSession)
offlineProcessor = OfflineRequestProcessor(apiKey: apiKey,
authProvider: authProvider,
authManager: authManager,
endpoint: endpoint,
deviceMetadata: deviceMetadata,
taskScheduler: createTaskScheduler(persistenceContextProvider: persistenceContextProvider,
healthMonitor: healthMonitor!),
taskRunner: createTaskRunner(persistenceContextProvider: persistenceContextProvider,
healthMonitor: healthMonitor!),
notificationCenter: notificationCenter)


return RequestHandler(onlineProcessor: onlineProcessor,
offlineProcessor: offlineProcessor,
healthMonitor: healthMonitor,
Expand Down
82 changes: 82 additions & 0 deletions swift-sdk/Internal/EmbeddedHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// Copyright © 2023 Iterable. All rights reserved.
//
import UIKit

/// Utility Methods for embedded
/// All classes/structs are internal.
struct EmbeddedHelper {
enum EmbeddedClickedUrl {
case localResource(name: String) // applewebdata://abc-def/something => something
case iterableCustomAction(name: String) // iterable://something => something
case customAction(name: String) // action:something => something or itbl://something => something
case regularUrl(URL) // protocol://something => protocol://something
}

static func parse(embeddedUrl url: URL) -> EmbeddedClickedUrl? {
guard let scheme = UrlScheme.from(url: url) else {
ITBError("Request url contains an invalid scheme: \(url)")
return nil
}

switch scheme {
case .applewebdata:
ITBError("Request url contains an invalid scheme: \(url)")
guard let urlPath = getUrlPath(url: url) else {
return nil
}
return .localResource(name: urlPath)
case .iterable:
return .iterableCustomAction(name: dropScheme(urlString: url.absoluteString, scheme: scheme.rawValue))
case .action, .itbl:
return .customAction(name: dropScheme(urlString: url.absoluteString, scheme: scheme.rawValue))
case .other:
return .regularUrl(url)
}
}

private enum UrlScheme: String {
case applewebdata
case iterable
case action
case itbl // this is for backward compatibility and should be handled just like action://
case other

fileprivate static func from(url: URL) -> UrlScheme? {
guard let name = url.scheme else {
return nil
}

if let scheme = UrlScheme(rawValue: name.lowercased()) {
return scheme
} else {
return .other
}
}
}

// returns everything other than scheme, hostname and leading slashes
// so scheme://host/path#something => path#something
private static func getUrlPath(url: URL) -> String? {
guard let host = url.host else {
return nil
}

let urlArray = url.absoluteString.components(separatedBy: host)
guard urlArray.count > 1 else {
return nil
}

let urlPath = urlArray[1]
return dropLeadingSlashes(str: urlPath)
}

private static func dropLeadingSlashes(str: String) -> String {
String(str.drop { $0 == "/" })
}

private static func dropScheme(urlString: String, scheme: String) -> String {
let prefix = scheme + "://"
return String(urlString.dropFirst(prefix.count))
}
}
Loading

0 comments on commit d3a408b

Please sign in to comment.