From 721cd568861d8ccc04aa2de46e39cb76d1dd06d6 Mon Sep 17 00:00:00 2001 From: Brandon Stalnaker <33703490+BrandonStalnaker@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:37:32 -0500 Subject: [PATCH] feat:Migrate to Braze Swift SDK (#68) --- Cartfile | 2 - Cartfile.private | 1 - Package.swift | 6 +- Sources/mParticle-Appboy/MPKitAppboy.m | 173 +++++++++++-------------- mParticle-Appboy.podspec | 9 +- 5 files changed, 86 insertions(+), 105 deletions(-) delete mode 100644 Cartfile delete mode 100644 Cartfile.private diff --git a/Cartfile b/Cartfile deleted file mode 100644 index 8beb6e7..0000000 --- a/Cartfile +++ /dev/null @@ -1,2 +0,0 @@ -github "Appboy/appboy-ios-sdk" ~> 4.4.0 -github "mparticle/mparticle-apple-sdk" ~> 8.0 diff --git a/Cartfile.private b/Cartfile.private deleted file mode 100644 index 78a9326..0000000 --- a/Cartfile.private +++ /dev/null @@ -1 +0,0 @@ -github "erikdoe/ocmock" ~> 3.3.1 diff --git a/Package.swift b/Package.swift index 24d013b..838ecee 100644 --- a/Package.swift +++ b/Package.swift @@ -15,9 +15,9 @@ let package = Package( .package(name: "mParticle-Apple-SDK", url: "https://github.com/mParticle/mparticle-apple-sdk", .upToNextMajor(from: "8.0.0")), - .package(name: "Appboy_iOS_SDK", - url: "https://github.com/braze-inc/braze-ios-sdk", - .upToNextMajor(from: "4.4.0")), + .package(name: "braze-swift-sdk", + url: "https://github.com/braze-inc/braze-swift-sdk", + .upToNextMajor(from: "5.9.0")), ], targets: [ .target( diff --git a/Sources/mParticle-Appboy/MPKitAppboy.m b/Sources/mParticle-Appboy/MPKitAppboy.m index 4fad1a3..fd2f345 100644 --- a/Sources/mParticle-Appboy/MPKitAppboy.m +++ b/Sources/mParticle-Appboy/MPKitAppboy.m @@ -1,30 +1,22 @@ #import "MPKitAppboy.h" #if SWIFT_PACKAGE - #import "AppboyKit.h" + #if TARGET_OS_IOS == 1 + import BrazeKit + import BrazeKitCompat + import BrazeUI + #elif TARGET_OS_TV == 1 + import BrazeKit + import BrazeKitCompat + #endif #else - #ifdef COCOAPODS - #if TARGET_OS_IOS == 1 - #if defined(__has_include) && __has_include() - #import - #elif defined(__has_include) && __has_include() - #import - #else - #import "AppboyKit.h" - #endif - #elif TARGET_OS_TV == 1 - #if defined(__has_include) && __has_include() - #import - #else - #import "AppboyKit.h" - #endif - #endif - #else - #if TARGET_OS_IOS == 1 - #import - #elif TARGET_OS_TV == 1 - #import "AppboyKit.h" - #endif + #if TARGET_OS_IOS == 1 + @import BrazeKit; + @import BrazeKitCompat; + @import BrazeUI; + #elif TARGET_OS_TV == 1 + @import BrazeKit; + @import BrazeKitCompat; #endif #endif @@ -59,11 +51,11 @@ // User Attribute key with reserved functionality for Braze kit static NSString *const brazeUserAttributeDob = @"dob"; -__weak static id inAppMessageControllerDelegate = nil; -__weak static id urlDelegate = nil; +__weak static id inAppMessageControllerDelegate = nil; +__weak static id urlDelegate = nil; @interface MPKitAppboy() { - Appboy *appboyInstance; + Braze *appboyInstance; BOOL collectIDFA; BOOL forwardScreenViews; } @@ -86,18 +78,18 @@ + (void)load { } + (void)setInAppMessageControllerDelegate:(id)delegate { - inAppMessageControllerDelegate = (id)delegate; + inAppMessageControllerDelegate = (id)delegate; } -+ (id)inAppMessageControllerDelegate { ++ (id)inAppMessageControllerDelegate { return inAppMessageControllerDelegate; } + (void)setURLDelegate:(id)delegate { - urlDelegate = (id)delegate; + urlDelegate = (id)delegate; } -+ (id)urlDelegate { ++ (id)urlDelegate { return urlDelegate; } @@ -120,11 +112,11 @@ - (NSString *)stringRepresentation:(id)value { return stringRepresentation; } -- (Appboy *)appboyInstance { +- (Braze *)appboyInstance { return self->appboyInstance; } -- (void)setAppboyInstance:(Appboy *)instance { +- (void)setAppboyInstance:(Braze *)instance { self->appboyInstance = instance; } @@ -157,7 +149,7 @@ - (MPKitExecStatus *)logAppboyCustomEvent:(MPEvent *)event eventType:(NSUInteger // Appboy expects that the properties are non empty when present. if (detectedEventInfo && detectedEventInfo.count > 0) { - [self->appboyInstance logCustomEvent:event.name withProperties:detectedEventInfo]; + [self->appboyInstance logCustomEvent:event.name properties:detectedEventInfo]; } else { [self->appboyInstance logCustomEvent:event.name]; } @@ -200,7 +192,6 @@ - (MPKitExecStatus *)logAppboyCustomEvent:(MPEvent *)event eventType:(NSUInteger return execStatus; } -#pragma mark ABKIDFADelegate - (BOOL)isAdvertisingTrackingEnabled { BOOL advertisingTrackingEnabled = NO; Class MPIdentifierManager = NSClassFromString(@"ASIdentifierManager"); @@ -268,7 +259,7 @@ - (MPKitExecStatus *)didFinishLaunchingWithConfiguration:(NSDictionary *)configu // 1. Apps that initialize Braze prior to mParticle, and/or // 2. Apps that initialize mParticle too late, causing the SDK to miss // the launch notification which would otherwise trigger start(). - if ([Appboy sharedInstance]) { + if (self->appboyInstance) { NSLog(@"mParticle -> Warning: Braze SDK initialized outside of mParticle kit, this will mean Braze settings within the mParticle dashboard such as API key, endpoint URL, flush interval and others will not be respected."); [self start]; } else { @@ -284,32 +275,33 @@ - (id const)providerKitInstance { } - (void)start { - static dispatch_once_t appboyPredicate; - - dispatch_once(&appboyPredicate, ^{ + if (!self->appboyInstance) { NSDictionary *optionsDict = [self optionsDictionary]; + BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:self.configuration[eabAPIKey] endpoint:optionsDict[ABKEndpointKey]]; + configuration.api.requestPolicy = ((NSNumber *)optionsDict[ABKRequestProcessingPolicyOptionKey]).intValue; + configuration.api.flushInterval = ((NSNumber *)optionsDict[ABKFlushIntervalOptionKey]).doubleValue; + configuration.sessionTimeout = ((NSNumber *)optionsDict[ABKSessionTimeoutKey]).doubleValue; + configuration.triggerMinimumTimeInterval = ((NSNumber *)optionsDict[ABKMinimumTriggerTimeIntervalKey]).doubleValue; + configuration.location.automaticLocationCollection = optionsDict[ABKEnableAutomaticLocationCollectionKey]; + [configuration.api addSDKMetadata:@[BRZSDKMetadata.mparticle]]; + configuration.api.sdkFlavor = ((NSNumber *)optionsDict[ABKSDKFlavorKey]).intValue; - [Appboy startWithApiKey:self.configuration[eabAPIKey] - inApplication:[UIApplication sharedApplication] - withLaunchOptions:self.launchOptions - withAppboyOptions:optionsDict]; - }); + self->appboyInstance = [[Braze alloc] initWithConfiguration:configuration]; + } - if (![Appboy sharedInstance] ) { + if (!self->appboyInstance) { return; } - CFTypeRef appboyRef = CFRetain((__bridge CFTypeRef)[Appboy sharedInstance]); - self->appboyInstance = (__bridge Appboy *)appboyRef; - - [self->appboyInstance addSdkMetadata:@[ABKSdkMetadataMParticle]]; if (self->collectIDFA) { - self->appboyInstance.idfaDelegate = (id)self; + [self->appboyInstance setIdentifierForAdvertiser:[self advertisingIdentifierString]]; + [self->appboyInstance setAdTrackingEnabled:[self isAdvertisingTrackingEnabled]]; } #if TARGET_OS_IOS == 1 if ([MPKitAppboy inAppMessageControllerDelegate]) { - self->appboyInstance.inAppMessageController.delegate = [MPKitAppboy inAppMessageControllerDelegate]; + BrazeInAppMessageUI *inAppMessageUI = [[BrazeInAppMessageUI alloc] init]; + inAppMessageUI.delegate = [MPKitAppboy inAppMessageControllerDelegate]; } #endif @@ -404,19 +396,6 @@ - (void)start { return optionsDictionary; } -- (MPKitExecStatus *)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo { -#if TARGET_OS_IOS == 1 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [appboyInstance getActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:^{}]; -#pragma clang diagnostic pop - -#endif - - MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; - return execStatus; -} - - (MPKitExecStatus *)incrementUserAttribute:(NSString *)key byValue:(NSNumber *)value { [appboyInstance.user incrementCustomUserAttribute:key by:[value integerValue]]; @@ -481,10 +460,10 @@ - (MPKitExecStatus *)routeCommerceEvent:(MPCommerceEvent *)commerceEvent { [properties removeObjectsForKeys:keys]; [appboyInstance logPurchase:product.sku - inCurrency:currency - atPrice:[NSDecimalNumber decimalNumberWithDecimal:[product.price decimalValue]] - withQuantity:[product.quantity integerValue] - andProperties:properties]; + currency:currency + price:[product.price doubleValue] + quantity:[product.quantity integerValue] + properties:properties]; [execStatus incrementForwardCount]; } @@ -517,11 +496,14 @@ - (MPKitExecStatus *)logScreen:(MPEvent *)event { } - (MPKitExecStatus *)receivedUserNotification:(NSDictionary *)userInfo { + MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; + #if TARGET_OS_IOS == 1 - [appboyInstance registerApplication:[UIApplication sharedApplication] didReceiveRemoteNotification:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult fetchResult) {}]; + if (![appboyInstance.notifications handleBackgroundNotificationWithUserInfo:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult fetchResult) {}]) { + execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeFail]; + } #endif - MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; return execStatus; } @@ -534,7 +516,7 @@ - (MPKitExecStatus *)removeUserAttribute:(NSString *)key { - (MPKitExecStatus *)setDeviceToken:(NSData *)deviceToken { #if TARGET_OS_IOS == 1 - [appboyInstance registerDeviceToken:deviceToken]; + [appboyInstance.notifications registerDeviceToken:deviceToken]; #endif MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; @@ -545,7 +527,7 @@ - (MPKitExecStatus *)setOptOut:(BOOL)optOut { MPKitReturnCode returnCode; if (optOut) { - [appboyInstance.user setEmailNotificationSubscriptionType:ABKUnsubscribed]; + [appboyInstance.user setEmailSubscriptionState:BRZUserSubscriptionStateSubscribed]; returnCode = MPKitReturnCodeSuccess; } else { returnCode = MPKitReturnCodeCannotExecute; @@ -571,9 +553,9 @@ - (MPKitExecStatus *)setUserAttribute:(NSString *)key value:(NSString *)value { } if ([key isEqualToString:mParticleUserAttributeFirstName]) { - appboyInstance.user.firstName = value; + [appboyInstance.user setFirstName:value]; } else if ([key isEqualToString:mParticleUserAttributeLastName]) { - appboyInstance.user.lastName = value; + [appboyInstance.user setLastName:value]; } else if ([key isEqualToString:mParticleUserAttributeAge]) { NSDate *now = [NSDate date]; NSCalendar *calendar = [NSCalendar currentCalendar]; @@ -593,7 +575,7 @@ - (MPKitExecStatus *)setUserAttribute:(NSString *)key value:(NSString *)value { birthComponents.month = 01; birthComponents.day = 01; - appboyInstance.user.dateOfBirth = [calendar dateFromComponents:birthComponents]; + [appboyInstance.user setDateOfBirth:[calendar dateFromComponents:birthComponents]]; } else if ([key isEqualToString:brazeUserAttributeDob]) { // Expected Date Format @"yyyy'-'MM'-'dd" NSCalendar *calendar = [NSCalendar currentCalendar]; @@ -637,49 +619,45 @@ - (MPKitExecStatus *)setUserAttribute:(NSString *)key value:(NSString *)value { birthComponents.month = month; birthComponents.day = day; - appboyInstance.user.dateOfBirth = [calendar dateFromComponents:birthComponents]; + [appboyInstance.user setDateOfBirth:[calendar dateFromComponents:birthComponents]]; } else if ([key isEqualToString:mParticleUserAttributeCountry]) { - appboyInstance.user.country = value; + [appboyInstance.user setCountry:value]; } else if ([key isEqualToString:mParticleUserAttributeCity]) { - appboyInstance.user.homeCity = value; + [appboyInstance.user setHomeCity:value]; } else if ([key isEqualToString:mParticleUserAttributeGender]) { -#if TARGET_OS_IOS == 1 - appboyInstance.user.gender = ABKUserGenderOther; + [appboyInstance.user setGender:BRZUserGender.other]; if ([value isEqualToString:mParticleGenderMale]) { - appboyInstance.user.gender = ABKUserGenderMale; + [appboyInstance.user setGender:BRZUserGender.male]; } else if ([value isEqualToString:mParticleGenderFemale]) { - appboyInstance.user.gender = ABKUserGenderFemale; + [appboyInstance.user setGender:BRZUserGender.female]; } else if ([value isEqualToString:mParticleGenderNotAvailable]) { - appboyInstance.user.gender = ABKUserGenderNotApplicable; + [appboyInstance.user setGender:BRZUserGender.notApplicable]; } -#elif TARGET_OS_TV == 1 - appboyInstance.user.gender = [value isEqualToString:mParticleGenderMale] ? ABKUserGenderMale : ABKUserGenderFemale; -#endif } else if ([key isEqualToString:mParticleUserAttributeMobileNumber] || [key isEqualToString:@"$MPUserMobile"]) { - appboyInstance.user.phone = value; + [appboyInstance.user setPhoneNumber:value]; } else if ([key isEqualToString:mParticleUserAttributeZip]){ - [appboyInstance.user setCustomAttributeWithKey:@"Zip" andStringValue:value]; + [appboyInstance.user setCustomAttributeWithKey:@"Zip" stringValue:value]; } else { key = [self stripCharacter:@"$" fromString:key]; if (!_enableTypeDetection) { - [appboyInstance.user setCustomAttributeWithKey:key andStringValue:value]; + [appboyInstance.user setCustomAttributeWithKey:key stringValue:value]; } else { NSDictionary *tempConversionDictionary = @{key: value}; tempConversionDictionary = [self simplifiedDictionary:tempConversionDictionary]; id obj = tempConversionDictionary[key]; if ([obj isKindOfClass:[NSString class]]) { - [appboyInstance.user setCustomAttributeWithKey:key andStringValue:obj]; + [appboyInstance.user setCustomAttributeWithKey:key stringValue:obj]; } else if ([obj isKindOfClass:[NSNumber class]]) { if ([self isBoolNumber:obj]) { - [appboyInstance.user setCustomAttributeWithKey:key andBOOLValue:((NSNumber *)obj).boolValue]; + [appboyInstance.user setCustomAttributeWithKey:key boolValue:((NSNumber *)obj).boolValue]; } else if ([self isInteger:value]) { - [appboyInstance.user setCustomAttributeWithKey:key andIntegerValue:((NSNumber *)obj).intValue]; + [appboyInstance.user setCustomAttributeWithKey:key intValue:((NSNumber *)obj).intValue]; } else if ([self isFloat:value]) { - [appboyInstance.user setCustomAttributeWithKey:key andDoubleValue:((NSNumber *)obj).doubleValue]; + [appboyInstance.user setCustomAttributeWithKey:key doubleValue:((NSNumber *)obj).doubleValue]; } } else if ([obj isKindOfClass:[NSDate class]]) { - [appboyInstance.user setCustomAttributeWithKey:key andDateValue:obj]; + [appboyInstance.user setCustomAttributeWithKey:key dateValue:obj]; } } } @@ -878,7 +856,7 @@ - (nonnull MPKitExecStatus *)updateUser:(FilteredMParticleUser *)user request:(F } if (userEmail) { - appboyInstance.user.email = userEmail; + [appboyInstance.user setEmail:userEmail]; execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; } } @@ -892,9 +870,12 @@ - (MPKitExecStatus *)setUserIdentity:(NSString *)identityString identityType:(MP #if TARGET_OS_IOS == 1 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - (nonnull MPKitExecStatus *)userNotificationCenter:(nonnull UNUserNotificationCenter *)center didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response API_AVAILABLE(ios(10.0)) { - [appboyInstance userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:^{}]; - MPKitExecStatus *execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeSuccess]; + + if (![appboyInstance.notifications handleUserNotificationWithResponse:response withCompletionHandler:^{}]) { + execStatus = [[MPKitExecStatus alloc] initWithSDKCode:@(MPKitInstanceAppboy) returnCode:MPKitReturnCodeFail]; + } + return execStatus; } #endif diff --git a/mParticle-Appboy.podspec b/mParticle-Appboy.podspec index 36b96d9..a526a86 100755 --- a/mParticle-Appboy.podspec +++ b/mParticle-Appboy.podspec @@ -18,13 +18,16 @@ Pod::Spec.new do |s| s.ios.dependency 'mParticle-Apple-SDK/mParticle', '~> 8.0' s.ios.frameworks = 'CoreTelephony', 'SystemConfiguration' s.libraries = 'z' - s.ios.dependency 'Appboy-iOS-SDK', '~> 4.4' + s.ios.dependency 'BrazeKit', '~> 5.9' + s.ios.dependency 'BrazeKitCompat', '~> 5.9' + s.ios.dependency 'BrazeUI', '~> 5.9' s.tvos.deployment_target = "9.0" - s.ios.source_files = 'Sources/**/*.{h,m,mm}' + s.tvos.source_files = 'Sources/**/*.{h,m,mm}' s.tvos.dependency 'mParticle-Apple-SDK/mParticle', '~> 8.0' s.tvos.frameworks = 'SystemConfiguration' - s.tvos.dependency 'Appboy-tvOS-SDK', '~> 4.3' + s.tvos.dependency 'BrazeKit', '~> 5.9' + s.tvos.dependency 'BrazeKitCompat', '~> 5.9' s.tvos.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=appletvsimulator*]' => 'arm64'