Skip to content

Commit

Permalink
feat: support init SDK with custom configuration (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhu-xiaowei authored Mar 25, 2024
1 parent 32f44a8 commit df48ac9
Show file tree
Hide file tree
Showing 20 changed files with 489 additions and 155 deletions.
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ let package = Package(
),
.testTarget(
name: "ClickstreamTests",
dependencies: ["Clickstream", .product(name: "Swifter", package: "swifter")]
dependencies: ["Clickstream", .product(name: "Swifter", package: "swifter")],
resources: [.process("amplifyconfiguration.json")]
)
]
)
62 changes: 45 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The Clickstream SDK supports iOS 13+.

Clickstream requires Xcode 13.4 or higher to build.

**1.Add Package**
### 1.Add Package

We use **Swift Package Manager** to distribute Clickstream Swift SDK, open your project in Xcode and select **File > Add Pckages**.

Expand All @@ -30,7 +30,7 @@ Enter the Clickstream Library for Swift GitHub repo URL (`https://github.com/aws

![](images/add_package_url.png)

**2.Parameter configuration**
### 2.Parameter configuration

Downlod your `amplifyconfiguration.json` file from your Clickstream solution control plane, and paste it to your project root folder:

Expand Down Expand Up @@ -62,15 +62,15 @@ Your `appId` and `endpoint` are already set up in it, here's an explanation of e
- **autoFlushEventsInterval**: event sending interval, the default is `10s`
- **isTrackAppExceptionEvents**: whether auto track exception event in app, default is `false`

**3.Initialize the SDK**
### 3.Initialize the SDK

Once you have configured the parameters, you need to initialize it in your app delegate's `application(_:didFinishLaunchingWithOptions:)` lifecycle method:

#### 3.1 Initialize the SDK with default configuration
```swift
import Clickstream
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
do {
try ClickstreamAnalytics.initSDK()
} catch {
Expand All @@ -80,6 +80,34 @@ func application(_ application: UIApplication, didFinishLaunchingWithOptions lau
}
```

#### 3.2 Initialize the SDK with global attributes and custom configuration

```swift
import Clickstream
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
let configuration = ClickstreamConfiguration()
.withAppId("your appId")
.withEndpoint("https://example.com/collect")
.withLogEvents(true)
.withInitialGlobalAttributes([
"_traffic_source_name": "Summer promotion",
"_traffic_source_medium": "Search engine"
])
try ClickstreamAnalytics.initSDK(configuration)
} catch {
assertionFailure("Fail to initialize ClickstreamAnalytics: \(error)")
}
return true
}
```

By default, we will use the configurations in `amplifyconfiguration.json` file. If you add a custom configuration, the added configuration items will override the default values.

You can also add all the configuration parameters you need in the `initSDK` method without using the `amplifyconfiguration.json` file.

#### 3.3 SwiftUI configuration
If your project is developed with SwiftUI, you need to create an application delegate and attach it to your `App` through `UIApplicationDelegateAdaptor`.

```swift
Expand All @@ -94,24 +122,24 @@ struct YourApp: App {
}
```

You also need to disable swzzling by setting `configuration.isTrackScreenViewEvents = false`, see the next configuration steps.
Clickstream Swift SDK depends on method swizzling to automatically record screen views. SwiftUI apps must [record screen view events manually](#record-screen-view-events-manually), and disable automatic tracking of screen view events by setting `configuration.withTrackScreenViewEvents(false)` when the SDK is initialized.

**4.Configure the SDK**
### 4. Update Configuration

```swift
import Clickstream

// config the sdk after initialize.
// configure the sdk after initialize.
do {
var configuration = try ClickstreamAnalytics.getClickstreamConfiguration()
configuration.appId = "appId"
configuration.endpoint = "https://example.com/collect"
configuration.authCookie = "your authentication cookie"
configuration.sessionTimeoutDuration = 1800000
configuration.isTrackScreenViewEvents = true
configuration.isTrackUserEngagementEvents = true
configuration.isLogEvents = true
configuration.isCompressEvents = true
let configuration = try ClickstreamAnalytics.getClickstreamConfiguration()
configuration.withAppId("your appId")
.withEndpoint("https://example.com/collect")
.withLogEvents(true)
.withCompressEvents(true)
.withSessionTimeoutDuration(1800000)
.withTrackAppExceptionEvents(true)
.withTrackScreenViewEvents(true)
.withTrackUserEngagementEvents(true)
} catch {
print("Failed to config ClickstreamAnalytics: \(error)")
}
Expand All @@ -136,7 +164,7 @@ let attributes: ClickstreamAttribute = [
]
ClickstreamAnalytics.recordEvent("testEvent", attributes)

// for record an event directly
// for record an event with event name
ClickstreamAnalytics.recordEvent("button_click")
```

Expand Down
87 changes: 79 additions & 8 deletions Sources/Clickstream/AWSClickstreamPlugin+Configure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,10 @@ extension AWSClickstreamPlugin {
}

/// Configure AWSClickstreamPlugin programatically using AWSClickstreamConfiguration
func configure(using configuration: AWSClickstreamConfiguration) throws {
let contextConfiguration = ClickstreamContextConfiguration(appId: configuration.appId,
endpoint: configuration.endpoint,
sendEventsInterval: configuration.sendEventsInterval,
isTrackAppExceptionEvents:
configuration.isTrackAppExceptionEvents,
isCompressEvents: configuration.isCompressEvents)
clickstream = try ClickstreamContext(with: contextConfiguration)
func configure(using amplifyConfigure: AWSClickstreamConfiguration) throws {
try mergeConfiguration(amplifyConfigure: amplifyConfigure)

clickstream = try ClickstreamContext(with: configuration)
let sessionClient = SessionClient(clickstream: clickstream)
clickstream.sessionClient = sessionClient
let eventRecorder = try EventRecorder(clickstream: clickstream)
Expand All @@ -61,6 +56,7 @@ extension AWSClickstreamPlugin {
autoFlushEventsTimer: autoFlushEventsTimer,
networkMonitor: networkMonitor
)
initGlobalAttributes()
log.debug("initialize Clickstream SDK successful")
sessionClient.handleAppStart()
}
Expand All @@ -82,4 +78,79 @@ extension AWSClickstreamPlugin {
)
)
}

/// Internal method to merge the configurations
func mergeConfiguration(amplifyConfigure: AWSClickstreamConfiguration) throws {
let defaultConfiguration = ClickstreamConfiguration.getDefaultConfiguration()
if (configuration.appId.isNilOrEmpty() && amplifyConfigure.appId.isEmpty) ||
(configuration.endpoint.isNilOrEmpty() && amplifyConfigure.endpoint.isEmpty)
{
throw ConfigurationError.unableToDecode(
"Configuration Error: `appId` and `endpoint` are required", """
Ensure they are correctly set in `amplifyconfiguration.json`\
or provided during SDK initialization with `initSDK()`
"""
)
}

if configuration.appId.isNilOrEmpty() {
defaultConfiguration.appId = amplifyConfigure.appId
} else {
defaultConfiguration.appId = configuration.appId
}
if configuration.endpoint.isNilOrEmpty() {
defaultConfiguration.endpoint = amplifyConfigure.endpoint
} else {
defaultConfiguration.endpoint = configuration.endpoint
}
if configuration.sendEventsInterval > 0 {
defaultConfiguration.sendEventsInterval = configuration.sendEventsInterval
} else if amplifyConfigure.sendEventsInterval > 0 {
defaultConfiguration.sendEventsInterval = amplifyConfigure.sendEventsInterval
}
if configuration.isTrackAppExceptionEvents != nil {
defaultConfiguration.isTrackAppExceptionEvents = configuration.isTrackAppExceptionEvents
} else if amplifyConfigure.isTrackAppExceptionEvents != nil {
defaultConfiguration.isTrackAppExceptionEvents = amplifyConfigure.isTrackAppExceptionEvents
}
if configuration.isCompressEvents != nil {
defaultConfiguration.isCompressEvents = configuration.isCompressEvents
} else if amplifyConfigure.isCompressEvents != nil {
defaultConfiguration.isCompressEvents = amplifyConfigure.isCompressEvents
}

mergeDefaultConfiguration(defaultConfiguration)
configuration = defaultConfiguration
}

/// Internal method to merge the default configurations
func mergeDefaultConfiguration(_ defaultConfiguration: ClickstreamConfiguration) {
if let isTrackScreenViewEvents = configuration.isTrackScreenViewEvents {
defaultConfiguration.isTrackScreenViewEvents = isTrackScreenViewEvents
}
if let isTrackUserEngagementEvents = configuration.isTrackUserEngagementEvents {
defaultConfiguration.isTrackUserEngagementEvents = isTrackUserEngagementEvents
}
if let isLogEvents = configuration.isLogEvents {
defaultConfiguration.isLogEvents = isLogEvents
}
if configuration.sessionTimeoutDuration > 0 {
defaultConfiguration.sessionTimeoutDuration = configuration.sessionTimeoutDuration
}
if configuration.authCookie != nil {
defaultConfiguration.authCookie = configuration.authCookie
}
if configuration.globalAttributes != nil {
defaultConfiguration.globalAttributes = configuration.globalAttributes
}
}

/// Internal method to add global attributes
func initGlobalAttributes() {
if let globalAttributes = configuration.globalAttributes {
for (key, value) in globalAttributes {
analyticsClient.addGlobalAttribute(value, forKey: key)
}
}
}
}
6 changes: 5 additions & 1 deletion Sources/Clickstream/AWSClickstreamPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ final class AWSClickstreamPlugin: AnalyticsCategoryPlugin {
"awsClickstreamPlugin"
}

var configuration: ClickstreamConfiguration

/// Instantiates an instance of the AWSClickstreamPlugin
init() {}
init(_ configuration: ClickstreamConfiguration? = nil) {
self.configuration = configuration ?? ClickstreamConfiguration()
}
}
27 changes: 23 additions & 4 deletions Sources/Clickstream/ClickstreamAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
//

import Amplify
import Foundation

/// ClickstreamAnalytics api for swift
public enum ClickstreamAnalytics {
/// Init ClickstreamAnalytics
public static func initSDK() throws {
try Amplify.add(plugin: AWSClickstreamPlugin())
try Amplify.configure()
public static func initSDK(_ configuration: ClickstreamConfiguration? = nil) throws {
try Amplify.add(plugin: AWSClickstreamPlugin(configuration))
try Amplify.configure(getAmplifyConfigurationSafely())
}

/// Use this method to record event
Expand Down Expand Up @@ -70,7 +71,7 @@ public enum ClickstreamAnalytics {

/// Get Clickstream configuration, please config it after initialize sdk
/// - Returns: ClickstreamContextConfiguration to modify the configuration of clickstream sdk
public static func getClickstreamConfiguration() throws -> ClickstreamContextConfiguration {
public static func getClickstreamConfiguration() throws -> ClickstreamConfiguration {
let plugin = try Amplify.Analytics.getPlugin(for: "awsClickstreamPlugin")
// swiftlint:disable force_cast
return (plugin as! AWSClickstreamPlugin).getEscapeHatch().configuration
Expand All @@ -89,6 +90,22 @@ public enum ClickstreamAnalytics {
Amplify.Analytics.enable()
}

static func getAmplifyConfigurationSafely(_ bundle: Bundle = Bundle.main) throws -> AmplifyConfiguration {
guard let path = bundle.path(forResource: "amplifyconfiguration", ofType: "json") else {
log.debug("Could not load default `amplifyconfiguration.json` file")
let plugins: [String: JSONValue] = [
"awsClickstreamPlugin": [
"appId": JSONValue.string(""),
"endpoint": JSONValue.string("")
]
]
let analyticsConfiguration = AnalyticsCategoryConfiguration(plugins: plugins)
return AmplifyConfiguration(analytics: analyticsConfiguration)
}
let url = URL(fileURLWithPath: path)
return try AmplifyConfiguration(configurationFile: url)
}

/// ClickstreamAnalytics preset events
public enum EventName {
public static let SCREEN_VIEW = "_screen_view"
Expand Down Expand Up @@ -133,3 +150,5 @@ public enum ClickstreamAnalytics {
public static let ITEM_CATEGORY5 = "item_category5"
}
}

extension ClickstreamAnalytics: ClickstreamLogger {}
7 changes: 6 additions & 1 deletion Sources/Clickstream/ClickstreamObjc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import Foundation
try ClickstreamAnalytics.initSDK()
}

/// Init the Clickstream sdk
public static func initSDK(_ configuration: ClickstreamConfiguration) throws {
try ClickstreamAnalytics.initSDK(configuration)
}

/// Use this method to record event
/// - Parameter eventName: the event name
public static func recordEvent(_ eventName: String) {
Expand Down Expand Up @@ -76,7 +81,7 @@ import Foundation

/// Get Clickstream configuration, please config it after initialize sdk
/// - Returns: ClickstreamContextConfiguration to modify the configuration of clickstream sdk
public static func getClickstreamConfiguration() throws -> ClickstreamContextConfiguration {
public static func getClickstreamConfiguration() throws -> ClickstreamConfiguration {
try ClickstreamAnalytics.getClickstreamConfiguration()
}

Expand Down
Loading

0 comments on commit df48ac9

Please sign in to comment.