Adalo Components API Updates
↗️

Adalo Components API Updates

Notes for Component Developers

Overview

We’re updating our dependencies, which means your might need to change too. Here’s what we’re moving to:
react 18.2.0
react-native 0.72.5
react-native-web 0.19.8
node 18.18.0

Deadlines

These updates will roll out to ALL Adalo Makers and Apps on October 30th.
🚧
For public components, please submit new versions for review by October 20th!
 

How to Test on Web and Native

🚧
The steps below assume that you have been added to the feature flags for testing on web and native. If your account doesn’t look like the screenshots below please reach out to us.

Preview your components on the web

When in the Adalo Builder, you’ll see a new button in the top toolbar. Click the “Preview (New)” button to launch a version of the Adalo Previewer with the updated version of React Native Web.
notion image

Preview your components in Native iOS and Android builds

When kicking off a new native build, on the second step of the modal below the App Request Permissions section you should see a new toggle. Turn the toggle ON to run a build with the updated version of React Native. If you need to run a build of your app on the current, production version then you can turn the toggle off.
notion image
 

Helpful Hints and FAQ

Make every effort to make your changes backwards compatible with our current build pipeline! This will greatly reduce the number of problems that are encountered by our Makers! If you are unable to make backwards compatible changes, then we’ll need to coordinate the release of your component updates alongside the releases of react native, react native web, and node.
 
We have upgraded from Node 14 to Node 18 which may present some new error messages in build logs.
  • If your component relies on node-sass, then the version must be updated to be compatible with Node 18, which is not a backwards compatible change with Node 14:
    • consider pre-compiling your Sass into CSS thus avoiding the need for node-sass
    • publish a version of the component that’s compatible with Node 18 and we’ll publish it at the same time as the roll-out
    • 💡
      The error in the build logs for this one is not immediately obvious, it will probably manifest along the lines of “not being able to find the right version of Python” which produces a very long error chain output.
  • If your component relies on extra install scripts then, depending on the type script you’re using, you’ll need to include the interpreter you’re using as the first line of the file. You may see your builds fail with an error like Error: spawn Unknown system error -8 if it’s missing:
    • for a Bash script (e.g. a .sh file) you’ll need to include #!/bin/bash
    • for a Node script (e.g. a .js file) you’ll need to include #!/usr/bin/env node

Component install scripts

If your component relies on install scripts, e.g. if you need to make changes to native code, then please be aware that the native file structure has changed between the older and newer version of React Native.
For example, in the case of iOS, the AppDelegate.m file is now AppDelegate.mm and its contents is slightly altered.
💡
We recommend initialising a fresh React Native project with the version Adalo uses with the command npx react-native@0.72.5 init TestApp --version 0.72.5 to familiarise yourself with the project structure for each native platform.
We have provided a series of “comment markers” in some of the common files to help you target the part of the file you wish to add code to, rather than relying on targeting lines of code that may change over time.
💡
These markers will only be available in the new React Native builds
 
AppDelegate.mm
Target the following comment markers to…
  • // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_IMPORTS - add imports after the standard iOS core and React Native imports
  • // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_START - add code directly after the opening @implementation AppDelegate line
  • // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_DID_FINISH_LAUNCHING_WITH_OPTIONS - to add code to the end of the didFinishLaunchingWithOptions method
  • // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_END - add code directly before the @end of the AppDelegate implementation
  • // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_IMPLEMENTATION - add code directly after the @end of the AppDelegate implementation
 
index.js
Target the following comment markers to…
  • // MARKER_REACT_NATIVE_INDEX_JS_IMPORTS - add imports after the standard iOS/Android React Native imports
  • // MARKER_REACT_NATIVE_INDEX_JS_REGISTER_COMPONENTS - add code directly after the AppRegistry.registerComponent... method call

File reference

Some React Native libraries require small modifications to the native app configuration and occasionally the code. Below are the default contents of the files that are commonly modified.
Android
android/build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext {
        buildToolsVersion = "33.0.0"
        minSdkVersion = 21
        compileSdkVersion = 33
        targetSdkVersion = 33

        // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
        ndkVersion = "23.1.7779620"
        // START ADDED MANUALLY
        kotlinVersion = "1.7.0"
        androidXBrowser = "1.5.0"
        // END ADDED MANUALLY
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle")
        classpath("com.facebook.react:react-native-gradle-plugin")
        classpath("com.google.gms:google-services:4.3.3")
    }
}

allprojects {
  repositories {
      jcenter()
  }
}
android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  <uses-permission android:name="android.permission.VIBRATE" />
  <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> 

  <application
    android:name=".MainApplication"
    android:label="@string/app_name"
    android:icon="@mipmap/ic_launcher"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:allowBackup="false"
    android:theme="@style/AppTheme">
    <activity
      android:name=".MainActivity"
      android:label="@string/app_name"
      android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
      android:launchMode="singleTask"
      android:windowSoftInputMode="adjustResize"
      android:exported="true">
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <meta-data
      android:name="com.google.firebase.messaging.default_notification_icon"
      android:resource="@drawable/ic_stat_ic_notification" />
  </application>
</manifest>
iOS
ios/Podfile - same as React Native 0.72.5 default
ios/AdaloApp/AppDelegate.h
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : RCTAppDelegate <UNUserNotificationCenterDelegate>

@end
ios/AdaloApp/AppDelegate.mm
#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>

// MARKER_REACT_NATIVE_IOS_APP_DELEGATE_IMPORTS

@implementation AppDelegate

// MARKER_REACT_NATIVE_IOS_APP_DELEGATE_START

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.moduleName = @"AdaloApp";
  // You can add your custom initial props in the dictionary below.
  // They will be passed down to the ViewController used by React Native.
  self.initialProps = @{};

  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  // MARKER_REACT_NATIVE_IOS_APP_DELEGATE_DID_FINISH_LAUNCHING_WITH_OPTIONS

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}

//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}

// MARKER_REACT_NATIVE_IOS_APP_DELEGATE_END

@end

// MARKER_REACT_NATIVE_IOS_APP_DELEGATE_IMPLEMENTATION

Linking libraries

The command npx react-native link is no longer available in the latest version of React Native: https://github.com/facebook/react-native/issues/34095
Assuming you’re using a recent version of your dependency, it should be possible to remove any lines similar to npx react-native link react-native-dependency from your component install scripts.
If you need to link assets as part of your component installation, the following library is considered the “modern” alternative to react-native link:

Common Errors

<something>.removeEventListener is not a function
React Native has changed how it handles removing event listeners from some of their APIs.
For example, in the teardown of a component (e.g. componentWillUnmount() or the functional component equivalent), you may have had the following code:
import { Component } from 'react'
import { Keyboard } from 'react-native'

class MyComponent extends Component {

	// ...more component code

  componentDidMount() {
    Keyboard.addListener('event', this.callback)
  }

  componentWillUnmount() {
    Keyboard.removeListener('event', this.callback)
  }
}
In React 0.72.5, there is no removeEventListener method on some of these APIs (the Keyboard API is one of them), but instead a remove method that you must call on a reference to the event listener.
 
The above code becomes:
import { Component } from 'react'
import { Keyboard } from 'react-native'

class MyComponent extends Component {

	// ...more component code

	keyboardEventListener = null

  componentDidMount() {
    this.keyboardEventListener = Keyboard.addListener('event', this.callback)
  }

  componentWillUnmount() {
		// this maintains backwards compatibility with older React Native versions
    // while the Adalo platform transitions from old to new React Native
    if (
      this.keyboardEventListener &&
      this.keyboardEventListener.remove &&
      typeof this.keyboardEventListener.remove === 'function'
    ) {
      this.keyboardEventListener.remove()
    } else {
      Keyboard.removeListener('event', this.callback)
    }
  }
}