new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo & React Native · all subjects

expo-core

512 notes in this subject, read out of this brain and free to use. This is page 2 of 9.

expo-calendar permission functions

Permission functions remain: requestCalendarPermissions(), getCalendarPermissions(), requestRemindersPermissions(), getRemindersPermissions(). The Async suffix is dropped. Hooks useCalendarPermissions and useRemindersPermissions are unchanged.

expo-calendar getSourcesSync synchronous

Get calendar sources using getSourcesSync(), which is now synchronous. This replaces the legacy asynchronous getSourcesAsync(). Fetching a single source by ID has no direct equivalent in the new API.

expo-calendar createEvent returns instance not ID

The new calendar.createEvent() method returns an ExpoCalendarEvent instance instead of a string ID. Usage: const event = await calendar.createEvent({ title: 'Lunch', startDate, endDate });

expo-calendar reminder.delete iOS only

Delete a reminder using await reminder.delete(). This replaces the legacy deleteReminderAsync(reminderId). iOS only.

expo-calendar getDefaultCalendarSync iOS only

Get the default calendar on iOS using getDefaultCalendarSync(), which is now synchronous. This replaces the legacy asynchronous getDefaultCalendarAsync().

expo-calendar reminder.update iOS only

Update a reminder using await reminder.update({ title: 'Buy oat milk' }). This replaces the legacy updateReminderAsync(reminderId, options). iOS only.

expo-calendar presentPicker iOS only

Show a calendar picker on iOS using presentPicker(). It returns an ExpoCalendar instance if the user selects one, or null if the user dismisses the picker. This is new in the API.

expo-calendar calendar.listReminders iOS only

List reminders in a calendar using calendar.listReminders(startDate, endDate, status). This replaces the legacy getRemindersAsync([calendarId], status, startDate, endDate). iOS only.

expo-calendar createReminder returns instance not ID

The new calendar.createReminder() method returns an ExpoCalendarReminder instance instead of a string ID. Usage: const reminder = await calendar.createReminder({ title: 'Buy milk' }). iOS only.

expo-calendar attendee.delete Android only

Delete an attendee using await attendee.delete(). This is an instance method and is only supported on Android.

expo-calendar calendar.delete method

Delete a calendar using the instance method: await calendar.delete(). This replaces the legacy deleteCalendarAsync(calendarId).

expo-calendar attendee.update Android only

Update an attendee using await attendee.update({ name: 'Alexander' }). This is an instance method and is only supported on Android.

expo-calendar event.createAttendee method

Create an attendee using const attendee = await event.createAttendee({ email: 'alex@example.com', name: 'Alex' }). Returns an ExpoCalendarAttendee instance. Role, type, and status parameters are no longer required.

expo-calendar calendar.update method

Update a calendar using the instance method: await calendar.update({ title: 'Renamed' }). This replaces the legacy updateCalendarAsync(calendarId, options).

expo-calendar event.getAttendees method

Get attendees of an event using await event.getAttendees(). This replaces the legacy getAttendeesForEventAsync(eventId).

expo-calendar event.getOccurrenceSync method

Get a recurring event occurrence using event.getOccurrenceSync({ instanceStartDate }). First obtain the event instance via ExpoCalendarEvent.get(eventId).

expo-calendar createCalendar returns instance not ID

The new createCalendar() function returns an ExpoCalendar instance instead of a string ID. Usage: const calendar = await createCalendar({ title: 'My Calendar', color: '#ff0000' });

expo-calendar event.editInCalendar and calendar.addEventWithForm

Edit an event using event.editInCalendar(params). To create a new event with form, use calendar.addEventWithForm({ title, startDate, endDate }). The id field is no longer part of params. Presentation options (startNewActivityTask) are now passed in the same params object.

expo-calendar new API class-based imports

The new API exports class instances: import { ExpoCalendar, ExpoCalendarEvent } from 'expo-calendar'. Import additional classes ExpoCalendarReminder and ExpoCalendarAttendee as needed.

expo-calendar event.openInCalendar parameters

Use event.openInCalendar(params) to open an event in calendar. The id field is no longer part of params (it comes from the event instance). Presentation options (allowsEditing, allowsCalendarPreview, startNewActivityTask) are now passed in the same params object instead of as a separate argument.

expo-calendar event.delete always deletes entire series

The new event.delete() method always deletes the entire recurring event series. The legacy recurringEventOptions parameter (iOS only) that allowed targeting single occurrences or future occurrences is not supported.

expo-calendar event.update always modifies entire series

The new event.update() method always modifies the entire recurring event series. The legacy recurringEventOptions parameter (iOS only) that allowed targeting single occurrences or future occurrences is not supported.

expo-calendar Async suffix removed

The Async suffix is dropped from function names in the new API. The majority of the library is asynchronous by default. Only synchronous functions use a Sync suffix (for example, getDefaultCalendarSync, getSourcesSync, getOccurrenceSync).

expo-calendar legacy API import path

The legacy expo-calendar API is available from 'expo-calendar/legacy'. Users can import the legacy API during migration while updating to the new API from the root 'expo-calendar' import.

Listen for media library changes with new API

Use addListener(event => { ... }) to subscribe to library changes, with subscription.remove() to unsubscribe. Use removeAllListeners() to remove all listeners at once. The listener event shape is unchanged from the legacy API.

New expo-media-library API structure

The new class-based expo-media-library API replaces function-based MediaLibrary.getAssetsAsync() with Asset, Album, and Query classes. Albums and assets are represented as class instances that hold only the ID of the native asset. Asset properties are async getters instead of pre-fetched fields. Query replaces getAssetsAsync with a chainable builder pattern.

Delete assets with new API

For a single asset, call asset.delete(). For multiple assets, call Asset.delete([asset1, asset2]). This replaces the legacy MediaLibrary.deleteAssetsAsync([asset]).

Removed functions in new expo-media-library API

The following legacy functions are removed with no replacement: getMomentsAsync, albumNeedsMigrationAsync, and migrateAlbumIfNeededAsync. These calls can be safely deleted.

Breaking changes in new expo-media-library API

Breaking semantic changes: (1) Asset properties are now async getters (getFilename(), getWidth(), etc.) instead of synchronous fields; use asset.getInfo() for all properties at once. (2) The 'Async' suffix is dropped; the entire library is asynchronous. (3) Query replaces getAssetsAsync options bag with no endCursor/hasNextPage; use .limit() and .offset() for pagination. (4) Operations on albums and assets are methods on instances instead of free functions accepting IDs. (5) saveToLibraryAsync is replaced by Asset.create which returns an Asset instance.

Media library permissions in new API

Permission hooks and functions keep the same names. The only change is that presentPermissionsPickerAsync was renamed to presentPermissionsPicker. requestPermissionsAsync, getPermissionsAsync, and usePermissions are unchanged.

Read asset properties with new API

Asset properties are accessed through async getters: asset.getFilename(), asset.getWidth(), asset.getHeight(), asset.getMediaType(). Use asset.getInfo() to retrieve all properties at once as an AssetInfo object. EXIF data is accessed via asset.getExif().

Delete album with new API

For a single album, call await album.delete(). For multiple albums, call await Album.delete([album1, album2]). This replaces MediaLibrary.deleteAlbumsAsync([album], false).

Query assets with new API

Use the Query class with chainable methods: new Query().eq(AssetField.MEDIA_TYPE, MediaType.IMAGE).limit(20).orderBy({ key: AssetField.CREATION_TIME, ascending: false }).exe(). Query returns an array of Asset instances directly with no assets wrapper or endCursor. Pagination uses .limit() and .offset() methods instead of firstCursor/endCursor.

Remove assets from album with new API

Call await album.removeAssets(assets) to remove assets from an album. This operation is only available on iOS. This replaces MediaLibrary.removeAssetsFromAlbumAsync(assets, album).

Add assets to album with new API

Call await album.add([asset]) to add one or more assets to an album. This replaces MediaLibrary.addAssetsToAlbumAsync([asset], album, false).

Get album title with new API

Use await album.getTitle() to get an album's title. In the legacy API, title was a synchronous property that required fetching the full album object first.

Get all assets in album with new API

Call album.getAssets() on an Album instance to get all assets in that album. This replaces MediaLibrary.getAssetsAsync({ album: album.id }).

Create album with new API

Use Album.create('MyNewAlbum', [asset]) where the second argument is an array of assets to add to the album. This replaces MediaLibrary.createAlbumAsync('MyNewAlbum', asset, false).

Get all albums with new API

Use Album.getAll() to retrieve all albums. This replaces MediaLibrary.getAlbumsAsync().

Get album by name with new API

Use Album.get('MyAlbum') which returns the album if found or null if not found. This replaces MediaLibrary.getAlbumAsync('MyAlbum').

Create asset from file with new API

Use Asset.create(localUri) to save a file to the library and get an Asset instance back. This replaces the legacy MediaLibrary.saveToLibraryAsync(localUri) and MediaLibrary.createAssetAsync(localUri). The function saveToLibraryAsync is not available in the new API.

expo-media-library legacy API location

The legacy function-based expo-media-library API is available from the 'expo-media-library/legacy' import path. New code should import from the root 'expo-media-library' to use the new class-based API.

Hermes improves app startup time and reduces binary size

Hermes improves app startup time by compiling JavaScript into bytecode ahead of time. The binary size of Hermes is smaller than other JavaScript engines such as JavaScriptCore (JSC), and it uses less memory at runtime, which is particularly valuable on lower-end Android devices.

Setting jsEngine in app.json to switch JavaScript engines per platform

Set the "jsEngine" field at the top level in app.json to configure the JavaScript engine. Platform-specific values inside the "ios" or "android" keys will take precedence over the top-level setting. For example, you can set jsEngine to "hermes" at the top level and override it with "jsc" under the "ios" key to use Hermes on Android and JSC on iOS.

Platform API differences between Apple platforms

Platform APIs differ between Apple platforms, most notably in UI frameworks: UIKit is used on iOS and tvOS, while AppKit is used on macOS. Although react-native-macos and expo-modules-core provide aliases and polyfills (e.g., UIView aliased to NSView, UIApplication aliased to NSApplication) for iOS-first libraries, these are usually insufficient for full cross-platform support out of the box.

Expo Modules API platform support

The Expo Modules API provides first-class support for Android and iOS. Support for additional Apple platforms (macOS and tvOS) is available as Out-of-Tree platforms.

Why onStart and onStop are not supported

onStart and onStop Activity lifecycle listeners are not supported because the implementation hooks from ReactActivityDelegate, not MainActivity. ReactActivityDelegate does not have onStart and onStop callbacks.

Interface stability and backwards compatibility

Listener interfaces may change between Expo SDK releases. The backwards compatibility strategy is to add new interfaces and use @Deprecated annotation for interfaces planned for removal. All interfaces use Java 8 interface default methods, so you should only override the methods you need to reduce maintenance costs between SDK updates.

WeakReference prevents memory leaks in lifecycle listeners

When bridging Android lifecycle events to JavaScript, use WeakReference to prevent memory leaks. Create observers with WeakReference to the module instance, and only send events if the WeakReference returns a non-null module.

onBackPressed return value

The onBackPressed callback in ReactActivityLifecycleListener should return a boolean. Return true to prevent the default back button behavior; return false to allow default behavior.

onNewIntent return value

The onNewIntent callback in ReactActivityLifecycleListener should return a boolean. Return true to indicate the intent was handled and prevent default behavior; return false otherwise.

Lifecycle listeners are singleton classes

Lifecycle listeners are singleton classes that exist independently of your Expo module instances. To communicate between a lifecycle listener and your module, you need to observe events from your module and notify the lifecycle listener when events occur.

Deep link handling example with lifecycle listeners

Example showing how to use lifecycle listeners to bridge Android system events to React Native: Create a Package that registers a lifecycle listener, the listener captures deep links from intents in onCreate and onNewIntent, stores initial URL and notifies module observers, the module maintains observers and sends events to JavaScript with bundleOf containing url, scheme, host, and path, then expose via TypeScript module interface and React hook for app usage.

Package interface requirement

Inside your Expo module, create a concrete class that implements the Package interface. For most cases, you only need to implement the createReactActivityLifecycleListeners or createApplicationLifecycleListeners methods.

Application lifecycle callbacks supported

The following Application lifecycle callbacks are currently supported: onCreate, onConfigurationChanged.

Activity lifecycle callbacks supported

The following Activity lifecycle callbacks are currently supported: onCreate, onResume, onPause, onDestroy, onNewIntent, onBackPressed.

Create an ExpoAppDelegateSubscriber

Create a new public Swift class that extends ExpoAppDelegateSubscriber from ExpoModulesCore. Add the class name to the apple.appDelegateSubscribers array in the module config. Run pod install, and the subscriber will be generated in the ExpoModulesProvider.swift file within the application project.

ExpoAppDelegate and AppDelegate subscribers overview

To respond to iOS system events relevant to an app, such as inbound links and notifications, the app AppDelegate must inherit from ExpoAppDelegate. The React Native module API does not provide a mechanism to hook into AppDelegate methods, so the Expo Modules API provides a mechanism for libraries to subscribe to AppDelegate function calls. ExpoAppDelegate implements most functions from UIApplicationDelegate protocol and forwards their calls to all subscribers.

Objective-C not supported for AppDelegate subscribers

Objective-C classes are not supported for AppDelegate subscribers. Only Swift classes that extend ExpoAppDelegateSubscriber can be used.

Unsupported AppDelegate functions

App delegate functions that may cause side effects when provided are not supported yet. For example, application(_:viewControllerWithRestorationIdentifierPath:coder:) is not supported.

Give your agent this brain