Skip to content

Commit

Permalink
chore: address feedback
Browse files Browse the repository at this point in the history
Co-Authored-By: Anton Yarmolenko <[email protected]>
  • Loading branch information
rnr committed Jan 21, 2025
1 parent 6ba5fdc commit 099024b
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This workflow will test a Swift project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift

name: Unit Tests
name: Xcode

on:
- pull_request
Expand All @@ -12,7 +12,7 @@ concurrency:

jobs:
test-ios:
name: iOS 18.1
name: Build and Test (iOS 18.1)
runs-on: macos-latest
env:
DEVELOPER_DIR: "/Applications/Xcode_16.1.app/Contents/Developer"
Expand All @@ -22,12 +22,20 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

# Run Unit Tests
# Build
- name: Build
run: |
xcodebuild build\
-scheme EDXMobileAnalytics \
-destination 'platform=iOS Simulator,name=iPhone SE (3rd generation)' \
-skipPackagePluginValidation \
-skipMacroValidation
# Run Unit Tests
- name: Run tests
run: |
xcodebuild test \
-scheme EDXMobileAnalytics \
-sdk iphonesimulator \
-destination "OS=18.1,name=iPhone 16 Pro" \
-skipPackagePluginValidation \
-skipMacroValidation
22 changes: 0 additions & 22 deletions .github/workflows/xcodebuild.yml

This file was deleted.

20 changes: 19 additions & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "24865ce1a5bdb12bdf2082dc9a6b3b327986ee32238127921bf6e08f58b56e7f",
"originHash" : "aa75406ff97bf77af672c326455e75425b255f9dca089e8766327c96248e2f30",
"pins" : [
{
"identity" : "abseil-cpp-binary",
Expand Down Expand Up @@ -217,6 +217,15 @@
"version" : "1.28.2"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax.git",
"state" : {
"revision" : "0687f71944021d616d34d922343dcef086855920",
"version" : "600.0.1"
}
},
{
"identity" : "swiftlintplugins",
"kind" : "remoteSourceControl",
Expand All @@ -243,6 +252,15 @@
"revision" : "be9dbcc7b86811bc131539a20c6f9c2d3e56919f",
"version" : "2.9.1"
}
},
{
"identity" : "testablemacro",
"kind" : "remoteSourceControl",
"location" : "https://github.com/fernandolucheti/TestableMacro.git",
"state" : {
"revision" : "84b111ae97f2e5cb4d84115edd0ebc145147ee00",
"version" : "0.0.2"
}
}
],
"version" : 3
Expand Down
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ let package = Package(
.package(url: "https://github.com/segmentio/analytics-swift.git", from: "1.5.3"),
.package(url: "https://github.com/segment-integrations/analytics-swift-firebase", from: "1.3.5"),
.package(url: "https://github.com/braze-inc/braze-segment-swift.git", from: "2.2.0"),
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.57.0")
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.57.0"),
.package(url: "https://github.com/fernandolucheti/TestableMacro.git", from: "0.0.2")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand All @@ -30,7 +31,8 @@ let package = Package(
.product(name: "OEXFoundation", package: "openedx-app-foundation-ios"),
.product(name: "Segment", package: "analytics-swift"),
.product(name: "SegmentFirebase", package: "analytics-swift-firebase"),
.product(name: "SegmentBraze", package: "braze-segment-swift")
.product(name: "SegmentBraze", package: "braze-segment-swift"),
.product(name: "TestableMacro", package: "TestableMacro")
],
plugins: [
.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# EDXMobileAnalytics

[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE)

EDXMobileAnalytics is a Swift plugin for integrating analytics in edX iOS mobile applications. This plugin includes support for **Segment Analytics** and **Braze** (via Segment) to help developers efficiently track user behavior and events within their apps.

Expand Down Expand Up @@ -57,10 +57,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

private func initPlugins() {
// - Segment analytic
// - Segment analyticы
let SegmentAnalyticsService = SegmentAnalyticsService(
writeKey: "your_writeKey",
firebaseAnalyticSourceIsSegment: true // or false
addFirebaseAnalytics: true // or false
)
pluginManager.addPlugin(analyticsService: SegmentAnalyticsService)

Expand All @@ -69,12 +69,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
pluginManager.addPlugin(
pushNotificationsProvider:
BrazeProvider(
segmentAnalyticService: SegmentAnalyticsService
segmentAnalyticsService: SegmentAnalyticsService
),
pushNotificationsListener:
BrazeListener(
deepLinkManager: deepLinkManager,
segmentAnalyticService: SegmentAnalyticsService
segmentAnalyticsService: SegmentAnalyticsService
)
)

Expand Down Expand Up @@ -103,7 +103,7 @@ func logScreenEvent(_ event: String, parameters: [String: Any]?)

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
This project is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details.

## Contact

Expand Down
10 changes: 6 additions & 4 deletions Sources/EDXMobileAnalytics/Braze/BrazeListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@

import Foundation
import OEXFoundation
import TestableMacro

@Testable
public class BrazeListener: PushNotificationsListener {

private let deepLinkManager: DeepLinkManagerProtocol
private let segmentAnalyticService: SegmentAnalyticsServiceProtocol?
private let segmentAnalyticsService: SegmentAnalyticsServiceProtocol?

public init(
deepLinkManager: DeepLinkManagerProtocol,
segmentAnalyticService: SegmentAnalyticsServiceProtocol? = nil
segmentAnalyticsService: SegmentAnalyticsServiceProtocol? = nil
) {
self.deepLinkManager = deepLinkManager
self.segmentAnalyticService = segmentAnalyticService
self.segmentAnalyticsService = segmentAnalyticsService
}

public func shouldListenNotification(userinfo: [AnyHashable: Any]) -> Bool {
Expand All @@ -30,7 +32,7 @@ public class BrazeListener: PushNotificationsListener {
public func didReceiveRemoteNotification(userInfo: [AnyHashable: Any]) {
guard shouldListenNotification(userinfo: userInfo) else { return }

segmentAnalyticService?.receivedRemoteNotification(userInfo: userInfo)
segmentAnalyticsService?.receivedRemoteNotification(userInfo: userInfo)
deepLinkManager.processLinkFrom(userInfo: userInfo)
}
}
11 changes: 5 additions & 6 deletions Sources/EDXMobileAnalytics/Braze/BrazeProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import SegmentBraze

final public class BrazeProvider: PushNotificationsProvider {

private let segmentAnalyticService: SegmentAnalyticsServiceProtocol?
private let segmentAnalyticsService: SegmentAnalyticsServiceProtocol

public init(segmentAnalyticService: SegmentAnalyticsServiceProtocol? = nil) {
self.segmentAnalyticService = segmentAnalyticService
public init(segmentAnalyticsService: SegmentAnalyticsServiceProtocol) {
self.segmentAnalyticsService = segmentAnalyticsService
}

public func didRegisterWithDeviceToken(deviceToken: Data) {
guard let segmentService = segmentAnalyticService else { return }
segmentService.add(
segmentAnalyticsService.add(
plugin: BrazeDestination(
additionalConfiguration: { configuration in
configuration.logger.level = .info
Expand All @@ -29,6 +28,6 @@ final public class BrazeProvider: PushNotificationsProvider {
)
)

segmentService.registeredForRemoteNotifications(deviceToken: deviceToken)
segmentAnalyticsService.registeredForRemoteNotifications(deviceToken: deviceToken)
}
}
2 changes: 0 additions & 2 deletions Sources/EDXMobileAnalytics/EDXMobileAnalytics.swift

This file was deleted.

27 changes: 16 additions & 11 deletions Sources/EDXMobileAnalytics/Segment/SegmentAnalyticsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,56 +9,61 @@ import Foundation
import OEXFoundation
@preconcurrency import Segment
import SegmentFirebase
import TestableMacro

public protocol SegmentAnalyticsServiceProtocol: AnalyticsService {
func receivedRemoteNotification(userInfo: [AnyHashable: Any])
func registeredForRemoteNotifications(deviceToken: Data)
func add(plugin: Plugin)
}

@Testable
final public class SegmentAnalyticsService: SegmentAnalyticsServiceProtocol {
private let analytics: Analytics?
private let analytics: Analytics

// Init manager
public init(writeKey: String, firebaseAnalyticSourceIsSegment: Bool) {
public init(writeKey: String, addFirebaseAnalytics: Bool) {
let configuration = Configuration(writeKey: writeKey)
.trackApplicationLifecycleEvents(true)
.flushInterval(10)
analytics = Analytics(configuration: configuration)
if firebaseAnalyticSourceIsSegment {
analytics?.add(plugin: FirebaseDestination())
if addFirebaseAnalytics {
analytics.add(plugin: FirebaseDestination())
}
}

public func identify(id: String, username: String?, email: String?) {
guard let email = email, let username = username else { return }
guard let email = email, let username = username else {
assertionFailure("Email and Username are required for identifying user")
return
}
let traits: [String: String] = [
"email": email,
"username": username
]
analytics?.identify(userId: id, traits: traits)
analytics.identify(userId: id, traits: traits)
}

public func logEvent(_ event: String, parameters: [String: Any]?) {
analytics?.track(
analytics.track(
name: event,
properties: parameters
)
}

public func logScreenEvent(_ event: String, parameters: [String: Any]?) {
analytics?.screen(title: event, properties: parameters)
analytics.screen(title: event, properties: parameters)
}

public func receivedRemoteNotification(userInfo: [AnyHashable: Any]) {
analytics?.receivedRemoteNotification(userInfo: userInfo)
analytics.receivedRemoteNotification(userInfo: userInfo)
}

public func registeredForRemoteNotifications(deviceToken: Data) {
analytics?.registeredForRemoteNotifications(deviceToken: deviceToken)
analytics.registeredForRemoteNotifications(deviceToken: deviceToken)
}

public func add(plugin: Plugin) {
analytics?.add(plugin: plugin)
analytics.add(plugin: plugin)
}
}
19 changes: 9 additions & 10 deletions Tests/EDXMobileAnalyticsTests/BrazeListenerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ private extension BrazeListenerTests {
@MainActor
@Suite(".shouldListenNotification") struct ShouldListenNotification {
let deepLinkMananger = DeepLinkManagerProtocolMock()
let segmentAnalyticService = SegmentAnalyticsServiceMock()
let segmentAnalyticsService = SegmentAnalyticsServiceMock()
var brazeListener: BrazeListener {
BrazeListener(
deepLinkManager: deepLinkMananger,
segmentAnalyticService: segmentAnalyticService
segmentAnalyticsService: segmentAnalyticsService
)
}

Expand All @@ -34,26 +34,26 @@ private extension BrazeListenerTests {
@MainActor
@Suite(".didReceiveRemoteNotification") struct DidReceiveRemoteNotification {
let deepLinkMananger = DeepLinkManagerProtocolMock()
let segmentAnalyticService = SegmentAnalyticsServiceMock()
let segmentAnalyticsService = SegmentAnalyticsServiceMock()
var brazeListener: BrazeListener {
BrazeListener(
deepLinkManager: deepLinkMananger,
segmentAnalyticService: segmentAnalyticService
segmentAnalyticsService: segmentAnalyticsService
)
}

@Test("When shouldListenNotification returns false should do nothing") func check1() async throws {
brazeListener.didReceiveRemoteNotification(userInfo: TestData.dataWithoutNeededKey)
#expect(segmentAnalyticService.receivedRemoteNotificationCount == 0)
#expect(segmentAnalyticsService.receivedRemoteNotificationCount == 0)
#expect(deepLinkMananger.processLinkFromCallsCount == 0)
}

@Test("When shouldListenNotification returns true should call processLinkFrom") func check2() async throws {
brazeListener.didReceiveRemoteNotification(userInfo: TestData.dataWithNeededKey)
#expect(segmentAnalyticService.receivedRemoteNotificationCount == 1)
#expect(segmentAnalyticsService.receivedRemoteNotificationCount == 1)
#expect(deepLinkMananger.processLinkFromCallsCount == 1)
#expect(
segmentAnalyticService.receivedRemoteNotificationUserInfo["ab"] as? [String: String] ==
segmentAnalyticsService.receivedRemoteNotificationUserInfo["ab"] as? [String: String] ==
TestData.dataWithNeededKey["ab"] as? [String: String]
)
}
Expand All @@ -64,9 +64,8 @@ private extension BrazeListenerTests {
@Test("Should set default value") func check1() async throws {
let deepLinkMananger = DeepLinkManagerProtocolMock()
let brazeListener = BrazeListener(deepLinkManager: deepLinkMananger)
let analyticService = Mirror(reflecting: brazeListener)
.descendant("segmentAnalyticService") as? SegmentAnalyticsServiceProtocol
#expect(analyticService == nil)
let analyticsService = brazeListener.testHooks.segmentAnalyticsService
#expect(analyticsService == nil)
}
}
}
Loading

0 comments on commit 099024b

Please sign in to comment.