Develop React Native Printing App for Android & iOS with Link-OS SDK

Steven Si -
10 MIN READ

Since its release in 2015, React Native has become one of the popular cross-platform mobile development frameworks used to build thousands of mobile apps. Often, we have developers asking about how to integrate the Link-OS SDK with a React Native app for label printing on Zebra printers. In this tutorial, we will walk through the steps of how to add the Link-OS SDK into a React Native project for Android and iOS, so that the React Native app can perform label printing through the native API in the Link-OS SDK.

As we know, React Native is written entirely in JavaScript, while the Link-OS SDK libraries are written in Java for Android and written in Objective-C for iOS. Is it possible to call Java or Objective-C native methods from React Native? Thanks to the Native Module system provided in React Native framework, the answer is yes. The Native Module system in React Native framework allows us to expose instances of Java or Objective-C classes to JavaScript as JS objects, therefore we can call and execute the native methods from JavaScript. We will demonstrate the steps of how to expose the native methods that are based on the Link-OS SDK libraries through the Native Module system.

In this tutorial, we will create a React Native app with a simple use case that just does two things:

  1. Scan for nearby Bluetooth printers on Android or list the paired Bluetooth printers on iOS.
  2. Print a test label when tapping on a printer on the list.

The source code for this tutorial can be downloaded from ZSDKRCTDevDemo on GitHub.

1. Environment Setup

The environment setup for React Native is well documented. Simply follow the instructions in the Environment Setup doc to set up the development environment.If you  have the latest Android Studio and Xcode installed on a MacBook, you can add the Link-OS SDK into a React Native project and expose the native methods through the Native Module system for both Android and iOS on the same MacBook.

2. Create a React Native App

Once the environment setup is complete, you can go ahead to create a new React Native app. Open a terminal, go to a folder where you want the app to reside, and launch the following CLI command to create an app called ZSDKRCTDevDemo. It may take a few minutes for the CLI to complete, as it will create the folder structure and set up dependencies.

npx react-native init ZSDKRCTDevDemo

After ZSDKRCTDevDemo is created, you will get the following folder structure with the android and ios subfolders, where the corresponding projects for Android Studio and Xcode are located respectively. The the Link-OS SDK libraries for Android and iOS will be added into the android and ios subfolders respectively. The native Java and Objective-C methods for label printing will be created and exposed through the Native Module system from these two subfolders respectively as well.

Graphical user interface, application</p>
<p>Description automatically generated

3. Add Link-OS SDK Libraries

Download and install Link-OS Multiplatform SDK, if you have not done so. In the root directory (link_os_sdk) of the installed SDK, you will see the following folder structure, where you can find the corresponding the Link-OS SDK libraries for Android and iOS under their respective lib fodders. You need to add the libraries to Android project and iOS project separately.

Graphical user interface, application</p>
<p>Description automatically generated                        Graphical user interface</p>
<p>Description automatically generated with medium confidence

3.1 Add Link-OS SDK Library for Android to Android Project

  1. Open the Android project of ZSDKRCTDevDemo in Android Studio.
  2. Click on “Open an existing Android Studio project” or click on File -> Open, then navigate to the android folder and click on Open.
  3. Select the Project view in Android Studio and create a libs subfolder under app folder.
  4. Copy and paste ZSDK_Android_API.jar file into the libs folder.

Note: Do not copy and paste the other *.jar files (in the same folder) that come with ZSDK_Android_API.jar in the same folder to the ZSDKRCTDevDemo project. Adding those*.jar files will cause the duplicate definition errors, because the ZSDKRCTDevDemo project already has those *.jar files added in during the project creation through the CLI command.

  1. Right click on ZSDK_Android_API.jar in the Project view and select “Add As Library …” option, then navigate to the libs folder where you just copied the ZSDK_Android_API.jar into, and click on OK.

Now, the library of the Link-OS SDK for Android is added to the Android project of ZSDKRCTDevDemo.

Graphical user interface, application</p>
<p>Description automatically generated

Note: Since this tutorial will use Bluetooth and Bluetooth scanning, the following three permissions need to be added in the AndroidManifest.xml.

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

3.2 Add Link-OS SDK Library for iOS to iOS Project

  1. Open ZSDKRCTDevDemo.xcworkspace in Xcode.
  2. Add a new folder called ZSDK and a subfolder called include under ZSDKRCTDevDemo folder in the Project navigator.
  3. Copy and paste the libZSDK_API.a into ZSDK folder. Copy and paste all the other header files (*.h) in the include folder in the SDK into the newly created include subfolder. This can also be done through the drag and drop.
  4. Right click on ZSDK folder in the Project navigator and select “Add Files to “ZSDKRCTDevDemo”…” option. Then navigate to ZSDK folder where we just copied the library files into and select libZSDK_API.a, and then click on Add.
  5. Repeat the step 4 above for the header files.

Now, the library and its header files of the Link-OS SDK for iOS are added to the iOS project of ZSDKRCTDevDemo, as shown in the screenshots below.

Graphical user interface, application</p>
<p>Description automatically generated                       A picture containing graphical user interface</p>
<p>Description automatically generated

Note:

  1. The ExternalAccessory.framework needs to be added to the iOS project, because the Link-OS SDK for iOS is based on this framework for Bluetooth connection.
  2. In addition, the com.zebra.rawport protocol string needs to be added to the info.plist under the “Supported external accessory protocols” field. This is required by Apple MFi for connecting Bluetooth on Zebra printers.

The following two screenshots show where and how they can be added.

Graphical user interface, text, application</p>
<p>Description automatically generated

Graphical user interface, text, application, email</p>
<p>Description automatically generated

Now, we have successfully added the SDK library to the project for both Android and iOS. Let’s proceed to expose (or export) the native print methods based on the SDK to JavaScript via Native Modules.

4. Expose (Export) Print Methods via Native Modules

Please note this demo doesn’t expose the entire the Link-OS SDK API to JavaScript, as it’s not practical and unnecessary. The proper and practical way of using the Native Module system is to expose the needed methods implemented with the Link-OS SDK API.

The sections of Android Native Modules and iOS Native Modules on the React Native website have the in-depth details on how to expose the native methods via the Native Module system for Android and iOS. They will not be repeated in this tutorial. We will focus on the implementation of this tutorial.

As mentioned at the beginning, the demo app in this tutorial will do just two things:

  1. Discover the nearby Bluetooth printers on Android or list the paired Bluetooth printers on iOS.
  2. Perform a test print.

So, it is sufficient for this tutorial just to expose (or export) the following two methods for JavaScript to call.

// Start Bluetooth discovery and notify React Native via the callback when discovery is done
zsdkPrinterDiscoveryBluetooth(callback);

// The 1st string is printer’s Bluetooth MAC or serial number. The 2nd
// string is not used for now, as we just want method to print a test label.
zsdkWriteBluetooth(string, string); 

Note: When exposing native methods through Native Modules, it is recommended to use asynchronous macros, i.e. @ReactMethod in Java or RCT_EXPORT_METHOD in Objective-c. The benefit is that it is guaranteed that the native methods will always be executed on a non-UI thread in both Android or iOS. This is exactly what the Link-OS SDK requires. So there is no need to explicitly use a background thread to call the Link-OS SDK API in the native methods.

4.1 For Android

4.1.1 Create an Android Native Module

To expose native Java methods, we create a class that inherits ReactContextBaseJavaModule class and implement the methods to be exposed inside the child class. In this tutorial, we create ZSDKModule class as a child class of ReactContextBaseJavaModule, and then implement and expose the two methods (mentioned above) inside ZSDKModule class.

  1. The ZSDKModule class extends ReactContextBaseJavaModule class and is initialized with the ReactApplicationContext object.
  2. The name of this native module is defined by the return of getName(), not by the name of the class. In this tutorial, the name is called ZSDKModule, as returned by getName().
  3. Both zsdkWriteBluetooth() and zsdkPrinterDiscoveryBluetooth() are exposed to JavaScript in React Native as asynchronous methods with @ReactMethod macro.

Graphical user interface, application</p>
<p>Description automatically generated

  1. The zsdkPrinterDiscoveryBluetooth() method will be called by JavaScript with a callback function. Then it calls BluetoothDiscoverer.findPrinters(), the native API in the Link-OS SDK, to start the Bluetooth discovery.
  2. The zsdkWriteBluetooth() method will be called by JavaScript with the MAC address of the printer as one of the input parameters. Android uses the Bluetooth MAC address of the printer to make a connection. The other input parameter of zsdkWriteBluetooth() is not used in this tutorial, as zsdkWriteBluetooth() simply calls a number of the Link-OS SDK API to print a predefined sample test label.

Graphical user interface, text, application</p>
<p>Description automatically generated

  1. The callback from JavaScript is passed to DiscoveryResult through zsdkPrinterDiscoveryBluetooth(). It is then invoked by discoveryFinished() upon the completion of Bluetooth scan. When the callback is called in the statement of callback.invoke(null, foundPrinterJson.toString()), it passes the discovered printers as a JSON string back to JavaScript. This is one of the common ways to pass data from the native back to JavaScript.

Graphical user interface, text, application, email</p>
<p>Description automatically generated

4.1.2 Expose Android Native Module

The next step is to expose ZSDKModule we just created above to React Native. To do that, we need to add ZSDKModule to the Native Module system.

  1. Create a class called ZSDKModulePackage that implements ReactPackage interface.
  2. Instantiate ZSDKModule class and add the instance to the modules, which is then returned as NativeModules list to register with the Native Module system.
  3. Finally, we instantiate ZSDKModulePackage and add the instance to the package list in MainApplication class, as shown in the screenshots below.

Graphical user interface, text, application, email</p>
<p>Description automatically generated

Graphical user interface, text, application, email</p>
<p>Description automatically generated

Now, the two native methods implemented in ZSDKModule class have been exposed to JavaScript through the Native Module system.

4.2 For iOS

To create and expose a native module in iOS is much simpler than that in Android. It only requires implementing a class that implements RCTBridgeModule interface. In this tutorial, we create a class named RCTZSDKModule. See the screenshots of the code snippets below.

  1. In RCTZSDKModule.h, it simply defines the compliance to the interface defined in RCTBridgeModule.
  2. In RCTZSDKModule.m, it is exposes (or exports) RCTZSDKModule to the Native Module system as ZSDKModule via the RCT_EXPORT_MODULE macro statement of RCT_EXPORT_MODULE(ZSDKModule).

Note: The module name exposed (exported) here needs to be the same as the one from Android, because the frontend React Native uses the same name to reference the native module.

  1. It then implements and exposes the two native methods via the two RCT_EXPORT_METHOD macro statements.
  • RCT_EXPORT_METHOD(zsdkPrinterDiscoveryBluetooth:(RCTResponseSenderBlock)callback ){ … }
  • RCT_EXPORT_METHOD(zsdkWriteBluetooth: (NSString *)printerSerialNumber data:(NSString *)data) { … }

The zsdkWriteBluetooth method takes the serial number of the printer, makes the Bluetooth connection with the serial number, and then prints a predefined test label. The zsdkPrinterDiscoveryBluetooth method finds a list of serial numbers of Zebra printers that are already paired and connected to the iOS device, and then passes the serial numbers as a JSON string back to JavaScript through the callback. 

Graphical user interface, text, application, email</p>
<p>Description automatically generated

Graphical user interface, text, application</p>
<p>Description automatically generated

Graphical user interface, text, application, email</p>
<p>Description automatically generated

This is all we need to do to set up the native module for iOS.

5. UI of ZSDKRCTDevDemo

In the root folder of ZSDKRCTDevDemo project, there is an App.js file. This is the file we will work on to create the UI and its use case logic. In addition, we create a new JavaScript file, called ZSDKModule.js, in the same folder.

  1. ZSDKModule.js is a file to import the ZSDKModule from the Native Modules with a designated name of “ZSDKModule”. Any JavaScript file wants to call a method in ZSDKModule, it only needs to import the ZSDKModule.js file.

Graphical user interface, text, application</p>
<p>Description automatically generated

  1. App.js is the main JavaScript file for this demo app.
    • The import statement at the line 4 imports the ZSDKModule.
    • The printTestLabel() function at the line 13 calls the ZSDKModule.zsdkWriteBluetooth() with either the Bluetooth MAC address or the serial number of the printer, depending on the platform OS the app is running on.
    • The discoverPrinters() function at the line 27 calls the ZSDKModule.zsdkPrinterDiscoveryBluetooth() with a callback. The second parameter of the callback is the JSON string of the discovered printers, passed back from the native module. See the highlighted parts in the screenshot below.

Graphical user interface, text, application</p>
<p>Description automatically generated

6. Run ZSDKRCTDevDemo

Once we have all the files in places, we can run the app on a physical device, either Android or iOS device. For your convenience, the source code for this tutorial can be downloaded from ZSDKRCTDevDemo on GitHub.

  • To run the demo on an Android device, connect the Android device with a USB, go to the root folder of ZSDKRCTDevDemo project, then run following CLI command.
### To run the demo app on Android device
npx react-native run-android
  • To run the demo on an iOS device, connect the iOS device with a USB, open ZSDKRCTDevDemo.xcworkspace in Xcode, and then click on run button in Xcode.

Here are a few screenshots of this demo app:

Shape</p>
<p>Description automatically generated with medium confidence A picture containing scatter chart</p>
<p>Description automatically generated Graphical user interface, text, application</p>
<p>Description automatically generated Graphical user interface, application</p>
<p>Description automatically generated

As always, your comments and feedback are very welcome. Happy coding!

profile

Steven Si

Please register or login to post a reply

26 Replies

L Luis Daniel Rincon Forero

Hi steve

I need your help,I did all the publishing process, but the printer does not connect to the app. the application stays loading
Thanks

S Steven Si

Is this about running the app on an Android device? If it's on an Android device, please check the fine location permission. Refer to this thread for details on how to grant the permission: https://developer.zebra.com/content/zsdk-bluetoothdiscovererfindprinter…

G Giorgio Zett

Hi!
I'm looking into this example, and I'd need to get the Tag ID after print the label on a RFID tag.
How can I get it?
I can print the tag right now, but I can't understand how to get the TID as print response.
Thanks :-)

S Steven Si

HI Giorgio,

This thread is about React Native. For the RIFD question, please create a new thread for discussion.

 

S Steven Si

The Network Discovery is supported by the Link-OS SDK across all platforms, including iOS and Android. For iOS, you need to follow the network multicast requirement. See this article for details:&nbsp;<a href="https://developer.zebra.com/blog/link-os-sdk-ios-printer-network-discov…; hreflang="en">Link-OS SDK for iOS – Printer Network Discovery API impacted by iOS 14.5 and higher</a>.

z zack gan

Hey, followed the flow u mentioned, but still cannot discovery the printer, have tried the following apis:
NetworkDiscoverer.localBroadcast
NetworkDiscoverer.multicastWithHops
NetworkDiscoverer.directedBroadcastWithIpAddress

z zack gan

can only use NetworkDiscoverer.subnetSearchWithRange to discovery ZD620, why?

z zack gan

Got it, thanks!

z zack gan

How to use SGD on the printer? I followed the article and send a txt file included the command (! U1 getvar "bluetooth") to the printer, didn't get any output

z zack gan

Got the mode setting:
bluetooth.le.controller_mode : le , Choices: le
Is the SDK support this mode?

S Steven Si

The "bluetooth.le.controller_mode : le , Choices: le" from the output indicates that this ZD620 unit has the Bluetooth LE only. It doesn't have the Bluetooth Classic. The React Native demo in this blog post only works with the Bluetooth Classic. Currently, the <a href="https://techdocs.zebra.com/link-os/2-13/ios/">Link-OS SDK for iOS</a> supports the Bluetooth Classic only, while the <a href="https://techdocs.zebra.com/link-os/2-14/android_btle/">Link-OS SDK for Android-BTLE</a> supports the Bluetooth LE for Android. To create a React Native project to work with the Bluetooth LE, you can use the library of the&nbsp;<a href="https://techdocs.zebra.com/link-os/2-14/android_btle/">Link-OS SDK for Android-BTLE</a>&nbsp;for the Android part, however, you would need to create your own implementation of the LE for the iOS part.

z zack gan

How about the NetworkDiscoverer Api? try to use it, but can't discover ZD620 on the same network. Does it have any restrictions?

z zack gan

Yes, there is a "LE" next to the Bluetooth icon next to the ZD620 on the Discovered Printers list on the Printer Setup app.

S Steven Si

The Printer Setup app can discover both the Classic and the LE. You may have noticed that there is a "LE" next to the Bluetooth icon next to the ZD620 on the Discovered Printers list on the Printer Setup app.

z zack gan

But the ZD620 can be discovered on the "Printer Setup" IOS App

S Steven Si

This demo is using the Bluetooth Classic to connect to the printers. If the demo cannot discover the ZD620 printer, it could be due to two reasons.

1. By factory default, the "bluetooth.discoverable" is set to off on all Link-OS printers, including the ZD620, for security reason. You can use the following SGD command to turn on the "bluetooth.discoverable" on the ZD620, followed by a restart of the printer. Then the demo should discover the printer.

! U1 setvar "bluetooth.discoverable" "on"

2. If the printer still cannot be discovered after the above step, it's likely that your ZD620 unit may not have the Bluetooth Classic equipped or may have been enabled with the LE only. You can use the following SGD to verify.

! U1 getvar "bluetooth"

If the output contains the following statement, it means the ZD620 has both the Classic and the LE. Otherwise, the ZD620 may have the LE only or may have only been enabled for the LE.

...
bluetooth.le.controller_mode : both , Choices: both,classic,le
...

If the ZD620 has both the Classic and the LE equipped, you can use the following SGD to enable the Classic, the LE or the both.

// Enable the classic only
! U1 setvar "bluetooth.le.controller_mode" "classic"

// Enable the LE only
! U1 setvar "bluetooth.le.controller_mode" "le"

// Enable both
! U1 setvar "bluetooth.le.controller_mode" "both"

 

z zack gan

Hi, I am trying to use the demo to connect the Zebra ZD620, but cannot discover it on the demo. is it support this device?

L Lucus Wili

How to print customized receipts other than test labels using this??? is there any documentation?

H HARSHITH VURUKONDA

Hello, I can print a predefine test label. But I have a define a label structure and print that label with my data. Can you please let me know whether you have any tutorials for that.
Any help is highly appreciated!!

R RENIL R

Hi , When we are trying to test bluetooth discovery on our mobile application in android , its showing no printer found. We developed in react native

Please assist on this.

 

S Steven Si

For the Bluetooth discovery on devices with Android 10 or higher, we must use the ACCESS_FINE_LOCATION permission in the manifest file and request the user permission explicitly in the app. See Link-OS SDK for Android | Permissions and Bluetooth API for details.

In addition, make sure your printer has the proper Bluetooth (Classic or Low Energy) configured. It can be verified with the Printer Setup app. With Bluetooth Classic printer, it can only be discovered by the BluetoothDiscoverer. With Bluetooth Low Energy printer, it can only be discovered by the BluetoothLeDiscoverer.

R RENIL R

Hi Steven , So Can I print labels when I am connected to BLE only printer ?  

S Steven Si

Yes, you can print labels over the BLE connection. For Android devices, you can use the Link-OS SDK for Android BTLE (i.e., the ZSDK_ANDROID_BTLE.jar), which comes with the Link-OS Multiplatform SDK. For iOS devices, unfortunately, we don't have the SDK for BTLE. You would have to create the BTLE connection from the Core Bluetooth framework. There is a demo app using BTLE for iOS on GitHub - zebraPrinterBLEDemo.

R Ricardo Gomez Chavez

Hi how are things! I'm trying to implement the Link-OS cross-platform SDK. with React Native focused on Android devices, however when I want to scan or test print with what the documentation gives, I can´t do it, it already adds these permissions in my manifest.xml 
<use-permission android:name="android.permission.INTERNET" />
<use-permission android:name="android.permission.BLUETOOTH"/>
<use-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<use-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<use-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<use-permission android:name="android.permission.BLUETOOTH_SCAN"/> 

and it also requests permissions at runtime, but the printer is not detected and is ZQ series. To be exact, it is a ZQ210 printer, but I would like to know if anyone has a similar detail or idea that I would need, since it works with Android 13 version

S Steven Si

Make sure Bluetooth is enabled AND discoverable on the ZQ210. You can use the Printer Setup app to verify the Bluetooth settings on the ZQ210.

R Ricardo Gomez Chavez

Yes, through the PrinterSetup application it is visible, it enables the basic options, but within them it is not possible to send a test print, and from the application that is consuming the link-os SDK It cannot be identified, I don't know if I have to see the version of Android that I am using since I use version 13 and the React native documentation works with 10, I already applied the giving permissions, but I still have no success, any other example or github repository where I can support myself to use the SDK and detect printers and send QR codes to print from an application made with React Native?