braze-inc / braze-react-native-sdk

Public repo for the Braze React Native SDK
https://www.braze.com
Other
64 stars 82 forks source link

[Feature]: React Native iOS sdk setup missing swift version of configuration #191

Closed bernardorubin closed 1 year ago

bernardorubin commented 1 year ago

What problem are you facing?

I'm trying to setup the react-native sdk following the documentation here: https://www.braze.com/docs/developer_guide/platform_integration_guides/react_native/react_sdk_setup/#step-2-complete-native-setup

But the AppDelegate.m changes only come in objective c and not swift. I see you have swift in other docs but it's missing here. Tried adapting the code to swift but it doesn't work.

Workarounds

Converting the code to Swift which I tried but doesn't really work:

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
    // Setup Braze bridge
    let moduleInitializer = BrazeReactBridge() as? RCTBridgeDelegate
    let bridge = RCTBridge(
        delegate: moduleInitializer,
        launchOptions: launchOptions)
    let rootView = RCTRootView(
        bridge: bridge,
        moduleName: "<YOUR_PROJECT_NAME>",
        initialProperties: nil)
    self.bridge = rootView.bridge

    // Configure views in the application
    window = UIWindow(frame: UIScreen.main.bounds)
    let rootViewController = UIViewController()
    rootViewController.view = rootView
    window.rootViewController = rootViewController
    window.makeKeyAndVisible()

    // Setup Braze
    let configuration = BRZConfiguration(
        apiKey: "<BRAZE_API_KEY>",
        endpoint: "<BRAZE_ENDPOINT>")
    // - Enable logging and customize the configuration here
    let braze = BrazeReactBridge.initBraze(configuration)
    AppDelegate.braze() = braze

    return true
}

// MARK: - AppDelegate.braze
private var _braze: Braze? = nil

class func braze() -> Braze? {
    return braze
}

class func setBraze(_ braze: Braze?) {
    self.braze = braze
}

Ideal Solution

It would be nice to have the documentation updated for swift as well.

Other Information

No response

lowip commented 1 year ago

Hi @bernardorubin,

Thanks for reporting this issue, we'll update our documentation with the following Swift code snippet:

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
    // Setup Braze bridge
    let moduleInitializer = BrazeReactBridge() as? RCTBridgeDelegate
    let bridge = RCTBridge(
        delegate: moduleInitializer,
        launchOptions: launchOptions)
    let rootView = RCTRootView(
        bridge: bridge,
        moduleName: "<YOUR_PROJECT_NAME>",
        initialProperties: nil)
    self.bridge = rootView.bridge

    // Configure views in the application
    window = UIWindow(frame: UIScreen.main.bounds)
    let rootViewController = UIViewController()
    rootViewController.view = rootView
    window.rootViewController = rootViewController
    window.makeKeyAndVisible()

    // Setup Braze
    let configuration = Braze.Configuration(
        apiKey: "<BRAZE_API_KEY>",
        endpoint: "<BRAZE_ENDPOINT>")
    // - Enable logging and customize the configuration here
    let braze = BrazeReactBridge.initBraze(configuration)
    AppDelegate.braze = braze

    return true
}

// MARK: - AppDelegate.braze
static var braze: Braze? = nil
bernardorubin commented 1 year ago

Thanks @hokstuff 🙏

bernardorubin commented 1 year ago

@lowip https://github.com/braze-inc/braze-docs/pull/4869#issuecomment-1437936852

hokstuff commented 1 year ago

Hi @bernardorubin,

Thanks!

hokstuff commented 1 year ago

For the first bullet, the line you'll need to add is import braze_react_native_sdk. Let me know if you still have issues - thanks!

bernardorubin commented 1 year ago

@hokstuff getting no such module error with import braze_react_native_sdk already reinstalled using yarn add @braze/react-native-sdk and followed docs, but still no luck 😕 . Deleted and reinstalled pods as well.

Screenshot 2023-02-21 at 17 43 55
hokstuff commented 1 year ago

Which version of the Braze React Native SDK are you using? There were some changes that were released recently which may be relevant. Also, does your IDE's IntelliSense display any suggestions around importing the Braze SDK?

bernardorubin commented 1 year ago

I'm at "@braze/react-native-sdk": "^2.0.2", The intellisense shows this @hokstuff

Screenshot 2023-02-21 at 17 51 53

it now also shows braze_react_native_sdk but it's only cause I wrote it before but as soon as I type it I get the No such module "braze_react_native_sdk" error

Screenshot 2023-02-21 at 18 06 40
hokstuff commented 1 year ago

It looks like it isn't able to find the ObjC Braze bridge files in your Swift code. Can you create a bridging header which allows you to import ObjC headers into your Swift project? Then, in the bridging header, you can import BrazeReactBridge.h.

^ If this works then I will make a request to update the docs for Swift projects

bernardorubin commented 1 year ago

That doesn't work either.

I added #import "BrazeReactBridge.h" to our ...Bridging-Header.h file and imported the BrazeReactBridge.h file, still no luck so far, will continue trying.

BrazeReactBridge.h

#import <Foundation/Foundation.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTBridgeModule.h>

#import <BrazeKit/BrazeKit-Swift.h>

@interface BrazeReactBridge : RCTEventEmitter <RCTBridgeModule, RCTBridgeDelegate>

/// Intializes the Braze instance based on a configuration. This same instance is also used by the Braze bridge.
/// - Parameters:
///   - configuration: The customizable configuration from the host app.
+ (Braze *)initBraze:(BRZConfiguration *)configuration;

@end

@hokstuff

bernardorubin commented 1 year ago

I managed to get the bridge working, as BrazeReactBridge() is being called without having to import anything at the top of the AppDelegate.m file, so I assume it's working as expected.

These are the new errors causing the swift compilation and the build to fail:

Value of optional type 'RCTBridge?' must be unwrapped to a value of type 'RCTBridge'
Cannot convert value of type 'Braze.Configuration' to expected argument type 'Configuration?'
Screenshot 2023-02-21 at 23 56 45

@hokstuff

hokstuff commented 1 year ago

Hi @bernardorubin,

Can you try a couple more changes to attempt to resolve the issue:

  1. The BrazeReactBridge conforms to the protocol RCTBridgeDelegate so can you try removing as? RCTBridgeDelegate?
  2. For the issue on the bottom, in our Expo plugin we implemented this workaround to address the typing discrepancy. Do you mind trying it in your project?

Thanks for your patience and help in working out this issue. We have added an item to our internal roadmap to create a Swift sample project that we can use to test integrations with our React Native SDK.

bernardorubin commented 1 year ago

The following worked for us with no errors and no warnings - added bridge: bridge!, and this workaround let braze = BrazeReactBridge.perform(#selector(BrazeReactBridge.initBraze(_:)), with: configuration).takeUnretainedValue() as! Braze

Here's the full snippet:

// MARK: - AppDelegate.braze
  static var braze: Braze? = nil

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Setup Braze bridge
    let moduleInitializer = BrazeReactBridge()
    let bridge = RCTBridge(
        delegate: moduleInitializer,
        launchOptions: launchOptions)
    let rootView = RCTRootView(
        bridge: bridge!,
        moduleName: "<MODULE_NAME>",
        initialProperties: nil)
    self.bridge = rootView.bridge
    // Configure views in the application
    window = UIWindow(frame: UIScreen.main.bounds)
    let rootViewController = UIViewController()
    rootViewController.view = rootView
    window?.rootViewController = rootViewController
    window?.makeKeyAndVisible()
    // Setup Braze
    let configuration = Braze.Configuration(
        apiKey: "<API_KEY>",
        endpoint: "<ENDPOINT>")
    // - Enable logging and customize the configuration here
    configuration.logger.level = .info
    let braze = BrazeReactBridge.perform(#selector(BrazeReactBridge.initBraze(_:)), with: configuration).takeUnretainedValue() as! Braze
    AppDelegate.braze = braze

Thanks a lot for your help working this out @hokstuff really appreciate it 🙏

hokstuff commented 1 year ago

Great to hear you got it up and running! We'll update our public docs with the relevant code.

One thing to note above is that you have the following two lines copied twice:

    window?.rootViewController = rootViewController
    window?.makeKeyAndVisible()
bernardorubin commented 1 year ago

You're right thanks @hokstuff

bernardorubin commented 1 year ago

@hokstuff I'm able to see users and lifetime sessions increase in the dev console.

But I'm not able to find the user neither by id or email after assigning a user id and email like suggested in the docs:

useEffect(() => {
    Braze.changeUser('test-user-id-10');
    Braze.setEmail('bernardorubin@gmail.com');
  }, []);
Screenshot 2023-02-24 at 20 52 00

Any ideas what might be causing this? Could this be related to the bridging header?

I confirmed both the apikey and endpoints are correct on both iOS and android but still not able to find the users in the braze console. I only see the users numbers going up, but can't find the users by email or id in the search field.

Screenshot 2023-02-24 at 20 51 05

Appreciate your help 🙏

Update:

It appears like the user id is being stored and I can see the id working when creating a campaign, but the search feature mentioned above is still broken and showing "user not found".

Screenshot 2023-02-26 at 9 22 59
hokstuff commented 1 year ago

Can you raise this issue with support@braze.com? I'm unsure if this is an issue due to the React Native SDK, user error, or the dashboard search feature, and they can help narrow down the root cause. They will also request for verbose logging to verify that those API calls are successfully reaching the backend as expected and whether or not this behavior occurs on one or both mobile platforms.

Thanks!

bernardorubin commented 1 year ago

@hokstuff we're currently getting this error even when just installing the react native sdk without adding any other code:

The following build commands failed:
    CompileSwift normal arm64 (in target 'BrazeUI' from project 'Pods')
    CompileSwiftSources normal arm64 com.apple.xcode.tools.swift.compiler (in target 'BrazeUI' from project 'Pods')

It only fails on App Center building, and not in Xcode locally.

We already tried a bunch of these solutions but nothing seems to work: https://stackoverflow.com/questions/63607158/xcode-building-for-ios-simulator-but-linking-in-an-object-file-built-for-ios-f

Do you have any ideas as to how we can try to fix this?

Thanks in advance @hokstuff

hokstuff commented 1 year ago

Hi, can you contact support@braze.com with more context around your setup including:

  1. What version of Xcode are you using?
  2. Do you have Enable bitcode set to true in your project?
  3. Can you attach your podfile.lock and your Podfile?
  4. When you say It only fails on App Center building, are you referencing that this fails to upload to Apple's App Store?
  5. Does this error only occur when using BrazeUI, or does it occur when using BrazeKit by itself?
bernardorubin commented 1 year ago

Hi @hokstuff I see you just released the 4.0.0 version of the braze RN SDK.

Currently it doesn't build as it's complaining about errors with the braze bridge in the swift appdelegate file.

I don't want to annoy youa any further, but would you mind pointing me to the correct direction in your docs were I can see what the new implementation of the bridge should look like now on V4.0.0?

Screenshot 2023-03-31 at 15 35 12
hokstuff commented 1 year ago

Hi @bernardorubin,

The integration code will be the Swift equivalent of this snippet in our ObjC example.

Can you check and see if this is the sufficient translation into your Swift app? We currently have a pull request in our public docs to update to the snippet below and would appreciate if you could validate this:

    // Setup Braze bridge
    let jsCodeLocation : URL = RCTBundleURLProvider.sharedSettings().jsBundleURL(
      forBundleRoot: "index"
    )
    let rootView = RCTRootView(
      bundleURL: jsCodeLocation,
      moduleName: "<YOUR_PROJECT_NAME>",
      initialProperties: nil,
      launchOptions: launchOptions
    )
    self.bridge = rootView.bridge

    /* The rest of the code */

Thanks!

bernardorubin commented 1 year ago

@hokstuff just confirmed this works for us, thanks for the update

bernardorubin commented 1 year ago

Hey @hokstuff I'm trying to navigate in the app using key value pairs like so:

Screenshot 2023-05-23 at 22 20 57

I have no issues when navigating correctly in the app on iOS when the app is either opened in the background or closed, when I tap on the notification I get the payload and can navigate accrodingly. Unfortunately on android it only navigates when the app is opened in the background, when it's force closed it only opens the app. I tried using this for android:

useEffect(() => {
    // This works when the app is open
    if (isAndroid) {
      const pushEventSubscription = Braze.addListener(
        Braze.Events.PUSH_NOTIFICATION_EVENT,
        (data) => {
          const brazePushNotificationObject = { data: data?.kvp_data };
          reactiveLegacyPushNotification(brazePushNotificationObject);
          Alert.alert('braze');
        },
      );

      // Fetch the initial URL, if any, that launched the app
      Braze.getInitialURL((url) => {
        if (url) {
          Alert.alert('App opened with URL', url);
          // handle the initial URL
        }
      });

      return () => {
        pushEventSubscription.remove();
      };
    }
  }, []);

But the listener only fires when the app is open. I'm confused and thought I might have to override something on my MainActivity.java:

protected void onCreate(Bundle savedInstanceState) {
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
        SplashScreen.show(this);
        super.onCreate(savedInstanceState);
        application = (MainApplication) getApplication();
    }

as I don't think the CustomFirebaseMessagingService.java is enough to pass the payload:

package com.sothebys.reactnative.notifications;

import android.content.Context;
import com.braze.push.BrazeFirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

public class CustomBrazeFirebaseMessagingService extends BrazeFirebaseMessagingService {
    public void onMessageReceivedWithContext(RemoteMessage remoteMessage, Context context) {
        attachBaseContext(context);
        onMessageReceived(remoteMessage);
    }
}

I'm a bit lost as to how I can achieve this and thought you might have an idea @hokstuff, thanks in advance 🙏

davidbielik commented 1 year ago

Hi @bernardorubin can you please raise a support ticket at https://support.braze.com or support@braze.com

It'll be easier/faster to troubleshoot than in github which is better suited for specific SDK method bugs/feature requests.