Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions Kits/appsflyer/appsflyer-6/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ After installing, rebuild and launch your app. With the mParticle log level set
Included kits: { AppsFlyer }
```

## Manual Start

If this kit's "Require Manual Start" connection setting is enabled, AppsFlyer does not start automatically — attribution and event forwarding stay paused until your app tells it to go (for example, once a user accepts a consent prompt). Resume it by calling `startAppsFlyer`, which always operates on the same AppsFlyer instance this kit configured:

```swift
import mParticle_AppsFlyer

MPKitAppsFlyer.startAppsFlyer()
```

```objc
#import <mParticle_AppsFlyer/mParticle_AppsFlyer.h>

[MPKitAppsFlyer startAppsFlyer];
```

Don't call `AppsFlyerLib.shared().start()` directly: if your app links the AppsFlyer SDK anywhere else, that call can resolve a different, unconfigured instance and fail with "No dev key".

## Platform Support

| Platform | Minimum Version |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ + (void)setDelegate:(id)delegate {
}
}

+ (BOOL)startAppsFlyer {
if (appsFlyerTracker == nil || appsFlyerTracker.appsFlyerDevKey.length == 0) {
NSLog(@"Warning: `startAppsFlyer` was called before AppsFlyer was initialized by mParticle. "
"Ensure the kit is configured and active before invoking manual start.");
return NO;
}

[appsFlyerTracker start];
return YES;
}

+ (NSNumber *)kitCode {
return @92;
}
Expand Down Expand Up @@ -158,7 +169,7 @@ - (void)start {
- (nonnull MPKitExecStatus *)didBecomeActive {
BOOL manualStart = [self configurationBoolForKey:afManualStart];
if (!manualStart) {
[appsFlyerTracker start];
[[self class] startAppsFlyer];
}
MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppsFlyer) returnCode:MPKitReturnCodeSuccess];
return execStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ extern NSString * _Nonnull const MPKitAppsFlyerErrorDomain;
@property (nonatomic, strong, nullable) id providerKitInstance;

+ (void)setDelegate:(id _Nonnull)delegate;

/**
Starts AppsFlyer on the instance this kit already configured, for apps using the
`manualStart` connection setting to delay attribution (for example, until a consent
banner is accepted). Prefer this over calling `[[AppsFlyerLib shared] start]` directly:
an app that also links the AppsFlyer SDK on its own can otherwise resolve a second,
unconfigured instance and fail with "No dev key".

@return `YES` if AppsFlyer was told to start; `NO` if mParticle has not yet configured
the AppsFlyer kit (for example, if called before `start` on the mParticle SDK itself).
*/
+ (BOOL)startAppsFlyer NS_SWIFT_NAME(startAppsFlyer());

+ (NSNumber * _Nonnull)computeProductQuantity:(nullable MPCommerceEvent *)event;
+ (NSString * _Nullable)generateProductIdList:(nullable MPCommerceEvent *)event;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class MPKitAppsFlyerTests: XCTestCase {
kit = MPKitAppsFlyer()
kit.configuration = [:]
mock = AppsFlyerLibMock()
mock.appsFlyerDevKey = "test-dev-key"
}

// MARK: - convertToKeyValuePairs
Expand Down Expand Up @@ -185,6 +186,38 @@ final class MPKitAppsFlyerTests: XCTestCase {
XCTAssertEqual(mock.startCallCount, 1)
}

// MARK: - startAppsFlyer

func test_startAppsFlyer_withConfiguredTracker_callsStartAndReturnsTrue() {
kit.providerKitInstance = mock

let result = MPKitAppsFlyer.startAppsFlyer()

XCTAssertTrue(result)
XCTAssertEqual(mock.startCallCount, 1)
}

func test_startAppsFlyer_withoutConfiguredTracker_returnsFalse() {
kit.providerKitInstance = nil

let result = MPKitAppsFlyer.startAppsFlyer()

XCTAssertFalse(result)
}

// Covers the window between `appsFlyerTracker = [AppsFlyerLib shared]` and the dev key
// actually being set in didFinishLaunchingWithConfiguration: the tracker exists but isn't
// configured yet, so starting it would behave as if mParticle never configured AppsFlyer.
func test_startAppsFlyer_withTrackerAssignedButNotYetConfigured_returnsFalse() {
let unconfiguredMock = AppsFlyerLibMock()
kit.providerKitInstance = unconfiguredMock

let result = MPKitAppsFlyer.startAppsFlyer()

XCTAssertFalse(result)
XCTAssertEqual(unconfiguredMock.startCallCount, 0)
}

// MARK: - setUserIdentity / userIdentificationType

func test_setUserIdentity_customerId_legacy_forwardsToAppsFlyer() {
Expand Down
Loading