You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Android Player dependency automatically pulls in both the `dev` as well as the `prod` versions of the underlying JS scripts.
53
-
54
-
The `dev` scripts are handy for debugging shared JS code through the JS debugger in debug builds, but they are not necessary for consumer facing release builds.
55
-
56
-
To optimize the release bundles, it is highly recommended to exclude the `dev` scripts, as well as enabling V8 to strip out unused code from the final bundle.
57
-
58
-
```kotlin
59
-
buildTypes {
60
-
getByName("release") {
61
-
isMinifyEnabled =true
62
-
isShrinkResources =true
63
-
packagingOptions {
64
-
exclude("**/*.dev.js")
65
-
}
66
-
}
67
-
}
68
-
```
69
-
70
49
</Fragment>
71
50
</PlatformTabs>
72
51
@@ -114,16 +93,14 @@ var body: some View {
114
93
```kotlin
115
94
// create Android Player with reference assets plugin
116
95
val player =AndroidPlayer(
117
-
listOf(
118
-
ReferenceAssetsPlugin(),
119
-
// Any other plugins
120
-
)
96
+
ReferenceAssetsPlugin(),
97
+
// Any other plugins
121
98
)
122
99
```
123
100
124
101
Apart from providing a list of plugins while setting up `AndroidPlayer`, you can also provide a `config` object that has the following options:
125
102
126
-
-`debuggable` - Indicates if the runtime is debuggable on android through chromium devtools, enabling this would let you set breakpoints in js code as you're running it on android.
103
+
-`debuggable` - Indicates Player should instrument its `debuggable` constructs
127
104
-`coroutineExceptionHandler` - [CoroutineExceptionHandler](https://kotlinlang.org/docs/exception-handling.html#coroutineexceptionhandler) should handle all uncaught exceptions while using the runtime coroutine scope.
128
105
-`timeout` - Timeout for the JS thread. If none is provided then it is set as `if (debuggable) Int.MAX_VALUE.toLong() else 5000`.
When you're done with Player, release any native runtime memory used to instantiate Player or run the flow.
195
-
196
-
```kotlin
197
-
player.release()
198
-
```
199
-
200
-
Creating your own `AndroidPlayer` instance is fairly simple, but it grows in complexity when considering proper resources management and app orchestration. We provide a Fragment/ViewModel integration to make it easier to properly integrate into your app and account for these concerns.
201
-
202
-
#### ViewModel
203
-
204
-
With the `PlayerViewModel` and `PlayerFragment`, the above orchestration is done for you. All that is needed is to provide concrete implementations of each and add the fragment to your app.
205
-
206
-
##### PlayerViewModel
207
-
208
-
The `PlayerViewModel` requires an `AsyncFlowIterator` to be supplied in the constructor. The AsyncFlowIterator is what tells Player which flows to run. This can be hardcoded into the view model or expected as an argument, as shown below.
The `PlayerFragment` is a simple Android `Fragment` that only requires a specific `PlayerViewModel` to be defined. If your view model requires the `AsyncFlowIterator` to be passed as part of the constructor, you can leverage the `PlayerViewModel.Factory` to produce it, as shown below.
219
-
220
-
Specifically, this fragment takes a flow as an argument to the constructor and creates a single-flow `AsyncFlowIterator` instance using the pseudo-constructor helper.
As the core Player is written in TypeScript, we need a JVM compatible JavaScript runtime to power a JVM based Player. There are several options to choose from, however, deciding on a specific runtime implementation is difficult to do at the library layer. Different use cases may demand different trade-offs regarding supporting multiple platforms, size, and speed. Thus, the base JVM Player implementation was done with a custom runtime abstraction, powered by [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization), to enable consumers to choose the runtime based on their needs.
233
-
234
-
To support a specific runtime implementation, there needs to be code connecting the runtime constructs to the Player runtime abstraction. The Player project has implemented this layer for several runtimes, described below. It is possible to define your own, but the abstraction definition is not yet final, and therefore not appropriately documented. You can take inspiration from the existing implementations, or file an issue for a runtime you wish to see supported by our team.
Each of the artifacts for the above are defined as `com.intuit.playerui:$runtime-$platform`, i.e. `com.intuit.playerui:j2v8-android`
244
-
:::
245
-
246
-
The `HeadlessPlayer` does not define a hard dependency on any specific runtime, however, the `AndroidPlayer` does transitively depends on the `j2v8` runtime, as the first class approach. To override, the transitive dependency would need to be explicitly excluded and the actual runtime dependency would need to be added:
If your application includes dependencies that may transitively depend on `com.intuit.playerui:android`, you would likely need to ensure the default runtime is transitively excluded from those as well, either manually or as a global strategy.
264
-
:::
265
-
171
+
Creating your own `AndroidPlayer` instance is fairly simple, but it grows in complexity when considering proper resources management and app orchestration. We provide a [Fragment/ViewModel integration](/platforms/android#viewmodel) to make it easier to properly integrate into your app and account for these concerns.
Copy file name to clipboardExpand all lines: docs/site/src/content/docs/guides/multi-flow-experiences.mdx
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -100,4 +100,7 @@ struct App: View {
100
100
```
101
101
102
102
</Fragment>
103
+
<Fragmentslot='android'>
104
+
On Android, the Managed Player paradigm is currently realized through the `PlayerFragment` and `PlayerViewModel` constructs. More information can be found in the [Android documentation](/platforms/android#viewmodel).
While we have published a majority of the plugins we have developed, there will always be new use cases that may require new functionality. Writing a plugin in the easiest way to extend Player functionality for these cases. Plugins work slightly differently on each platform so in this guide we will cover how to write a plugin for each platform.
9
11
@@ -56,6 +58,9 @@ For a more comprehensive guide on plugins, check out this [Plugin Implementation
56
58
57
59
_Note: For the React Player you can import and load the plugin the same way you would a React Player Plugin but for the iOS and Android Players you will need to wrap the javascript bundle in a iOS/Android plugin to ensure it is available on your platform._
58
60
61
+
:::caution
62
+
Even though core plugins are written in TypeScript, they're not targeting web environments, which limits the common [global web APIs](https://developer.mozilla.org/en-US/docs/Web/API) that TS developers are typically used to. Avoid web-specific APIs in core plugins unless absolutely necessary -- polyfills, or native implementations may be required for the core plugin to function on mobile platforms.
63
+
:::
59
64
</Fragment>
60
65
<Fragmentslot='react'>
61
66
@@ -105,6 +110,8 @@ Lastly React plugins can also act as a core plugin in cases where core functiona
105
110
106
111
</Fragment>
107
112
<Fragmentslot='ios'>
113
+
## iOS Plugins
114
+
108
115
iOS Player Plugins are very similar to core and react plugins in both their composition and use.
109
116
110
117
### NativePlugin
@@ -135,23 +142,7 @@ class EnvironmentPlugin: NativePlugin {
135
142
}
136
143
```
137
144
138
-
#### Asset Registration
139
-
140
-
Likely the most common usecase for plugins is to register assets:
141
-
142
-
```swift
143
-
importPlayerUI
144
-
145
-
classExampleAssetPlugin: NativePlugin {
146
-
let pluginName ="ExampleAssetPlugin"
147
-
148
-
funcapply<P>(player: P) where P:HeadlessPlayer {
149
-
guardlet player = player as? SwiftUIPlayer else { return }
Likely the most common usecase for plugins is to provide UI through assets. More information can be found in the [Custom Assets guide](/assets/custom).
155
146
156
147
### JSBasePlugin
157
148
@@ -211,5 +202,69 @@ class SharedJSPlugin: JSBasePlugin {
211
202
212
203
**Note**: `JSBasePlugin` implementations do not necessarily need to be a `PlayerPlugin`, for example, the [BeaconPlugin](https://github.com/player-ui/player/blob/main/plugins/beacon/ios/Sources/BaseBeaconPlugin.swift#L59) can take plugins in it's constructor, that are not `PlayerPlugin`.
213
204
205
+
</Fragment>
206
+
<Fragmentslot="android">
207
+
## Android Plugins
208
+
209
+
For all types of plugins, the `apply(instance: T)` signature is the standard way of getting the instance of whatever the plugin is applying to. It is best practice that this method remains idempotent, but that doesn't mean that plugins can't maintain state to hold a reference to said `instance`. Different types of plugins are handled at different stages of initialization, but the order of the same types of plugins will be preserved when applying the plugins.
210
+
211
+
### Android Player Plugins
212
+
213
+
Android Player plugins must implement the `AndroidPlayerPlugin` interface, which will give them access to an `AndroidPlayer` instance through the `apply` method. Plugins can use this instance to register UI assets, apply themes, or even access the [Player hooks](#jvm-player-plugins).
214
+
215
+
Likely the most common usecase for plugins is to provide UI through assets. More information can be found in the [Custom Assets guide](/assets/custom).
216
+
217
+
### JVM Player Plugins
218
+
219
+
Player plugins that are only required for the JVM use cases should implement the `PlayerPlugin` interface. This provides a _limited_`Player` wrapper of the core JS Player, which are exposed through the Player `hooks`. A basic example of `PlayerPlugin` to handle errors:
220
+
221
+
```kotlin
222
+
classHandleErrorPlayerPlugin : PlayerPlugin {
223
+
overridefunapply(player:Player) {
224
+
player.hooks.state.tap { state ->
225
+
if (state isErrorState) {
226
+
// handle error
227
+
}
228
+
}
229
+
}
230
+
}
231
+
```
232
+
233
+
For more practical examples, take a look at the [coroutines plugins](/plugins/android/coroutines/) which tie asynchronous Player paradigms to [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) constructs.
234
+
235
+
:::note
236
+
The `AndroidPlayer` also extends the "core" JVM `Player`, which means that Android Player plugins already have access to everything the `PlayerPlugin` provides.
237
+
:::
238
+
239
+
### JS Player Plugins
240
+
241
+
Given the core Player is written in TypeScript incidentally means that most Player plugins will also be written in TypeScript. Loading these on the JVM platform requires some additional setup. At the very least, the plugin will need to be bundled with its dependencies and transpiled according the the target [JS runtime](/platforms/jvm#javascript-runtime).
242
+
243
+
Once you have your bundle, all that is left is implementing the wrapper on the JVM. The plugin wrapper should implement the `JSPluginWrapper`, which includes an `apply(runtime: Runtime)` method to provide the `Runtime` in which to instantiate the JS Player plugin within.
244
+
245
+
For plugins that do not have any constructor arguments, the `JSScriptPluginWrapper` can be used to simplify the overhead of instantiated that plugin. It is an abstract class that implements of the `JSPluginWrapper`. This can be instantiated with the loaded bundle as a string or the location of the bundle on the classpath and will automatically read the bundle and instantiate the plugin within the JS runtime.
`RuntimePlugin`s are supertypes of the `JSPluginWrapper` for JS Player plugin wrappers. At this layer, they can be used to add non-player specific functionality to the JS runtime. For example, the [`BeaconPlugin`](/plugins/multiplatform/beacon/) requires the `setTimeout` global method, which does not exist by default in some runtimes. Thus, the `BeaconPlugin` applies the `SetTimeoutPlugin`, which is a `RuntimePlugin`, before it instantiates the actual JS Player beacon plugin.
262
+
263
+
### Plugin Order of Operations
264
+
265
+
With all these different types of plugins, it may be difficult to understand how they are treated and when each is applied. Each of these plugin types implements the base `Plugin` interface, which provides a bounding layer for any type of plugin. Both the Android and JVM Players accept a collection of `Plugin`s, which may or may not be specific to that Player. This allows non-player plugins, such as the `RuntimePlugin` to be passed into the instantiation of either Player, simplifying the overhead of using such plugins. During the different layers of initialization, the internals will handle each type of plugin that it cares about. Because the internals are expecting certain types of plugins, any direct implementation of the base `Plugin` is certainly an error. Below is a detailed sequence diagram describing the order in which plugins are applied.
266
+
267
+
<ImagedarkModeInvertsrc={pluginsOOO}alt="Plugin Order of Operations" />
0 commit comments