---
# System prepended metadata

title: "\U0001F4CA Views and shared object"

---

# 📊 Views and shared object

## Navigation

- [🏡 Native modules made easy with Expo](/gYH9xz-oR2ai0Yih8if50w)
- [👶 First Steps](/ANE6NSUlTSimTIrN-gMsBw)
- [⚙️ Native Module](/mAIt0ctDTvSL5xV4uE9nWQ)
- [📈 Passing data to view](/_gWWp8uoQwGkqKcEyfk8Tg) 
- [📚 View Props](/96IjlLNDRvydILdkbfaA5A)
- [🔥 View Events](/PQWXYmxLRCebmLfx3Fh_gg)
- [🏞️ View Functions](/DcjStCFdT6euzWqnJqCL6w)
- [👷‍♂️ Classes and Shared Objects](/__42gVw8RqiIgfSbVseIvw)
- [👉 📊 Views and shared object](/IgHyIAHQQPCbeBD74Ri4BA)

---

### Task

Create a linear chart data representation utilizing shared objects and export it as a native class. The shared linear chart data will be passed to the view using a view function. The attached view should also be directly updated when adding new data to the shared data set. Additionally, consider making the exported class constructable from a typed array for convenience.

:::spoiler :robot_face: Android 

:open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartModule.kt`

```kotlin
class SharedDataSetOptions : Record {
  @Field
  val mode: DataMode = DataMode.LINEAR

  @Field
  val lineWidth: Float = 5f
}

class SharedDataSet(
  initValues: Uint8Array? = null,
  options: SharedDataSetOptions? = null
) : SharedObject() {
  fun interface Listener {
    fun onNewData(newDataSet: LineDataSet)
  }

  private val listeners = mutableListOf<Listener>()

  private val dataSet = LineDataSet(
    initValues
      ?.withIndex()
      ?.map { (index, value) ->
        Entry((index + 1).toFloat(), value.toFloat())
      }
      ?.toMutableList() ?: mutableListOf<Entry?>(),
    "label"
  ).also {
    it.applyDefaultSettings()
    options?.mode?.toLineDataSetMode()?.let { mode->
      it.mode = mode
    }
    options?.lineWidth?.let { lineWidth ->
      it.lineWidth = lineWidth
    }
  }

  fun addListener(newDataListener: Listener) {
    listeners.add(newDataListener)

    if (dataSet.entryCount != 0) {
      newDataListener.onNewData(dataSet)
    }
  }

  fun removeListener(listener: Listener?) {
    if (listener == null) {
      return
    }
    listeners.remove(listener)
  }

  fun addData(value: Float) {
    dataSet.addEntry(
      Entry(
        (dataSet.entryCount + 1).toFloat(),
        value
      )
    )
    listeners.forEach { it.onNewData(dataSet) }
  }
}

// ...

AsyncFunction("setDataSet") { view: LinearChartView, sharedDataSet: SharedDataSet ->
  view.setSharedDataSet(sharedDataSet)
}

// ...

Class(SharedDataSet::class) {
  Constructor { initValues: Uint8Array?, options: SharedDataSetOptions? ->
    return@Constructor SharedDataSet(initValues, options)
  }

  Function("add") { sharedObject: SharedDataSet, y: Float ->
    appContext.mainQueue.launch {
      sharedObject.addData(y)
    }
  }
}
``` 

:open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartView.kt`

```kotlin
class LinearChartView(
  context: Context,
  appContext: AppContext
) : ExpoView(context, appContext), SharedDataSet.Listener {
  
  // ...
  
  private var currentDataSet: SharedDataSet? = null

  fun setSharedDataSet(dataset: SharedDataSet) {
    currentDataSet?.removeListener(this)
    dataset.addListener(this)
    currentDataSet = dataset
  }
  
  override fun onNewData(newDataSet: LineDataSet) {
    chartView.applyNewData(newDataSet)
  }
}
```

:::
 
:::spoiler :apple: iOS

:open_file_folder: File `ios/LinearChartModule.swift`

```swift
protocol Observer: AnyObject {
  func dataWasUpdated(newDataSet : LineChartDataSet)
}

struct SharedDataSetOptions : Record {
  @Field
  var mode: DataMode = DataMode.LINEAR

  @Field
  var lineWidth = 5.0
}

class SharedDataSet : SharedObject {
  private var listeners: [Observer] = []

  private var dataSet: LineChartDataSet

  init(_ initValues: Uint8Array?, _ options: SharedDataSetOptions?) {
    dataSet = LineChartDataSet()

    if let initValues = initValues {
      for i in 0..<initValues.length {
        dataSet.append(
          ChartDataEntry(x: Double(i+1), y: Double(initValues[i]))
        )
      }
    }

    dataSet.applyDefaultSettings()

    if let mode = options?.mode.toLineDataSetMode() {
      dataSet.mode = mode
    }

    if let lineWidth = options?.lineWidth {
      dataSet.lineWidth = lineWidth
    }

    super.init()
  }

  func addListener(newDataListener: Observer) {
    listeners.append(newDataListener)

    if (dataSet.count != 0) {
      newDataListener.dataWasUpdated(newDataSet: dataSet)
    }
  }

  func removeListener(listener: Observer?) {
    if let listener = listener {
      listeners.removeAll(where: { $0 === listener })
    }
  }

  func addData(value: Double) {
    dataSet.append(
      ChartDataEntry(x: Double(dataSet.count + 1), y: value)
    )

    listeners.forEach { $0.dataWasUpdated(newDataSet: dataSet) }
  }
}

// ...

AsyncFunction("setDataSet") { (view: LinearChartView, sharedDataSet: SharedDataSet) in
  view.setDataSet(dataSet: sharedDataSet)
}

// ...

Class(SharedDataSet.self) {
  Constructor { (initValues: Uint8Array?, options: SharedDataSetOptions?) in
    return SharedDataSet(initValues, options)
  }

  Function("add") { (sharedObject: SharedDataSet, newY: Double) in
    DispatchQueue.main.async {
      sharedObject.addData(value: newY)
    }
  }
}
```

:open_file_folder: File `ios/LinearChartView.swift`

```swift
class LinearChartView: ExpoView, ChartViewDelegate, Observer {
  var currentDataSet: SharedDataSet? = nil
  
  // ...

  func setDataSet(dataSet: SharedDataSet) {
    currentDataSet?.removeListener(listener: self)
    dataSet.addListener(newDataListener: self)
    currentDataSet = dataSet
  }

  func dataWasUpdated(newDataSet: LineChartDataSet) {
    chartView.applyNewData(dataSet: newDataSet)
  }
}
```

:::

<br />

📝 Full changelog: [open GitHub Commit](https://github.com/software-mansion-labs/appjs-2023-workshop-expo-modules/commit/86170599d9b8c141c8bc7f133600aa43c3c48724)

---
