First, copy the config plugin from this repo: https://github.com/gaishimo/eas-widget-example
You can reference my PRs there too (which, at the time of writing, aren't merged).
After adding the config plugin (see app.json
) with your dev team ID, as well as a bundle ID, you can edit the widget
folder to edit your code. Then npx expo run:ios
(or npx expo run:android
).
After npx expo run:ios
, open the ios
folder, and open the file that ends in .xcworkspace
in XCode. Make sure you have the latest macOS and XCode versions. If you don't, everything will break.
At the top of XCode, to the right of the play button, click the dropdown (likely to the left of a Simulator or your device) change the target to be widget
.
Next, in the left panel, click into your app and open widget/widget.swift
. Edit the file, click run, and it'll open on the simulator with the widget.
I watched ten minutes of this YouTube video to learn how widgets work: https://www.youtube.com/results?search_query=swiftui+widget
I also used GPT 4 and read the docs from Apple. Pretty simple.
The data fetching part happens in the TimelineProvider
btw.
TLDR: Widgets are like a server-side rendered, static app. All data fetching / image fetching happens in TimelineProvider
, which is like getStaticProps
from Next.js, except that it runs on a schedule determined by Apple. The result gets passed to your entry view as an entry
. You have to create the types for your entry (like TypeScript types, but evaluated at runtime.)
First, we need to set up app groups in app.json
:
{
"ios": {
"bundleIdentifier": "com.myapp.app",
"supportsTablet": true,
"entitlements": {
"com.apple.security.application-groups": [
"group.com.myapp.app.widget"
]
}
}
}
The entitlements should be an array with IDs. The ID here is important. It must start with group.
for iOS. Here, I made it group.${bundleIdentifier}.widget
. The text after group.
is considered your groupName
by Apple.
Next, you'll need a library to call the group functions. You can try this out, hopefully it works: https://t.co/UCgtzSR38g
I'm using this since I only need to target iOS for now: https://github.com/mobiletechvn/react-native-widget-bridge
Should be easy to turn that into an Expo Module if you're feeling creative and want to help out.
import React from "react"
import Constants from "expo-constants"
import { StyleSheet, Text, View } from "react-native"
// @ts-ignore
import Preferences from "react-native-shared-group-preferences"
const appGroupIdentifier = `group.${Constants.expoConfig?.ios?.bundleIdentifier}.widget`
export default function App() {
return (
<View style={styles.container}>
<Text
onPress={async () => {
try {
// apparently android needs to check permissions first
// but i ommitted that
await Preferences.setItem(
"test",
{
username: "test",
},
appGroupIdentifier,
)
} catch (errorCode) {
// errorCode 0 = There is no suite with that name
console.log(errorCode)
}
}}
>
Send data
</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
})
PS — I haven’t succeeded at reading data in UserDefaults in Swift UI sent from RN yet.