1--- 2title: Use third-party native libraries in Expo 3description: Learn how to create a simple wrapper around two separate native libraries using Expo Modules. 4--- 5 6import ImageSpotlight from '~/components/plugins/ImageSpotlight'; 7import { Terminal } from '~/ui/components/Snippet'; 8import { Step } from '~/ui/components/Step'; 9import { BoxLink } from '~/ui/components/BoxLink'; 10import { BookOpen02Icon, Grid01Icon } from '@expo/styleguide-icons'; 11 12Expo modules make it possible to easily use native, external libraries built for Android and iOS in React Native projects. 13This tutorial focuses on utilizing the Expo Modules API to create radial charts using two similar libraries accessible on both native platforms. 14 15- [MPAndroidChart by PhilJay](https://github.com/PhilJay/MPAndroidChart) 16- [Charts by Daniel Cohen Gindi](https://github.com/danielgindi/Charts) 17 18The iOS library is inspired by the Android library, so they both have a very similar API and functionality. 19This makes them a good example for this tutorial. 20 21<Step label="1"> 22## Create a new module 23 24You can start by creating a new empty Expo module. We're creating a separate project for this tutorial. However, you can create a new module inside your existing project. 25 26### Start with a new project 27 28To create an empty Expo module that can be published on npm and utilized in any Expo application, run the following command: 29 30<Terminal cmd={['$ npx create-expo-module expo-radial-chart']} /> 31 32> **Tip**: If you aren't going to ship this library, press <kbd>return</kbd> for all of the prompts to accept the default values in the terminal window. 33 34Now, open the newly created `expo-radial-chart` directory to start editing the native code. 35 36### Start with an existing project 37 38Alternatively, you can use the new module as a view inside the existing project. Run the following command in your project's directory: 39 40<Terminal cmd={['$ npx create-expo-module --local expo-radial-chart']} /> 41 42Now, open the newly created `modules/expo-radial-chart` directory to start editing the native code. 43 44</Step> 45 46<Step label="2"> 47 48## Run the example project 49 50To verify that everything is functioning correctly, let's run the example project. In the terminal window, start the TypeScript compiler to watch for changes and rebuild the module JavaScript. 51 52<Terminal 53 cmdCopy="npm run build" 54 cmd={[ 55 '# Run this in the root of the project to start the TypeScript compiler', 56 '$ npm run build', 57 ]} 58/> 59 60In another terminal window, compile and run the example app: 61 62<Terminal 63 cmdCopy="cd example && npx expo run:ios" 64 cmd={[ 65 '$ cd example', 66 '# Run the example app on iOS', 67 '$ npx expo run:ios', 68 '# Run the example app on Android', 69 '$ npx expo run:android', 70 ]} 71/> 72 73</Step> 74 75<Step label="3"> 76## Add native dependencies 77 78Now, add the native dependencies to the module by editing the **android/build.gradle** and **ios/ExpoRadialChart.podspec** files: 79 80```diff android/build.grade 81dependencies { 82 implementation project(':expo-modules-core') 83 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" 84+ implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' 85} 86``` 87 88```diff ios/ExpoRadialChart.podspec 89 s.static_framework = true 90 91 s.dependency 'ExpoModulesCore' 92+ s.dependency 'Charts', '~> 4.1.0' 93 94 # Swift/Objective-C compatibility 95``` 96 97</Step> 98 99<Step label="4"> 100## Define an API 101 102To use the module in the app without mistakes, define the types for the props. This module accepts a list of series — each with a color and a percentage value. 103 104```ts src/ExpoRadialChart.types.ts 105import { ViewStyle } from 'react-native/types'; 106 107export type ChangeEventPayload = { 108 value: string; 109}; 110 111type Series = { 112 color: string; 113 percentage: number; 114}; 115 116export type ExpoRadialChartViewProps = { 117 style?: ViewStyle; 118 data: Series[]; 119}; 120``` 121 122Since we won't implement the module on the web in this example, let's replace the **src/ExpoRadialChartView.web.tsx** file: 123 124```tsx src/ExpoRadialChartView.web.tsx 125import * as React from 'react'; 126 127export default function ExpoRadialChartView() { 128 return <div>Not implemented</div>; 129} 130``` 131 132</Step> 133 134<Step label="5"> 135## Implement the module on Android 136 137Now you can implement the native functionality by editing the placeholder files with the following changes: 138 139- Create a `PieChart` instance and set its `layoutParams` to match the parent view. Then, add it to the view hierarchy using the `addView` function. 140- Define a `setChartData` function that accepts a list of `Series` objects. You can iterate over the list, create a `PieEntry` for each series and store the colors in a separate list. Then, create a `PieDataSet`, use it to create a `PieData` object, and set it as data on the `PieChart` instance. 141 142```kotlin android/src/main/java/expo/modules/radialchart/ExpoRadialChartView.kt 143package expo.modules.radialchart 144 145import android.content.Context 146import android.graphics.Color 147import androidx.annotation.ColorInt 148import com.github.mikephil.charting.charts.PieChart 149import com.github.mikephil.charting.data.PieData 150import com.github.mikephil.charting.data.PieDataSet 151import com.github.mikephil.charting.data.PieEntry 152import expo.modules.kotlin.AppContext 153import expo.modules.kotlin.records.Field 154import expo.modules.kotlin.records.Record 155import expo.modules.kotlin.views.ExpoView 156 157 158class Series : Record { 159 @Field 160 val color: String = "#ff0000" 161 162 @Field 163 val percentage: Float = 0.0f 164} 165 166class ExpoRadialChartView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { 167 internal val chartView = PieChart(context).also { 168 it.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) 169 addView(it) 170 } 171 172 fun setChartData(data: ArrayList<Series>) { 173 val entries: ArrayList<PieEntry> = ArrayList() 174 val colors: ArrayList<Int> = ArrayList() 175 for (series in data) { 176 entries.add(PieEntry(series.percentage)) 177 colors.add(Color.parseColor(series.color)) 178 } 179 val dataSet = PieDataSet(entries, "DataSet"); 180 dataSet.colors = colors; 181 val pieData = PieData(dataSet); 182 chartView.data = pieData; 183 chartView.invalidate(); 184 185 } 186} 187``` 188 189You also need to use the [`Prop`](/modules/module-api/#prop) function to define the `data` prop and call the native `setChartData` function when the prop changes: 190 191```kotlin android/src/main/java/expo/modules/radialchart/ExpoRadialChartModule.kt 192package expo.modules.radialchart 193 194import expo.modules.kotlin.modules.Module 195import expo.modules.kotlin.modules.ModuleDefinition 196 197class ExpoRadialChartModule : Module() { 198 override fun definition() = ModuleDefinition { 199 Name("ExpoRadialChart") 200 201 View(ExpoRadialChartView::class) { 202 Prop("data") { view: ExpoRadialChartView, prop: ArrayList<Series> -> 203 view.setChartData(prop); 204 } 205 } 206 } 207} 208``` 209 210</Step> 211 212<Step label="6"> 213## Implement the module on iOS 214 215Now you can implement the native functionality by editing the placeholder files with the following changes: 216 217- Create a new `PieChartView` instance and use the `addSubview` function to add it to the view hierarchy. 218- Set the `clipsToBounds` property and override the `layoutSubviews` function to make sure the chart view is always the same size as the parent view. 219- Finally, create a `setChartData` function that accepts a list of series, creates a `PieChartDataSet` instance with the data, and assigns it to the `data` property of the `PieChartView` instance. 220 221```swift ios/ExpoRadialChartView.swift 222import ExpoModulesCore 223import Charts 224 225struct Series: Record { 226 @Field 227 var color: UIColor = UIColor.black 228 229 @Field 230 var percentage: Double = 0 231} 232 233class ExpoRadialChartView: ExpoView { 234 let chartView = PieChartView() 235 236 required init(appContext: AppContext? = nil) { 237 super.init(appContext: appContext) 238 clipsToBounds = true 239 addSubview(chartView) 240 } 241 242 override func layoutSubviews() { 243 chartView.frame = bounds 244 } 245 246 func setChartData(data: [Series]) { 247 let set1 = PieChartDataSet(entries: data.map({ (series: Series) -> PieChartDataEntry in 248 return PieChartDataEntry(value: series.percentage) 249 })) 250 set1.colors = data.map({ (series: Series) -> UIColor in 251 return series.color 252 }) 253 let chartData: PieChartData = [set1] 254 chartView.data = chartData 255 } 256} 257``` 258 259You also need to use the [`Prop`](/modules/module-api/#prop) function to define the `data` prop and call the native `setChartData` function when the prop changes: 260 261```swift ios/ExpoRadialChartModule.swift 262import ExpoModulesCore 263 264public class ExpoRadialChartModule: Module { 265 public func definition() -> ModuleDefinition { 266 Name("ExpoRadialChart") 267 268 View(ExpoRadialChartView.self) { 269 Prop("data") { (view: ExpoRadialChartView, prop: [Series]) in 270 view.setChartData(data: prop) 271 } 272 } 273 } 274} 275``` 276 277</Step> 278 279<Step label="7"> 280 281## Write an example app to use the module 282 283You can update the app inside the **example** directory to test the module. Use the `ExpoRadialChartView` component to render a pie chart with three slices: 284 285```tsx example/App.tsx 286import { ExpoRadialChartView } from 'expo-radial-chart'; 287import { StyleSheet } from 'react-native'; 288 289export default function App() { 290 return ( 291 <ExpoRadialChartView 292 style={styles.container} 293 data={[ 294 { 295 color: '#ff0000', 296 percentage: 0.5, 297 }, 298 { 299 color: '#00ff00', 300 percentage: 0.2, 301 }, 302 { 303 color: '#0000ff', 304 percentage: 0.3, 305 }, 306 ]} 307 /> 308 ); 309} 310 311const styles = StyleSheet.create({ 312 container: { 313 flex: 1, 314 }, 315}); 316``` 317 318> **Tip**: If you created the module inside an existing application, make sure to import it directly from your **modules** directory by using a relative import: `import { ExpoRadialChartView } from '../modules/expo-radial-chart';` 319 320</Step> 321 322<Step label="8"> 323## Rebuild and launch your application 324 325To make sure your app builds successfully on both platforms, rerun the build commands from step 2. After the app is successfully built on any of the platform you'll see a pie chart with three slices: 326 327<ImageSpotlight 328 src="/static/images/modules/third-party-library/result.webp" 329 alt="A PieChart module on Android and iOS" 330/> 331 332</Step> 333 334## Next steps 335 336Congratulations! You've created your first simple wrapper around two separate third-party native libraries using Expo Modules! 337 338<BoxLink 339 href="/modules/native-module-tutorial/" 340 title="Creating a native module" 341 icon="arrow-right" 342 iconType="feather" 343 iconSize={16} 344 iconColor="#000" 345/> 346<BoxLink 347 href="/modules/module-api/" 348 title="Expo Module API reference" 349 icon="arrow-right" 350 iconType="feather" 351 iconSize={16} 352 iconColor="#000" 353/> 354