@javascriptcommon/react-native-carplay 2.4.7 → 2.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,633 @@
1
+ # CarPlay with React Native
2
+
3
+ What if you could create CarPlay with React Native. Well, now you can.
4
+
5
+ ![Animated Demo](https://media.giphy.com/media/Ffa4hukA3YMLh6U8fl/giphy.gif)
6
+
7
+ ## Discord Channel
8
+
9
+ Come join us on our discord channel: https://discord.gg/b235pv6QHM
10
+
11
+ ## Support
12
+
13
+ This version of `react-native-carplay` supports iOS 14. If you need support for versions before this please refer to version 1.3.1 of this package.
14
+
15
+ ## CarPlay Entitlement and XCode Project Setup
16
+
17
+ ### Entitlement matrix
18
+
19
+ <table>
20
+ <thead>
21
+ <tr>
22
+ <th></th>
23
+ <th>List</th>
24
+ <th>Grid</th>
25
+ <th title="TabBar">T B</th>
26
+
27
+ <th>Alert</th>
28
+ <th title="Action Sheet">A S</th>
29
+
30
+ <th title="Voice Controller">🎤</th>
31
+ <th title="Now Playing">▶️</th>
32
+
33
+ <th>Map</th>
34
+ <th title="Search">🔎</th>
35
+
36
+ <th title="Point of Interest">POI</th>
37
+ <th title="Information">Info</th>
38
+
39
+ <th title="Contact">📇</th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>
43
+ <tr>
44
+ <td>
45
+ com.apple.developer.carplay-audio
46
+ </td>
47
+ <td>✅</td>
48
+ <td>✅</td>
49
+ <td>✅</td>
50
+
51
+ <td>✅</td>
52
+ <td>❌</td>
53
+
54
+ <td>✅</td>
55
+ <td>✅</td>
56
+
57
+ <td>❌</td>
58
+ <td>❌</td>
59
+
60
+ <td>❌</td>
61
+ <td>❌</td>
62
+
63
+ <td>❌</td>
64
+ </tr>
65
+ <tr>
66
+ <td>com.apple.developer.carplay-communication</td>
67
+ <td>✅</td>
68
+ <td>✅</td>
69
+ <td>✅</td>
70
+
71
+ <td>✅</td>
72
+ <td>✅</td>
73
+
74
+ <td>✅</td>
75
+ <td>❌</td>
76
+
77
+ <td>❌</td>
78
+ <td>❌</td>
79
+
80
+ <td>❌</td>
81
+ <td>✅</td>
82
+
83
+ <td>✅</td>
84
+ </tr>
85
+ <tr>
86
+ <td>com.apple.developer.carplay-charging</td>
87
+ <td>✅</td>
88
+ <td>✅</td>
89
+ <td>✅</td>
90
+
91
+ <td>✅</td>
92
+ <td>✅</td>
93
+
94
+ <td>❌</td>
95
+ <td>❌</td>
96
+
97
+ <td>❌</td>
98
+ <td>❌</td>
99
+
100
+ <td>✅</td>
101
+ <td>✅</td>
102
+
103
+ <td>✅</td>
104
+ </tr>
105
+ <tr>
106
+ <td>com.apple.developer.carplay-maps</td>
107
+ <td>✅</td>
108
+ <td>✅</td>
109
+ <td>✅</td>
110
+
111
+ <td>✅</td>
112
+ <td>✅</td>
113
+
114
+ <td>✅</td>
115
+ <td>❌</td>
116
+
117
+ <td>✅</td>
118
+ <td>✅</td>
119
+
120
+ <td>❌</td>
121
+ <td>✅</td>
122
+
123
+ <td>✅</td>
124
+ </tr>
125
+ <tr>
126
+ <td>com.apple.developer.carplay-parking</td>
127
+ <td>✅</td>
128
+ <td>✅</td>
129
+ <td>✅</td>
130
+
131
+ <td>✅</td>
132
+ <td>✅</td>
133
+
134
+ <td>❌</td>
135
+ <td>❌</td>
136
+
137
+ <td>❌</td>
138
+ <td>❌</td>
139
+
140
+ <td>✅</td>
141
+ <td>✅</td>
142
+
143
+ <td>✅</td>
144
+ </tr>
145
+ <tr>
146
+ <td>com.apple.developer.carplay-quick-ordering</td>
147
+ <td>✅</td>
148
+ <td>✅</td>
149
+ <td>✅</td>
150
+
151
+ <td>✅</td>
152
+ <td>✅</td>
153
+
154
+ <td>❌</td>
155
+ <td>❌</td>
156
+
157
+ <td>❌</td>
158
+ <td>❌</td>
159
+
160
+ <td>✅</td>
161
+ <td>✅</td>
162
+
163
+ <td>✅</td>
164
+ </tr>
165
+ </tbody>
166
+ </table>
167
+
168
+ #### Read this section if you are new to CarPlay!
169
+
170
+ One of the most useful resources for undertanding the requirements, constraints and capabilities of CarPlay apps is the official [App Programming Guidelines](https://developer.apple.com/carplay/documentation/CarPlay-App-Programming-Guide.pdf) from Apple. It's a 50-page document that clearly lays out steps required and you are strongly encouraged to read it if you are new to CarPlay. Further to the above guide, when developing a CarPlay app or if contributing to this package; you'll find the [CarPlay Documentation](https://developer.apple.com/documentation/carplay?language=objc) invaluable.
171
+
172
+ _You can develop CarPlay capabilities with this project without waiting for Apple to send you back an entitlement, through the simulator._
173
+
174
+ If you want to build and run your app on an iPhone or share it with others through the App Store Connect or TestFlight, you will need to request a CarPlay entitlement from Apple first. The process will take anywhere from a few days to weeks - your mileage will vary. This depends on the type of Entitlement you are requesting. If you are part of the MFi program, this may help speed things up too. You then need to add the entitlement to your provisioning profile or signing certificate that you use for signing your app in XCode.
175
+
176
+ You can go to [this Apple CarPlay entitlement request page](https://developer.apple.com/contact/carplay/) to request a CarPlay Entitlement. You need to be logged in with an Apple Developer account.
177
+
178
+ To start a CarPlay simulator in XCode, within the Simulator window, go to the menu option IO, click on External Displays, then select CarPlay.
179
+
180
+ #### NB:
181
+
182
+ Whether you are running through a simulator or building the app for distribution, you need to ensure that the correct entitlement key is added in your `Entitlements.plist` file. If you don't have an Entitlements.plist file, create one in your `iOS/` directory.
183
+
184
+ ## Installing
185
+
186
+ 1. Install the library
187
+
188
+ ```bash
189
+ yarn add react-native-carplay --save
190
+ ```
191
+
192
+ 2. Link using normal or cocoapods method
193
+
194
+ ```bash
195
+ react-native link react-native-carplay
196
+ ```
197
+
198
+ ```ruby
199
+ # in ios/Podfile:
200
+
201
+ pod 'react-native-carplay', path: '../node_modules/react-native-carplay'
202
+ ```
203
+
204
+ 3. Edit your AppDelegate
205
+
206
+ ```objc
207
+ // AppDelegate.h
208
+
209
+ // [step 1] add this line to the top
210
+ #import <CarPlay/CarPlay.h>
211
+
212
+ // [step 2] add the "CPApplicationDelegate" to the end, before ">":
213
+ @interface AppDelegate : UIResponder <UIApplicationDelegate, CPApplicationDelegate>
214
+ ```
215
+
216
+ ```objc
217
+ // AppDelegate.m
218
+
219
+ // [step 1] add this line to the top
220
+ #import <RNCarPlay.h>
221
+
222
+ // ...
223
+
224
+ // [step 2] add the following two methods before @end
225
+
226
+ - (void)application:(UIApplication *)application didConnectCarInterfaceController:(CPInterfaceController *)interfaceController toWindow:(CPWindow *)window {
227
+ [RNCarPlay connectWithInterfaceController:interfaceController window:window];
228
+ }
229
+
230
+ - (void)application:(nonnull UIApplication *)application didDisconnectCarInterfaceController:(nonnull CPInterfaceController *)interfaceController fromWindow:(nonnull CPWindow *)window {
231
+ [RNCarPlay disconnect];
232
+ }
233
+
234
+ @end
235
+ ```
236
+
237
+ ## Basic Usage
238
+
239
+ [See full example](https://github.com/birkir/react-native-carplay/blob/master/example/src/App.tsx)
240
+
241
+ The exported `CarPlay` class gives you the API needed to add / remove templates from the CarPlay view hierarchy.
242
+
243
+ ```jsx
244
+ import { CarPlay, GridTemplate } from 'react-native-carplay';
245
+
246
+ const template = new GridTemplate({
247
+ title: 'Hello, World',
248
+ buttons: [],
249
+ });
250
+
251
+ CarPlay.setRootTemplate(template);
252
+ ```
253
+
254
+ ## Connect / Disconnect
255
+
256
+ When working with CarPlay it is important to detect and respond to the connect / disconnect events. The CarPlay class provides both a `connected` boolean and an on connect / disconnect event you can register a callback to.
257
+
258
+ When you are creating and displaying a template within your existing app screens you may want to ensure CarPlay is connected before calling any carplay apis. This can be done within a `useEffect`.
259
+
260
+ ```jsx
261
+ useEffect(() => {
262
+ function onConnect() {
263
+ // Do things now that carplay is connected
264
+ }
265
+
266
+ function onDisconnect() {
267
+ // Do things now that carplay is disconnected
268
+ }
269
+
270
+ CarPlay.registerOnConnect(onConnect);
271
+ CarPlay.registerOnDisconnect(onDisconnect);
272
+
273
+ return () => {
274
+ CarPlay.unregisterOnConnect(onConnect);
275
+ CarPlay.unregisterOnDisconnect(onDisconnect);
276
+ };
277
+ });
278
+ ```
279
+
280
+ ## CarPlay API
281
+
282
+ ### CarPlay.setRootTemplate
283
+
284
+ Sets the root template of CarPlay.
285
+ This must be called before running any other CarPlay commands. Can be called multiple times.
286
+
287
+ ```tsx
288
+ CarPlay.setRootTemplate(template, /* animated */ false);
289
+ ```
290
+
291
+ ### CarPlay.pushTemplate
292
+
293
+ Pushes a new template to the navigation stack.
294
+ **Note** you cannot push the same template twice.
295
+
296
+ ```tsx
297
+ CarPlay.pushTemplate(template, /* animated */ true);
298
+ ```
299
+
300
+ ### CarPlay.popTemplate
301
+
302
+ Pop currently presented template from the stack.
303
+
304
+ ```tsx
305
+ CarPlay.popTemplate(/* animated */ false);
306
+ ```
307
+
308
+ ### CarPlay.popToTemplate
309
+
310
+ Pop currently presented template from the stack to a specific template. The template must be in the stack.
311
+
312
+ ```tsx
313
+ CarPlay.popToTemplate(template, /* animated */ false);
314
+ ```
315
+
316
+ ### CarPlay.popToRoot
317
+
318
+ Pop the stack to root template.
319
+
320
+ ```tsx
321
+ CarPlay.popToRoot(/* animated */ false);
322
+ ```
323
+
324
+ ## Templates
325
+
326
+ Templates are used to render contents on the CarPlay screen from your app. Details of the templates supported by apple can be found in the [developer guide](https://developer.apple.com/carplay/documentation/CarPlay-App-Programming-Guide.pdf)
327
+
328
+ ### MapTemplate
329
+
330
+ ![Map Template](/.github/images/mapTemplateRoutes.png)
331
+ ![Map Template](/.github/images/mapTemplateNavigation.png)
332
+
333
+ ```jsx
334
+ import { CarPlay } from 'react-native-carplay';
335
+
336
+ const mapTemplate = new MapTemplate({
337
+ component: /* react native view */ MapView,
338
+ onAlertActionPressed(e) {
339
+ console.log(e);
340
+ },
341
+ onStartedTrip({ tripId, routeIndex }) {
342
+ // start your navigation code
343
+ onStartNavigation(routeIndex);
344
+ },
345
+ });
346
+
347
+ CarPlay.setRootTemplate(mapTemplate);
348
+ ```
349
+
350
+ ### ListTemplate
351
+
352
+ ![List Template](/.github/images/listTemplate.png)
353
+
354
+ ```jsx
355
+ import { CarPlay } from 'react-native-carplay';
356
+
357
+ const listTemplate = new ListTemplate({
358
+ sections: [],
359
+ title: 'List Template',
360
+ async onItemSelect({ index }) {
361
+ // use the selected index
362
+ setSelected(index);
363
+ },
364
+ });
365
+
366
+ CarPlay.pushTemplate(listTemplate, true);
367
+ ```
368
+
369
+ ### InformationTemplate
370
+
371
+ ![Information Template](/.github/images/informationTemplate.png)
372
+
373
+ ```jsx
374
+ import { CarPlay } from 'react-native-carplay';
375
+
376
+ const template = new InformationTemplate({
377
+ title: 'Information',
378
+ items: Array.from({ length: 30 }).fill({ title: 'foo', detail: 'bar' }),
379
+ actions: [
380
+ { id: 'u', title: 'Update List' },
381
+ { id: 'r', title: 'Random #:' },
382
+ ],
383
+ onActionButtonPressed(action) {
384
+ console.log('pressed', action);
385
+ if (action.id == 'u') {
386
+ const numOfItems = Math.floor(Math.random() * 6);
387
+ template.updateInformationTemplateItems(
388
+ Array.from({ length: numOfItems }).fill({ title: 'foo', detail: 'bar' }),
389
+ );
390
+ } else if (action.id == 'r') {
391
+ template.updateInformationTemplateActions([
392
+ { id: 'u', title: 'Update List' },
393
+ { id: 'r', title: 'Random #:' + Math.floor(Math.random() * 100) },
394
+ ]);
395
+ }
396
+ },
397
+ });
398
+
399
+ CarPlay.pushTemplate(informationTemplate);
400
+ ```
401
+
402
+ ### GridTemplate
403
+
404
+ ![Grid Template](/.github/images/gridTemplate.png)
405
+
406
+ ```jsx
407
+ import { CarPlay } from 'react-native-carplay';
408
+
409
+ const gridTemplate = new GridTemplate({
410
+ trailingNavigationBarButtons: [],
411
+ buttons: [
412
+ {
413
+ id: 'List',
414
+ titleVariants: ['List'],
415
+ image: listImage,
416
+ },
417
+ {
418
+ id: 'Grid',
419
+ titleVariants: ['Grid'],
420
+ image: gridImage,
421
+ },
422
+ ],
423
+ title: 'Grid Template',
424
+ onButtonPressed({ id }) {
425
+ // id of button pressed
426
+ setSelected(id);
427
+ },
428
+ onBarButtonPressed({ id }) {
429
+ // id of bar button pressed
430
+ setSelected(id);
431
+ },
432
+ });
433
+
434
+ CarPlay.pushTemplate(gridTemplate, true);
435
+ ```
436
+
437
+ ### SearchTemplate
438
+
439
+ ![Search Template](/.github/images/searchTemplate.png)
440
+
441
+ ```jsx
442
+ const searchTemplate = new SearchTemplate({
443
+ async onSearch(query) {
444
+ // use the query to search
445
+ // and return item array
446
+ return performSearch(query);
447
+ },
448
+ async onItemSelect({ index }) {
449
+ // index of the selected item
450
+ setSelected(index);
451
+ },
452
+ onSearchButtonPressed() {
453
+ // on search button pressed, should display
454
+ // list template with results
455
+ navigation.navigate('List');
456
+ },
457
+ });
458
+
459
+ CarPlay.pushTemplate(searchTemplate, true);
460
+ ```
461
+
462
+ ### VoiceTemplate
463
+
464
+ ![Voice Template](/.github/images/voiceTemplate.png)
465
+
466
+ This template is presented via `CarPlay.presentTemplate`. In order to implement voice recognition, take a look at the [`@react-native-voice/voice`](https://github.com/react-native-voice/voice) package.
467
+
468
+ ```jsx
469
+ const voiceControlTemplate = new VoiceControlTemplate({
470
+ // pass the control states
471
+ voiceControlStates: [
472
+ {
473
+ identifier: 'TEST',
474
+ image: require('../images/cat.jpg'),
475
+ repeats: true,
476
+ titleVariants: ['Searching...'],
477
+ },
478
+ ],
479
+ });
480
+
481
+ CarPlay.presentTemplate(voiceControlTemplate, true);
482
+ ```
483
+
484
+ ### AlertTemplate
485
+
486
+ ![Alert Template](/.github/images/alertTemplate.png)
487
+
488
+ This template is presented via `CarPlay.presentTemplate`.
489
+
490
+ ```jsx
491
+ const alertTemplate = new AlertTemplate({
492
+ titleVariants: ['Hello world'],
493
+ actions: [
494
+ {
495
+ id: 'ok',
496
+ title: 'Ok',
497
+ },
498
+ {
499
+ id: 'remove',
500
+ title: 'Remove',
501
+ style: 'destructive',
502
+ },
503
+ ],
504
+ onActionButtonPressed({ id }) {
505
+ // id of the pressed button
506
+ if (id === 'remove') {
507
+ // presentable templates can be
508
+ // dismissed
509
+ CarPlay.dismissTemplate();
510
+ }
511
+ },
512
+ });
513
+
514
+ CarPlay.presentTemplate(alertTemplate);
515
+ ```
516
+
517
+ ### ActionSheetTemplate
518
+
519
+ ![ActionSheet Template](/.github/images/actionSheetTemplate.png)
520
+
521
+ This template is presented via `CarPlay.presentTemplate`.
522
+
523
+ ```jsx
524
+ const actionSheetTemplate = new ActionSheetTemplate({
525
+ title: 'Example',
526
+ message: 'This is an message for you',
527
+ actions: [
528
+ {
529
+ id: 'ok',
530
+ title: 'Ok',
531
+ },
532
+ {
533
+ id: 'remove',
534
+ title: 'Remove',
535
+ style: 'destructive',
536
+ },
537
+ ],
538
+ onActionButtonPressed({ id }) {
539
+ // the id of the button pressed
540
+ },
541
+ });
542
+
543
+ CarPlay.presentTemplate(actionSheetTemplate);
544
+ ```
545
+
546
+ ### TabTemplate
547
+
548
+ ![Tab Template](/.github/images/tabTemplate.png)
549
+
550
+ This template must be set as the root template and cannot be pushed on top of other templates.
551
+
552
+ ```jsx
553
+ const template1 = new ListTemplate({
554
+ sections: [
555
+ {
556
+ header: 'Test 1',
557
+ items: [{ text: 'Hello world 1' }],
558
+ },
559
+ ],
560
+ title: 'AA',
561
+ });
562
+ const template2 = new ListTemplate({
563
+ sections: [
564
+ {
565
+ header: 'Test 2',
566
+ items: [{ text: 'Hello world 3' }],
567
+ },
568
+ ],
569
+ title: 'BB',
570
+ });
571
+
572
+ const tabBarTemplate = new TabBarTemplate({
573
+ templates: [template1, template2],
574
+ onTemplateSelect(e: any) {
575
+ console.log('selected', e);
576
+ },
577
+ });
578
+
579
+ CarPlay.setRootTemplate(tabBarTemplate);
580
+ ```
581
+
582
+ ## Example App
583
+
584
+ A working example app can be found [here](https://github.com/birkir/react-native-carplay/blob/master/example/src/App.tsx).
585
+
586
+ To run it you must first install dependencies and pods.
587
+
588
+ 1. install dependencies and build `react-native-carplay`
589
+
590
+ ```bash
591
+ yarn install
592
+ ```
593
+
594
+ 2. move to example dir and install dependencies
595
+
596
+ ```bash
597
+ cd example
598
+ yarn install
599
+ ```
600
+
601
+ 3. run build dev in root dir, this will copy the output to the examples node modules.
602
+
603
+ ```base
604
+ cd ..
605
+ yarn build:dev
606
+ ```
607
+
608
+ 4. install pods in example app and start the metro bundler
609
+
610
+ ```base
611
+ cd example/ios
612
+ pod install
613
+ yarn start
614
+ ```
615
+
616
+ 5. start xcode and run the project on your simulator or device
617
+
618
+ ## Image Size and Resolution
619
+
620
+ Quirks observed where PNG image resolutions should be specfied with scale factor of 3.0 (i.e. append with `@3x`) with ListTemplate image sizing suggested around 80 x 80 px per [Issue #6](https://github.com/birkir/react-native-carplay/issues/6)
621
+
622
+ ## Not working / In progress
623
+
624
+ ### UI Elements
625
+
626
+ - [ ] Contact Template
627
+ - [ ] Now Playing Template
628
+ - [ ] Point of Interest Template
629
+
630
+ ### Getters
631
+
632
+ - [ ] topTemplate
633
+ - [ ] rootTemplate
package/ios/RNCarPlay.h CHANGED
@@ -2,6 +2,7 @@
2
2
  #import <CarPlay/CarPlay.h>
3
3
  #import <React/RCTBridgeModule.h>
4
4
  #import <React/RCTEventEmitter.h>
5
+ #import <React/RCTImageLoader.h>
5
6
  #import "RCTConvert+RNCarPlay.h"
6
7
  #import "RNCPStore.h"
7
8