From ee135415877c65bb12143af2c56b23c77d8ada07 Mon Sep 17 00:00:00 2001 From: Mateo Silguero Date: Tue, 8 Oct 2019 09:00:16 -0700 Subject: [PATCH] react native integration tutorial (#567) Summary: Integrate Flipper with React Native, is a bit different than a native app. I wrote this tutorial inspired by nparashuram 's blog. http://blog.nparashuram.com/2019/09/using-flipper-with-react-native.html ## Changelog Flipper + React Native Documentation Pull Request resolved: https://github.com/facebook/flipper/pull/567 Reviewed By: jknoxville Differential Revision: D17809429 Pulled By: passy fbshipit-source-id: a1172d36775f80f1ac849913cb855390d353ade0 --- docs/getting-started.md | 341 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 335 insertions(+), 6 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 1e1b965b1..c7f27bc7e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -20,7 +20,7 @@ Once you start Flipper and launch an emulator/simulator or connect a device, you ![Logs plugin](/docs/assets/initial.png) -### Setup your Android app +## Setup your Android app Add the following permissions to your `AndroidManifest.xml`. The SDK needs these to communicate with the desktop app on localhost via adb. It won't make any external internet requests. @@ -109,6 +109,7 @@ We support both Swift and Objective-C for Flipper with CocoaPods as build and di + ```ruby project 'MyApp.xcodeproj' flipperkit_version = '0.25.0' @@ -168,7 +169,9 @@ target 'MyApp' do end end ``` + + ```ruby project 'MyApp.xcodeproj' flipperkit_version = '0.25.0' @@ -237,6 +240,7 @@ target 'MyApp' do end end ``` + You need to compile your project with the `FB_SONARKIT_ENABLED=1` compiler flag. The above `post_install` hook adds this compiler flag to your project settings. @@ -244,6 +248,7 @@ You need to compile your project with the `FB_SONARKIT_ENABLED=1` compiler flag.
On the first run of `pod install`, `FB_SONARKIT_ENABLED=1` may not be added in the "Build Settings" of your project, but in all the subsequent runs of `pod install`, the above `post_install` hook successfully adds the compiler flag. So before running your app, make sure that `FB_SONARKIT_ENABLED=1` is present in `OTHER_CFLAGS` and `OTHER_SWIFT_FLAGS` for Objective-C and Swift projects respectively. +
Install the dependencies by running `pod install`. You can now import and initialize Flipper in your @@ -252,6 +257,7 @@ AppDelegate. + ```objective-c #import #import @@ -277,7 +283,9 @@ AppDelegate. } @end ``` + + ```swift import UIKit import FlipperKit @@ -298,8 +306,333 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } ``` + +## Ready for takeoff + +Finally, you need to add plugins to your Flipper client. Above we have only added the Layout Inspector plugin to get you started. See [Network Plugin](setup/network-plugin.md) and [Layout Inspector Plugin](setup/layout-plugin.md) for information on how to add them, and also enable Litho or ComponentKit support. You can check the sample apps in the [GitHub repo](https://github.com/facebook/flipper) for examples of integrating other plugins. + +## Setup your React Native app + +_Inspired by [a blog post by Ram N](http://blog.nparashuram.com/2019/09/using-flipper-with-react-native.html)._ + +Integrating Flipper with React Native is a bit different than with a native app. + +### Android + +First, add this line to your `android/gradle.properties`: + +```groovy +# On Android, React Native currently has issues with higher versions +FLIPPER_VERSION=0.23.4 +``` + +Add the following permissions to your `AndroidManifest.xml`. The SDK needs these to communicate with the desktop app on localhost via adb. It won't make any external internet requests. + +`android/app/src/main/AndroidManifest.xml` + +```xml + + +``` + +It's recommended that you add the following activity to the manifest too, which can help diagnose integration issues and other problems: + +```xml + +``` + +Flipper is distributed via JCenter. Add the dependencies to your `build.gradle` file. +You should also explicitly depend on [`soloader`](https://github.com/facebook/soloader) +instead of relying on transitive dependency resolution which is getting deprecated +with Gradle 5. + +We provide a "no-op" implementation of some oft-used Flipper interfaces you can +use to make it easier to strip Flipper from your release builds. + +`android/app/build.gradle` + +```groovy +android { + packagingOptions { + ... + // This line is required to prevent React Native from crash + pickFirst '**/libc++_shared.so' + } +} + +dependencies { + ... + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { + exclude group:'com.facebook.yoga' + exclude group:'com.facebook.flipper', module: 'fbjni' + exclude group:'com.facebook.litho', module: 'litho-annotations' + exclude group:'com.squareup.okhttp3' + } +} +``` + +Now, we create a new file inside `android/app/src/debug/java/com/yourappname/ReactNativeFlipper.java`. + +These are the suggested plugins integrations: + +- Layout Inspector +- Network +- Databases +- Images +- Shared Preferences +- Crash Reporter +- React devtools + +```java +package com.yourappname; + +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.react.ReactFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; + +public class ReactNativeFlipper { + + public static void initializeFlipper(Context context, final ReactInstanceManager reactInstanceManager) { + if (!FlipperUtils.shouldEnableFlipper(context)) { + return; + } + final FlipperClient client = AndroidFlipperClient.getInstance(context); + + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new ReactFlipperPlugin()); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + + final NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceManager.ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } +} +``` + +Now you can initialize Flipper in your Application's `onCreate` method, which involves +initializing SoLoader (for loading the C++ part of Flipper) and starting a `FlipperClient`. + +For this, we edit the `android/app/src/main/java/com/yourappname/MainApplication.java` file. + +```java +package com.yourappname; + +import ... +import com.facebook.react.ReactInstanceManager; + +public class MainApplication extends Application implements ReactApplication { + ... + + @Override + public void onCreate() { + ... + initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + } + + /** + * Loads Flipper in React Native templates. Call this in the onCreate method with something like + * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + * + * @param context + */ + private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (BuildConfig.DEBUG) { + try { + Class aClass = Class.forName("com.yourappname.ReactNativeFlipper"); + aClass + .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) + .invoke(null, context, reactInstanceManager); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } +} +``` + +Finally, open the Flipper desktop app, and run `yarn android` in your terminal. + +### iOS + +We support both Swift and Objective-C for Flipper with CocoaPods as build and distribution mechanism. For CocoaPods 1.7+ following is the configuration. + +#### CocoaPods + +`ios/Podfile` + + + + +```ruby +platform :ios, '9.0' + +def flipper_pods() + flipperkit_version = '0.25' + pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' + pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' + pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' + pod 'FlipperKit/FlipperKitUserDefaultsPlugin', '~>' + flipperkit_version, :configuration => 'Debug' +end + +# Post Install processing for Flipper +def flipper_post_install(installer) + installer.pods_project.targets.each do |target| + if target.name == 'YogaKit' + target.build_configurations.each do |config| + config.build_settings['SWIFT_VERSION'] = '4.1' + end + end + end + file_name = Dir.glob("*.xcodeproj")[0] + app_project = Xcodeproj::Project.open(file_name) + app_project.native_targets.each do |target| + target.build_configurations.each do |config| + cflags = config.build_settings['OTHER_CFLAGS'] || '$(inherited) ' + unless cflags.include? '-DFB_SONARKIT_ENABLED=1' + puts 'Adding -DFB_SONARKIT_ENABLED=1 in OTHER_CFLAGS...' + cflags << '-DFB_SONARKIT_ENABLED=1' + end + config.build_settings['OTHER_CFLAGS'] = cflags + end + app_project.save + end + installer.pods_project.save +end + +target 'your-app-name' do + ... + + target 'your-app-nameTests' do + inherit! :complete + # Pods for testing + end + + # For enabling Flipper. + # Note that if you use_framework!, flipper will no work. + # Disable these lines if you are doing use_framework! + flipper_pods() + post_install do |installer| + flipper_post_install(installer) + end +end +``` + + + +You need to compile your project with the `FB_SONARKIT_ENABLED=1` compiler flag. The above `post_install` hook adds this compiler flag to your project settings. + +
+ +On the first run of `pod install`, `FB_SONARKIT_ENABLED=1` may not be added in the "Build Settings" of your project, but in all the subsequent runs of `pod install`, the above `post_install` hook successfully adds the compiler flag. So before running your app, make sure that `FB_SONARKIT_ENABLED=1` is present in `OTHER_CFLAGS` and `OTHER_SWIFT_FLAGS` for Objective-C and Swift projects respectively. + +
+ +Install the dependencies by running `cd ios && pod install`. You can now import and initialize Flipper in your +`ios/your-app-name/AppDelegate.m`. + +The code below enables the following integrations: + +- Layout Inspector +- Network +- Shared Preferences +- Crash Reporter + + + + + +```objective-c +... +#ifdef DEBUG + #import + #import + #import + #import + #import + #import + #import +#endif + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + [self initializeFlipper:application]; + ... +} + +- (void) initializeFlipper:(UIApplication *)application { + #ifdef DEBUG + FlipperClient *client = [FlipperClient sharedClient]; + SKDescriptorMapper \*layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; + [client addPlugin: [[FlipperKitLayoutPlugin alloc] initWithRootNode: application withDescriptorMapper: layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client start]; + [client addPlugin: [[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; + [client start]; + #endif +} + +@end +``` + + + +Lastly, open the Flipper desktop app, and run `yarn ios` in your terminal. +
- If you do not use CocoaPods as a dependency management tool then currently there is no way to integrate FlipperKit other than manually including all the dependencies and building it. @@ -307,10 +640,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
-## Ready for takeoff - -Finally, you need to add plugins to your Flipper client. Above we have only added the Layout Inspector plugin to get you started. See [Network Plugin](setup/network-plugin.md) and [Layout Inspector Plugin](setup/layout-plugin.md) for information on how to add them, and also enable Litho or ComponentKit support. You can check the sample apps in the [GitHub repo](https://github.com/facebook/flipper) for examples of integrating other plugins. - ## Having trouble? -See the [troubleshooting page](troubleshooting.html) for help with known problems. +See the [troubleshooting page](troubleshooting.html) for help with known problems. \ No newline at end of file