Integrate with Existing Apps

Currently, Lynx is not suitable for building a new application from scratch. You need to integrate Lynx (engine) with your native mobile app or web app, and load Lynx apps through Lynx views. With a few steps, you can start developing with Lynx in your application.

Choose your target platform to view the specific integration steps:

Integrate Lynx with Existing Apps (iOS)

Lynx for iOS
  • This article assumes that you are familiar with the basic concepts of native iOS application development.
  • You can refer to the project: integrating-lynx-demo-projects for all the code mentioned below.

1. Dependency configuration

Using Cocoapods can easily integrate Lynx into your application

Recommended Versions
  • Cocoapods: >= 1.11.3
  • Ruby: >= 2.6.10

Configuring Deps

  1. Lynx

The core capabilities of Lynx Engine include basic capabilities such as parsing Bundle, style parsing, layout, and rendering views

Get the latest version of Lynx from Cocoapods. Then add Lynx to your Podfile:

Podfile
source 'https://cdn.cocoapods.org/'

platform :ios, '10.0'

target 'YourTarget' do
  pod 'Lynx', '3.2.0', :subspecs => [
    'Framework',
  ]

  pod 'PrimJS', '2.12.0', :subspecs => ['quickjs', 'napi']
end
  1. Lynx Service

Lynx Service includes LynxImageService, LynxLogService, etc. It aims to provide the ability to strongly correlate some host App features, allowing the App to inject custom Services at runtime, or use the default implementation provided by the official. For example, LynxImageService is implemented using the SDWebImage image library by default. Apps that do not integrate SDWebImage components can rely on other image libraries to implement Image Service. Lynx provides standard native Image, Log, and Http service capabilities, which can be quickly accessed and used by the access party;

Get the latest version of Lynx Service from Cocoapods. Then add Lynx Service to your Podfile:

Podfile
source 'https://cdn.cocoapods.org/'

platform :ios, '10.0'

target 'YourTarget' do
  pod 'Lynx', '3.2.0', :subspecs => [
    'Framework',
  ]

  pod 'PrimJS', '2.12.0', :subspecs => ['quickjs', 'napi']

  # integrate image-service, log-service, and http-service
  pod 'LynxService', '3.2.0', :subspecs => [
      'Image',
      'Log',
      'Http',
  ]

  # ImageService dependencies:
  pod 'SDWebImage','5.15.5'
  pod 'SDWebImageWebPCoder', '0.11.0'
end

Install Dependencies

Run pod install to install dependencies, then open your Xcode project. Additionally, make sure to disable the Sandbox Scripting capability.

NOTE

In order to disable the Sandbox scripting, in Xcode click on your app, then on build settings. Filter for script and set the User Script Sandboxing to NO.

2. Lynx Environment Initialization

LynxEnv Initialization

LynxEnv provides a global initialization interface for the Lynx Engine. Please ensure that the initialization of LynxEnv occurs before any interface call of the Lynx Engine. For example, it can be initialized in AppDelegate

Objective-C
Swift
AppDelegate.m
#import <Lynx/LynxEnv.h>
#import <Lynx/LynxView.h>

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [LynxEnv sharedInstance];
  return YES;
}

3. Render LynxView

LynxView is the basic rendering unit provided by Lynx Engine. LynxView is an implementation inherited from iOS native UIView. You can quickly construct a LynxView and add it to the ViewController.

Create Bundle Loader

Lynx Engine itself does not have the ability to load resources, so it is necessary to initialize LynxEnv, or when constructing LynxView to pass in the specific implementation of the LynxTemplateProvider protocol. Lynx will use the injected resource loader to obtain the Bundle content

You can use various methods to obtain the contents of the Bundle. Here, we choose to embed the contents of the Bundle within the application.

  1. First, please either generate the Bundle file from the Quick Start phase or download the file below to your local machine, and then follow these steps to embed the file:
NOTE

Steps to embed files:

  • In the target settings of the project, select the target;
  • Select the Build Phases tab;
  • In the Copy Bundle Resources section, click the add button (+) to add the file;
  1. Impl Bundle Loader
Objective-C
Swift
DemoLynxProvider.h
#import <Foundation/Foundation.h>
#import <Lynx/LynxTemplateProvider.h>

NS_ASSUME_NONNULL_BEGIN

@interface DemoLynxProvider : NSObject <LynxTemplateProvider>

@end

NS_ASSUME_NONNULL_END
Objective-C
Swift
DemoLynxProvider.m
#import <Foundation/Foundation.h>

#import "DemoLynxProvider.h"

@implementation DemoLynxProvider

- (void)loadTemplateWithUrl:(NSString*)url onComplete:(LynxTemplateLoadBlock)callback {
    NSString *filePath = [[NSBundle mainBundle] pathForResource:url ofType:@"bundle"];
    if (filePath) {
      NSError *error;
      NSData *data = [NSData dataWithContentsOfFile:filePath options:0 error:&error];
      if (error) {
        NSLog(@"Error reading file: %@", error.localizedDescription);
        callback(nil, error);
      } else {
        callback(data, nil);
      }
    } else {
      NSError *urlError = [NSError errorWithDomain:@"com.lynx"
                                                  code:400
                                                userInfo:@{NSLocalizedDescriptionKey : @"Invalid URL."}];
      callback(nil, urlError);
    }
}

@end

Construct Basic LynxView

you may construct a basic LynxView as follows:

Objective-C
Swift
ViewController.m
#import <Lynx/LynxView.h>

#import "ViewController.h"
#import "DemoLynxProvider.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    LynxView *lynxView = [[LynxView alloc] initWithBuilderBlock:^(LynxViewBuilder *builder) {
      builder.config = [[LynxConfig alloc] initWithProvider:[[DemoLynxProvider alloc] init]];
      builder.screenSize = self.view.frame.size;
      builder.fontScale = 1.0;
    }];
    lynxView.preferredLayoutWidth = self.view.frame.size.width;
    lynxView.preferredLayoutHeight = self.view.frame.size.height;
    lynxView.layoutWidthMode = LynxViewSizeModeExact;
    lynxView.layoutHeightMode = LynxViewSizeModeExact;
}
@end

Add LynxView To The Window:

and then, adding the LynxView to the window.

Objective-C
Swift
ViewController.m
#import <Lynx/LynxView.h>

#import "ViewController.h"
#import "DemoLynxProvider.h"

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // ...

  [self.view addSubview:lynxView];
}

@end

Render View

After completing the above steps, all the work of initializing LynxView have been completed. Call the lynxView.loadTemplateFromURL method to render the corresponding Bundle onto the LynxView.

Objective-C
Swift
ViewController.m
#import <Lynx/LynxView.h>

#import "ViewController.h"
#import "DemoLynxProvider.h"

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  // ...

  [lynxView loadTemplateFromURL:@"main.lynx" initData:nil];
}

@end

Then you will see the following interface on the screen:

Congratulations, you have now completed all the work of integrating Lynx Engine!

4. Now what?

At this stage, you have successfully integrated Lynx into your App. Refer to our developing and debugging docs for in-depth insights on working with Lynx.

Integrate Lynx with Existing Apps (Android)

Lynx for Android
  • This article assumes that you are familiar with the basic concepts of native Android application development.
  • You can refer to the project: integrating-lynx-demo-projects for all the code mentioned below.

1. Dependency configuration

Configuring Gradle

  1. Lynx

The core capabilities of Lynx Engine include basic capabilities such as parsing Bundle, style parsing, layout, rendering views and the basic code of the javascript runtime that Lynx pages rely on

build.gradle
build.gradle.kts
build.gradle
dependencies {
    // lynx dependencies
    implementation "org.lynxsdk.lynx:lynx:3.2.0"
    implementation "org.lynxsdk.lynx:lynx-jssdk:3.2.0"
    implementation "org.lynxsdk.lynx:lynx-trace:3.2.0"
    implementation "org.lynxsdk.lynx:primjs:2.12.0"
}
  1. Lynx Service

Lynx Service includes LynxImageService, LynxLogService, etc. It aims to provide the ability to strongly correlate some host App features, allowing the App to inject custom Services at runtime, or use the default implementation provided by the official.For example, LynxImageService is implemented using the Fresco image library by default. Apps that do not integrate Fresco components can rely on other image libraries, such as Glide to implement Image Service. Lynx provides standard native Image, Log, and Http service capabilities, which can be quickly accessed and used by the access party;

NOTE

Since Fresco depends on AndroidX, you also need to configure the following in gradle.properties:

android.useAndroidX=true
build.gradle
build.gradle.kts
build.gradle
dependencies {
    // lynx dependencies
    implementation "org.lynxsdk.lynx:lynx:3.2.0"
    implementation "org.lynxsdk.lynx:lynx-jssdk:3.2.0"
    implementation "org.lynxsdk.lynx:lynx-trace:3.2.0"
    implementation "org.lynxsdk.lynx:primjs:2.12.0"

    // integrating image-service
    implementation "org.lynxsdk.lynx:lynx-service-image:3.2.0"

    // image-service dependencies, if not added, images cannot be loaded; if the host APP needs to use other image libraries, you can customize the image-service and remove this dependency
    implementation "com.facebook.fresco:fresco:2.3.0"
    implementation "com.facebook.fresco:animated-gif:2.3.0"
    implementation "com.facebook.fresco:animated-webp:2.3.0"
    implementation "com.facebook.fresco:webpsupport:2.3.0"
    implementation "com.facebook.fresco:animated-base:2.3.0"

    implementation "com.squareup.okhttp3:okhttp:4.9.0"

    // integrating log-service
    implementation "org.lynxsdk.lynx:lynx-service-log:3.2.0"

    // integrating http-service
    implementation "org.lynxsdk.lynx:lynx-service-http:3.2.0"
}

Configure obfuscation rules (Proguard)

The obfuscation rules for Lynx Engine are as follows. It is recommended to refer to the latest source code configuration.

proguard-rules.pro
# LYNX START
# use @Keep to annotate retained classes.
-dontwarn android.support.annotation.Keep
-keep @android.support.annotation.Keep class **
-keep @android.support.annotation.Keep class ** {
    @android.support.annotation.Keep <fields>;
    @android.support.annotation.Keep <methods>;
}
-dontwarn androidx.annotation.Keep
-keep @androidx.annotation.Keep class **
-keep @androidx.annotation.Keep class ** {
    @androidx.annotation.Keep <fields>;
    @androidx.annotation.Keep <methods>;
}

# native method call
-keepclasseswithmembers,includedescriptorclasses class * {
    native <methods>;
}
-keepclasseswithmembers class * {
    @com.lynx.tasm.base.CalledByNative <methods>;
}

# to customize a module, you need to keep the class name and the method annotated as LynxMethod.
-keepclasseswithmembers class * {
    @com.lynx.jsbridge.LynxMethod <methods>;
}

-keepclassmembers class *  {
    @com.lynx.tasm.behavior.LynxProp <methods>;
    @com.lynx.tasm.behavior.LynxPropGroup <methods>;
    @com.lynx.tasm.behavior.LynxUIMethod <methods>;
}

-keepclassmembers class com.lynx.tasm.behavior.ui.UIGroup {
    public boolean needCustomLayout();
}

# in case R8 compiler may remove mLoader in bytecode.
# as mLoader is not used in java and passed as a WeakRef in JNI.
-keepclassmembers class com.lynx.tasm.LynxTemplateRender {
    private com.lynx.tasm.core.LynxResourceLoader mLoader;
}

# the automatically generated setter classes use the class names of LynxBaseUI and ShadowNode and their subclasses.
-keep class com.lynx.tasm.behavior.ui.LynxBaseUI
-keep class com.lynx.tasm.behavior.shadow.ShadowNode
-keep class com.lynx.jsbridge.LynxModule { *; }
-keep class * extends com.lynx.tasm.behavior.ui.LynxBaseUI
-keep class * extends com.lynx.tasm.behavior.shadow.ShadowNode
-keep class * extends com.lynx.jsbridge.LynxModule { *; }
-keep class * extends com.lynx.jsbridge.LynxContextModule
-keep class * implements com.lynx.tasm.behavior.utils.Settable
-keep class * implements com.lynx.tasm.behavior.utils.LynxUISetter
-keep class * implements com.lynx.tasm.behavior.utils.LynxUIMethodInvoker
-keep class com.lynx.tasm.rendernode.compat.**{
    *;
}
-keep class com.lynx.tasm.rendernode.compat.RenderNodeFactory{
    *;
}
# LYNX END

2. Environment Initialization

Lynx Service Initialization

  • Lynx Service provides host feature-related capabilities and it is recommended to complete the initialization of Lynx Service during the Application#onCreate lifecycle of the application.
  • Lynx Service needs to be actively injected
TIP

Please specify your custom Application class in the AndroidManifest.xml file, since Lynx needs to perform some global initialization operations when the application starts.

/app/src/main/AndroidManifest.xml
<application
  android:name=".YourApplication">
</application>
Java
Kotlin
YourApplication.java
import android.app.Application;

import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.memory.PoolConfig;
import com.facebook.imagepipeline.memory.PoolFactory;
import com.lynx.service.http.LynxHttpService;
import com.lynx.service.image.LynxImageService;
import com.lynx.service.log.LynxLogService;
import com.lynx.tasm.service.LynxServiceCenter;

public class YourApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initLynxService();
    }

    private void initLynxService() {
        // init Fresco which is needed by LynxImageService
        final PoolFactory factory = new PoolFactory(PoolConfig.newBuilder().build());
        ImagePipelineConfig.Builder builder =
                ImagePipelineConfig.newBuilder(getApplicationContext()).setPoolFactory(factory);
        Fresco.initialize(getApplicationContext(), builder.build());

        LynxServiceCenter.inst().registerService(LynxImageService.getInstance());
        LynxServiceCenter.inst().registerService(LynxLogService.INSTANCE);
        LynxServiceCenter.inst().registerService(LynxHttpService.INSTANCE);
    }
}

LynxEnv Initialization

LynxEnv provides the global initialization interface for the Lynx Engine. Please ensure that the initialization of LynxEnv occurs before any interface calls to the Lynx Engine. It is recommended to complete the initialization of LynxEnv during the Application#onCreate lifecycle of the application.

Java
Kotlin
YourApplication.java
import com.lynx.tasm.LynxEnv;

public class YourApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initLynxService();
        initLynxEnv();
    }

    private void initLynxEnv() {
        LynxEnv.inst().init(
            this,
            null,
            null,
            null
        );
    }
}
TIP

The parameters for the LynxEnv initialization method are described as follows:

  • appContext: The context object for the application lifecycle, i.e., ApplicationContext.
  • libraryLoader: The loader for native so files, the default value can be null, which will use the system default loader.
  • templateProvider: The global AppBundle loader, the default value can be null;
  • behaviorBundle: The list of custom components, the default value can be null. If there are customization requirements, please refer to custom-native-component

3. Render LynxView

Create Bundle Loader

Lynx Engine itself does not have the ability to integrate downloading resources, so the existing app needs to provide the specific implementation of AbsTemplateProvider, and inject it when initializing LynxEnv or constructing LynxView. Lynx will use the injected resource loader to obtain the Bundle content

You can use various methods to obtain the contents of the Bundle. Here, we choose to embed the contents of the Bundle within the application.

  1. First, please place the Bundle file generated in the Quick Start stage in the src/main/assets directory, or you can download the file below to your local machine and place it in the same directory:
app
└── src
    └── main
        ├── java
        ├── res
        └── assets
            └── main.lynx.bundle
  1. Impl Bundle Loader
Java
Kotlin
DemoTemplateProvider.java
import android.content.Context;
import com.lynx.tasm.provider.AbsTemplateProvider;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class DemoTemplateProvider extends AbsTemplateProvider {
    private Context mContext;

    DemoTemplateProvider(Context context) {
        this.mContext = context.getApplicationContext();
    }

    @Override
    public void loadTemplate(String uri, Callback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try (InputStream inputStream = mContext.getAssets().open(uri);
                     ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) != -1) {
                        byteArrayOutputStream.write(buffer, 0, length);
                    }
                    callback.onSuccess(byteArrayOutputStream.toByteArray());
                } catch (IOException e) {
                    callback.onFailed(e.getMessage());
                }
            }
        }).start();
    }
}

Construct LynxView

LynxView is the basic rendering view provided by Lynx Engine. LynxView inherits from the native Android View. You can quickly construct a LynxView and add it arbitrarily to the native Android view tree.

Java
Kotlin
MainActivity.java
import android.app.Activity;
import android.os.Bundle;

import com.lynx.tasm.LynxView;
import com.lynx.tasm.LynxViewBuilder;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LynxView lynxView = buildLynxView();
        setContentView(lynxView);
    }

    private LynxView buildLynxView() {
        LynxViewBuilder viewBuilder = new LynxViewBuilder();
        viewBuilder.setTemplateProvider(new DemoTemplateProvider(this));
        return viewBuilder.build(this);
    }
}

3.1 Render view

After completing the above steps, all the work of initializing LynxView have been completed. Call the lynxView.renderTemplateUrl method to render the corresponding Bundle onto the LynxView view.

Java
Kotlin
MainActivity.java
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LynxView lynxView = buildLynxView();
        setContentView(lynxView);

        String url = "main.lynx.bundle";
        lynxView.renderTemplateUrl(url, "");
    }
}

Then you will see the following interface on the screen:

Congratulations, you have now completed all the work of rendering the LynxView!

4. Now what?

At this stage, you have successfully integrated Lynx into your App. Refer to our developing and debugging docs for in-depth insights on working with Lynx.

Integrate Lynx with Existing Apps (Web)

Lynx for Web

Lynx for Web implements the Lynx engine in web browsers. With Lynx for Web, you can easily integrate Lynx apps into any existing web project, regardless of whether the project uses React, Vue, Svelte, or plain HTML.

1. Build web artifact

We need you to have read and created a Lynx project according to Quick Start.

Add web configuration

  1. Enter the previously created Lynx project:
cd <lynx-project-name>
  1. Add web configuration (environments.web) to lynx.config.ts:
import { defineConfig } from '@lynx-js/rspeedy';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';

export default defineConfig({
  plugins: [pluginReactLynx()],
  environments: {
    web: {
      output: {
        assetPrefix: '/',
      },
    },
    lynx: {},
  },
});

Build

Run:

npm
yarn
pnpm
bun
npm run build

You will see an additional dist/main.web.bundle file in this project, which is the final web build artifact.

2. Integrate with a new web project

Now that you have a Lynx for Web build artifact, we need to create a web project to use it. Here we use Rsbuild.

Create a web project

Create a new project at the same level as the Lynx project above and run:

npm
yarn
pnpm
bun
npm create rsbuild@latest

Follow the prompts to create a React project.

Configure the project

  1. Navigate to the created project:
cd <web-project-name>
  1. Install dependencies
npm
yarn
pnpm
bun
npm install @lynx-js/web-core @lynx-js/web-elements
  1. Import these dependencies in src/app.tsx
import './App.css';
import '@lynx-js/web-core/index.css';
import '@lynx-js/web-elements/index.css';
import '@lynx-js/web-core';
import '@lynx-js/web-elements/all';

const App = () => {
  return (
    <lynx-view
      style={{ height: '100vh', width: '100vw' }}
      url="/main.web.bundle"
    ></lynx-view>
  );
};

export default App;
  1. Update rsbuild.config.ts
WARNING

server.publicDir needs to be replaced with your actual Lynx project path.

import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default defineConfig({
  plugins: [pluginReact()],
  server: {
    publicDir: [
      {
        name: path.join(
          __dirname,
          '../',
          // Please replace this with your actual Lynx project name
          'lynx-project',
          'dist',
        ),
      },
    ],
  },
});

Start the project

Run:

npm
yarn
pnpm
bun
npm run dev

Visit http://localhost:3000 to see your Lynx application.

Next Steps

Except as otherwise noted, this work is licensed under a Creative Commons Attribution 4.0 International License, and code samples are licensed under the Apache License 2.0.