Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b7eff59
feat: Fix podspec default subspec and update Podfile deployment target
denischilik Jan 21, 2026
8bb980b
Add Swift target and move SceneDelegateHandler
denischilik Jan 21, 2026
01cf129
Move MPLogger to Swift target and update references
denischilik Jan 21, 2026
738dd30
Add Swift podspec and configure dependencies
denischilik Jan 21, 2026
974fc7f
Add mParticle_Apple_SDK_Swift.xcframework to artifacts and Tuist depe…
denischilik Jan 21, 2026
de192f4
fix linter commants
denischilik Jan 21, 2026
bef234d
Remove unused file
denischilik Jan 21, 2026
cf9d396
Fix linter comments
denischilik Jan 21, 2026
0175091
Swift target should support both platforms iOS and AppleTV
denischilik Jan 21, 2026
6df8fd9
Update version to match SDK version
denischilik Jan 21, 2026
d552808
Include local podspec
denischilik Jan 21, 2026
81a433a
Merge branch 'workstation/9.0-Release' into feat/SDKE-823-Create-sepa…
denischilik Jan 22, 2026
39e4a4a
MPLogLevel is part of public API we need to map between Swift and obj…
denischilik Jan 22, 2026
df08e93
We should import Swift code only in *.m files
denischilik Jan 22, 2026
9f2724d
Update imports to Internal so that Swift header should be private
denischilik Jan 22, 2026
2193289
No needs to build Swift target
denischilik Jan 22, 2026
a859e8d
Fix MPDevice
denischilik Jan 22, 2026
9aab465
Remove commented code
denischilik Jan 22, 2026
6a83abc
Cleanup script
denischilik Jan 22, 2026
db72fc8
Fix forced unwrap
denischilik Jan 23, 2026
60d26dc
Remove empty file
denischilik Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
run: find . -path '*.podspec' -exec perl -pi -e 's/.+\.social_media_url.+//' {} \;

- name: Lint with CocoaPods
run: pod lib lint --subspec=mParticleNoLocation
run: pod lib lint --subspec=mParticleNoLocation --include-podspecs=mParticle-Apple-SDK-Swift/mParticle-Apple-SDK-Swift.podspec

- name: Undo twitter change to podspec
run: git checkout *.podspec
Expand Down
15 changes: 12 additions & 3 deletions Example/Podfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
platform :ios, '15.0'
platform :ios, '15.6'

target 'mParticleExample' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
use_frameworks!

# Pods for mParticleExample
pod 'mParticle-Apple-SDK', :path => '..'
pod 'mParticle-Apple-Media-SDK', '~> 1.3.1'
pod 'mParticle-Apple-SDK-Swift', :path => '../mParticle-Apple-SDK-Swift'
pod 'mParticle-Apple-SDK/mParticleNoLocation', :path => '..'
pod 'mParticle-Apple-Media-SDK/mParticleMediaNoLocation', '~> 1.7.0'
pod 'mParticle-Rokt', '~> 8.0'
#pod 'PLCrashReporter', '~> 1.11.1'
#pod 'mParticle-UrbanAirship', :path => '../../mparticle-apple-integration-urbanairship'
Expand All @@ -27,3 +28,11 @@ target 'mParticleExample' do
# end

end

post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.6'
end
end
end
3 changes: 2 additions & 1 deletion Example/mParticleExample/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ - (void)logCustomMediaEvents {
title:@"Sample App Video"
duration:[NSNumber numberWithInt:120000]
contentType:MPMediaContentTypeVideo
streamType:MPMediaStreamTypeOnDemand];
streamType:MPMediaStreamTypeOnDemand
excludeAdBreaksFromContentTime: false];

[mediaSession logMediaSessionStartWithOptions:nil];
[mediaSession logPlayWithOptions:nil];
Expand Down
24 changes: 12 additions & 12 deletions IntegrationTests/Sources/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func testLogTimedEvent(mparticle: MParticle) {
mparticle.endTimedEvent(retrievedTimedEvent)
}
}

wait()
}

Expand All @@ -165,7 +165,7 @@ func testLogError(mparticle: MParticle) {
// Log an error with event info - exactly as in ViewController.m
let eventInfo = ["cause": "slippery floor"]
mparticle.logError("Oops", eventInfo: eventInfo)

wait()
}

Expand All @@ -184,7 +184,7 @@ func testLogException(mparticle: MParticle) {
// Note: topmostContext parameter is not available in Swift API,
// so we use the simpler logException method
mparticle.logException(exception)

wait()
}

Expand All @@ -207,7 +207,7 @@ func testSetUserAttributes(mparticle: MParticle) {

// Set a numeric user attribute using a custom key
currentUser.setUserAttribute("Achieved Level", value: 4)

wait()
}

Expand All @@ -226,7 +226,7 @@ func testIncrementUserAttribute(mparticle: MParticle) {

// Wait for the initial set to be uploaded
wait()

// Now increment the attribute by 1 - exactly as in ViewController.m
currentUser.incrementUserAttribute("Achieved Level", byValue: NSNumber(value: 1))

Expand All @@ -244,7 +244,7 @@ func testSetSessionAttribute(mparticle: MParticle) {
// End the session to trigger sending the session attribute
// Session attributes are sent in the session end message (dt: "se")
mparticle.endSession()

wait()
}

Expand All @@ -257,7 +257,7 @@ func testIncrementSessionAttribute(mparticle: MParticle) {

// Wait for session start to be uploaded (ensures separate request from session end)
wait()

// First set an initial numeric value for the session attribute
mparticle.setSessionAttribute("Song Count", value: 5)

Expand All @@ -267,7 +267,7 @@ func testIncrementSessionAttribute(mparticle: MParticle) {
// End the session to trigger sending the session attribute
// Session attributes are sent in the session end message (dt: "se")
mparticle.endSession()

wait()
}

Expand Down Expand Up @@ -306,7 +306,7 @@ func testToggleCCPAConsent(mparticle: MParticle) {
// Log an event to trigger upload that includes the CCPA consent state
// The consent state is included in the request body ("con" field) with event uploads
mparticle.logEvent("CCPA Consent Updated", eventType: .other, eventInfo: ["consent_status": "opted_in"])

wait()
}

Expand Down Expand Up @@ -345,7 +345,7 @@ func testToggleGDPRConsent(mparticle: MParticle) {
// Log an event to trigger upload that includes the GDPR consent state
// The consent state is included in the request body ("con" field) with event uploads
mparticle.logEvent("GDPR Consent Updated", eventType: .other, eventInfo: ["consent_status": "opted_in"])

wait()
}

Expand All @@ -371,7 +371,7 @@ func testLogIDFA(mparticle: MParticle) {

// Modify the user identity
mparticle.identity.modify(identityRequest) { _, _ in }

wait()
}

Expand All @@ -389,7 +389,7 @@ func testSetATTStatus(mparticle: MParticle) {
// Log an event to trigger upload that includes the ATT status in device info
// ATT status is sent in the "att" field within device_info ("di") section
mparticle.logEvent("ATT Status Updated", eventType: .other, eventInfo: ["att_status": "authorized"])

wait()
}

Expand Down
10 changes: 5 additions & 5 deletions IntegrationTests/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ build_framework() {
echo "🧹 Cleaning previous builds..."
rm -rf "$SDK_DIR/archives" "$TEMP_ARTIFACTS_DIR/mParticle_Apple_SDK.xcframework"

# Build for iOS Simulator only (faster for integration tests)
# Build main target (mParticle-Apple-SDK-NoLocation) which depends on Swift target
echo "📱 Building archive for iOS Simulator..."
xcodebuild archive \
-project "$SDK_DIR/mParticle-Apple-SDK.xcodeproj" \
Expand All @@ -50,16 +50,16 @@ build_framework() {
-output "$SDK_DIR/mParticle_Apple_SDK.xcframework" \
2>&1 | grep -v "note:" || true

# Move xcframework to temp artifacts directory
echo "📁 Moving xcframework to temp directory..."
# Move xcframeworks to temp artifacts directory
echo "📁 Moving xcframeworks to temp directory..."
mkdir -p "$TEMP_ARTIFACTS_DIR"
rm -rf "$TEMP_ARTIFACTS_DIR/mParticle_Apple_SDK.xcframework"
mv "$SDK_DIR/mParticle_Apple_SDK.xcframework" "$TEMP_ARTIFACTS_DIR/"

# Clean up archives
rm -rf "$SDK_DIR/archives"

echo "✅ SDK built successfully at: $TEMP_ARTIFACTS_DIR/mParticle_Apple_SDK.xcframework"
echo "✅ SDK built successfully at: ${TEMP_ARTIFACTS_DIR}/mParticle_Apple_SDK.xcframework"
}

build_application() {
Expand Down Expand Up @@ -211,7 +211,7 @@ wait_for_wiremock() {
local MAX_RETRIES=30
local RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if curl -k -s -o /dev/null -w "%{http_code}" https://localhost:${HTTPS_PORT}/__admin/mappings | grep -q "200"; then
if curl -k -s -o /dev/null -w "%{http_code}" "https://localhost:${HTTPS_PORT}/__admin/mappings" | grep -q "200"; then
echo "✅ WireMock is ready!"
break
fi
Expand Down
2 changes: 1 addition & 1 deletion UnitTests/MParticle+PrivateMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- (NSTimeInterval)uploadInterval;
- (NSDictionary<NSString *, id> *)userAttributesForUserId:(NSNumber *)userId;

- (MPLog*)getLogger;
- (id)getLogger;

@property (nonatomic, strong, nonnull) id<MPBackendControllerProtocol> backendController;
@property (nonatomic, strong) id<MPAppNotificationHandlerProtocol> appNotificationHandler;
Expand Down
11 changes: 6 additions & 5 deletions UnitTests/SwiftTests/MPEventTests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import XCTest
import mParticle_Apple_SDK_NoLocation
internal import mParticle_Apple_SDK_Swift

class MPEventTests: XCTestCase {
var sut: MPEvent!
Expand Down Expand Up @@ -62,7 +63,7 @@ class MPEventTests: XCTestCase {
}

func testInit_withEmptyName_returnsNil() {
let logger = MParticle.sharedInstance().getLogger()!
let logger = MParticle.sharedInstance().getLogger() as! MPLog
logger.logLevel = .verbose
logger.customLogger = { message in
self.receivedMessage = message
Expand All @@ -73,7 +74,7 @@ class MPEventTests: XCTestCase {
}

func testInitWithTooLongName_returnsNil() {
let logger = MParticle.sharedInstance().getLogger()!
let logger = MParticle.sharedInstance().getLogger() as! MPLog
logger.logLevel = .verbose
logger.customLogger = { message in
self.receivedMessage = message
Expand Down Expand Up @@ -159,7 +160,7 @@ class MPEventTests: XCTestCase {
}

func testSetCategory_withTooLongCategory_discardsAndLogs() {
let logger = MParticle.sharedInstance().getLogger()!
let logger = MParticle.sharedInstance().getLogger() as! MPLog
logger.logLevel = .verbose
logger.customLogger = { message in
self.receivedMessage = message
Expand Down Expand Up @@ -232,7 +233,7 @@ class MPEventTests: XCTestCase {
}

func testSetName_withEmptyName_discardsAndLogs() {
let logger = MParticle.sharedInstance().getLogger()!
let logger = MParticle.sharedInstance().getLogger() as! MPLog
logger.logLevel = .verbose
logger.customLogger = { message in
self.receivedMessage = message
Expand All @@ -243,7 +244,7 @@ class MPEventTests: XCTestCase {
}

func testSetName_withTooLongName_discardsAndLogs() {
let logger = MParticle.sharedInstance().getLogger()!
let logger = MParticle.sharedInstance().getLogger() as! MPLog
logger.logLevel = .verbose
logger.customLogger = { message in
self.receivedMessage = message
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import XCTest
@testable import mParticle_Apple_SDK_NoLocation
internal import mParticle_Apple_SDK_Swift

final class MParticleSceneDelegateTests: XCTestCase {

Expand All @@ -20,7 +21,7 @@ final class MParticleSceneDelegateTests: XCTestCase {

// The implementation calls [MParticle sharedInstance], so we need to set the mock on the shared instance
sceneMock = OpenURLHandlerProtocolMock()
let sceneHandler = SceneDelegateHandler(logger: MPLog(logLevel: .verbose), appNotificationHandler: sceneMock)
let sceneHandler = SceneDelegateHandler(appNotificationHandler: sceneMock)
mparticle.sceneDelegateHandler = sceneHandler
}

Expand Down
57 changes: 57 additions & 0 deletions mParticle-Apple-SDK-Swift/Sources/Logger/MPLogger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation

/// Log Levels
@objc
public enum MPILogLevelSwift: UInt {
/** No log messages are displayed on the console */
case none = 0
/** Only error log messages are displayed on the console */
case error
/** Warning and error log messages are displayed on the console */
case warning
/** Debug, warning, and error log messages are displayed on the console */
case debug
/** Verbose, debug, warning, and error log messages are displayed on the console */
case verbose
}

@objcMembers
public class MPLog: NSObject {
Comment thread
denischilik marked this conversation as resolved.
public var logLevel: MPILogLevelSwift
public var customLogger: ((String) -> Void)?

public init(logLevel: MPILogLevelSwift) {
self.logLevel = logLevel
}

public static func from(rawValue: UInt) -> MPILogLevelSwift {
return MPILogLevelSwift(rawValue: rawValue) ?? .none
}

private func log(loggerLevel: MPILogLevelSwift, format: String, arguments: any CVarArg...) {
if logLevel.rawValue >= loggerLevel.rawValue && loggerLevel != .none {
let msg = String.localizedStringWithFormat("mParticle -> \(format)", arguments)
if let customLogger = customLogger {
customLogger(msg)
} else {
NSLog(msg)
}
}
}

public func error(_ message: String) {
log(loggerLevel: .error, format: message)
}

public func warning(_ message: String) {
log(loggerLevel: .warning, format: message)
}

public func debug(_ message: String) {
log(loggerLevel: .debug, format: message)
}

public func verbose(_ message: String) {
log(loggerLevel: .verbose, format: message)
}
}
23 changes: 23 additions & 0 deletions mParticle-Apple-SDK-Swift/mParticle-Apple-SDK-Swift.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Pod::Spec.new do |s|
s.name = "mParticle-Apple-SDK-Swift"
s.version = "8.41.1"
s.summary = "mParticle Apple SDK Swift components."

s.description = <<-DESC
Swift components for the mParticle Apple SDK.
This pod contains Swift-only code that is used by the main mParticle-Apple-SDK.
DESC

s.homepage = "https://www.mparticle.com"
s.license = { :type => 'Apache 2.0', :file => '../LICENSE'}
s.author = { "mParticle" => "support@mparticle.com" }
s.source = { :git => "https://github.com/mParticle/mparticle-apple-sdk.git", :tag => "v" + s.version.to_s }
s.documentation_url = "https://docs.mparticle.com/developers/sdk/ios/"
s.requires_arc = true
s.module_name = 'mParticle_Apple_SDK_Swift'
s.ios.deployment_target = "15.6"
s.tvos.deployment_target = "15.6"
s.swift_versions = ["5.0"]

s.source_files = 'Sources/**/*.swift'
end
5 changes: 4 additions & 1 deletion mParticle-Apple-SDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Pod::Spec.new do |s|
s.documentation_url = "https://docs.mparticle.com/developers/sdk/ios/"
s.social_media_url = "https://twitter.com/mparticle"
s.requires_arc = true
s.default_subspec = 'mParticle'
s.default_subspec = 'mParticleNoLocation'
s.module_name = 'mParticle_Apple_SDK'
s.ios.deployment_target = "15.6"
s.tvos.deployment_target = "15.6"
Expand All @@ -33,12 +33,14 @@ Pod::Spec.new do |s|
ss.preserve_paths = 'mParticle-Apple-SDK', 'mParticle-Apple-SDK/**', 'mParticle-Apple-SDK/**/*'
ss.source_files = 'mParticle-Apple-SDK/**/*.{h,m,mm,cpp,swift}'
ss.resource_bundles = {'mParticle-Privacy' => ['PrivacyInfo.xcprivacy']}
ss.dependency 'mParticle-Apple-SDK-Swift', "~> #{s.version}"
end

s.subspec 'AppExtension' do |ext|
ext.public_header_files = 'mParticle-Apple-SDK/Include/*.h'
ext.preserve_paths = 'mParticle-Apple-SDK', 'mParticle-Apple-SDK/**', 'mParticle-Apple-SDK/**/*'
ext.source_files = 'mParticle-Apple-SDK/**/*.{h,m,mm,cpp,swift}'
ext.dependency 'mParticle-Apple-SDK-Swift', "~> #{s.version}"
end

s.subspec 'AppExtensionNoLocation' do |ext|
Expand All @@ -49,6 +51,7 @@ Pod::Spec.new do |s|
'GCC_PREPROCESSOR_DEFINITIONS' => 'MPARTICLE_LOCATION_DISABLE=1',
'OTHER_SWIFT_FLAGS' => '-D MPARTICLE_LOCATION_DISABLE'
}
ext.dependency 'mParticle-Apple-SDK-Swift', "~> #{s.version}"
end
end

Loading
Loading