Skip to content

Commit 38f2068

Browse files
authored
add activitySelectionMetadata and set functions (#45)
* add activitySelectionMetadata and set functions * going back to npm for example * Solid first iteration on set functionality * Lots of updates to support multiple blocks with separate shields * switch to monorepo structure * working directory for pr publish * fix absolute paths * fix pkg-pr-new publish path * try npx * try using wildcard * break out tests * try not using setup-node * always use latest pkg-pr-new * try removing gitignore from apps/example * remove husky from packages/react-native-device-activity * Add set tests * add set tests * makre sure xcode picks up swiftlint config * coderabbit nitpicks * remove unnessecary comments * coderabbit nitpicks * Stabilizing * add more tests * fix empty webDomainCategories check * Going back to familyActivitySelectionId for some things * better naming and add resetBlocks everywhere * add resetblocks to device-activity action types * change name to whitelistCurrentApp * add shieldactions for unblocking selections * typo + docs * remove unnessecary tries * typo * add refreshManagedSettingsStore and clearAllManagedSettingsStoreSettings * fix: isSubset was comparing with wrong set * add onlyFamilySelectionIdsContainingMonitoredActivityNames for getPossibleFamilyActivitySelectionIds * add whitelistPossibleFamilyActivitySelection and whitelistAllPossibleFamilyActivitySelections to shield action * add sortByGranularity to getPossibleFamilyActivitySelectionIds * add setup-bun * uppercase NPM_TOKEN * add debug npm token * add env to debug token * rename to NPM_AUTH_TOKEN * add working-directory * trying something else * use other approach * fix git url * add npm version * remove -m * hackaround * maybe git commit is not needed * fix working-directory * move bun install * revert * add working directory, remove all mumbo-jumbo * add nail version script * trying a different approach * remove potential newline * add --no-verify * make sure we get a clean version * bump version * change push logic * add more debugging * test * add git add and commit * update again * git commit —no-verify * chore: update version * add test for getActivitySelectionPrefixedConfigFromUserDefaults * upgrade rn * tests * more tests, add missing categoryTokens, update types * fix types * chore: update version * fix tests by ignoring test activityNames * swiftlint * add timeouts to workflows * starts bundler before running native tests * bump xcode-elect to 16.2 * copying swiftlint config to make sure it’s available, also remove it from build process * Add some more to the readme * stricter peer deps, clean up names and comments * prettier * skip rn setup for native tests * update lockfiles * specify swift version once * try swift_version 5.8 * print out swift version, remove trailing commas * adjust platforms
1 parent 68a99bd commit 38f2068

File tree

170 files changed

+6583
-6346
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+6583
-6346
lines changed

.github/workflows/npm-publish.yml

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,43 @@ jobs:
4545

4646
- uses: actions/setup-node@v4
4747

48+
- uses: oven-sh/setup-bun@v2
49+
with:
50+
bun-version: latest
51+
4852
- run: git config user.name "$(git log -n 1 --pretty=format:%an)"
4953
- run: git config user.email "$(git log -n 1 --pretty=format:%ae)"
50-
- run: echo "versionTag=`npm version ${{ github.event.inputs.release_type }} -m "%s ${{ github.event.inputs.description }}"`" >> $GITHUB_ENV
5154

5255
- run: bun install
5356

57+
- run: bun run nail-workspace-dependency-versions
58+
59+
- run: npm version ${{ github.event.inputs.release_type }}
60+
working-directory: packages/react-native-device-activity
61+
62+
- run: |
63+
VERSION=$(node -p "require('./package.json').version")
64+
echo "versionTag=v${VERSION}" >> $GITHUB_ENV
65+
working-directory: packages/react-native-device-activity
66+
67+
- name: Setup NPM Authentication
68+
run: |
69+
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc
70+
71+
- run: git add packages/react-native-device-activity/package.json
72+
73+
- run: |
74+
git commit -m 'chore: update version' --no-verify
75+
5476
- run: npm publish
55-
env:
56-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
77+
working-directory: packages/react-native-device-activity
5778

58-
- run: git push --follow-tags
79+
- run: |
80+
git status
81+
git branch -a
82+
git log -1
83+
git push origin HEAD --no-verify
84+
git push origin --tags --no-verify
5985
env:
6086
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
6187

.github/workflows/pr.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ jobs:
1414
with:
1515
bun-version: latest
1616

17-
- uses: actions/setup-node@v4
18-
with:
19-
registry-url: https://registry.npmjs.org/
20-
scope: '@kingstinct'
21-
2217
- run: bun install
2318

24-
- run: bun run prepublishOnly
19+
- name: Build package
20+
run: |
21+
cd packages/react-native-device-activity
22+
bun run build
23+
bun run prepublishOnly
2524
26-
- run: npx pkg-pr-new publish
25+
- name: Publish preview
26+
run: bunx pkg-pr-new@latest publish './packages/*'

.github/workflows/test.yml

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
lint:
1111
name: eslint
1212
runs-on: ubuntu-latest
13+
timeout-minutes: 10
1314
steps:
1415
- uses: actions/checkout@v4
1516

@@ -24,6 +25,7 @@ jobs:
2425
typecheck:
2526
name: Typecheck
2627
runs-on: ubuntu-latest
28+
timeout-minutes: 10
2729
steps:
2830
- uses: actions/checkout@v4
2931

@@ -38,6 +40,7 @@ jobs:
3840
typecheck-example:
3941
name: Typecheck (example project)
4042
runs-on: ubuntu-latest
43+
timeout-minutes: 10
4144
steps:
4245
- uses: actions/checkout@v4
4346

@@ -47,15 +50,13 @@ jobs:
4750

4851
- run: bun install
4952

50-
- run: bun install
51-
working-directory: example
52-
5353
- run: bun typecheck
54-
working-directory: example
54+
working-directory: apps/example
5555

5656
bundle-example:
5757
name: Expo Bundle (example project)
5858
runs-on: ubuntu-latest
59+
timeout-minutes: 10
5960
steps:
6061
- uses: actions/checkout@v4
6162

@@ -65,14 +66,12 @@ jobs:
6566

6667
- run: bun install
6768

68-
- run: bun install
69-
working-directory: example
70-
7169
- run: bunx expo export
72-
working-directory: example
70+
working-directory: apps/example
7371

7472
config-example:
7573
name: Expo Config (example project)
74+
timeout-minutes: 10
7675
runs-on: ubuntu-latest
7776
steps:
7877
- uses: actions/checkout@v4
@@ -83,15 +82,13 @@ jobs:
8382

8483
- run: bun install
8584

86-
- run: bun install
87-
working-directory: example
88-
8985
- run: bunx expo config
90-
working-directory: example
86+
working-directory: apps/example
9187

9288
prebuild-example:
9389
name: Expo Prebuild (example project)
9490
runs-on: ubuntu-latest
91+
timeout-minutes: 10
9592
steps:
9693
- uses: actions/checkout@v4
9794

@@ -101,15 +98,13 @@ jobs:
10198

10299
- run: bun install
103100

104-
- run: bun install
105-
working-directory: example
106-
107101
- run: bunx expo prebuild
108-
working-directory: example
102+
working-directory: apps/example
109103

110104
swift-lint:
111105
name: SwiftLint (example project)
112106
runs-on: macos-latest
107+
timeout-minutes: 10
113108
steps:
114109
- uses: actions/checkout@v4
115110

@@ -125,18 +120,16 @@ jobs:
125120
${{ runner.os }}-pods-
126121
127122
- run: bun install
128-
129-
- run: bun install
130-
working-directory: example
131-
123+
132124
- run: pod install
133-
working-directory: example/ios
125+
working-directory: apps/example/ios
134126

135-
- run: example/ios/Pods/SwiftLint/swiftlint lint
127+
- run: apps/example/ios/Pods/SwiftLint/swiftlint lint
136128

137129
swift-test:
138130
name: Swift Test (example project)
139131
runs-on: macos-latest
132+
timeout-minutes: 25
140133
steps:
141134
- uses: actions/checkout@v4
142135

@@ -150,6 +143,10 @@ jobs:
150143

151144
- run: bun install
152145

146+
- name: Start bundler
147+
run: bun start &
148+
working-directory: apps/example
149+
153150
- uses: actions/cache@v4
154151
with:
155152
path: example/ios/Pods
@@ -169,16 +166,18 @@ jobs:
169166
restore-keys: xcode-cache-deriveddata-${{ github.workflow }}-
170167

171168
- name: Set up Xcode version
172-
run: sudo xcode-select -s /Applications/Xcode_16.1.app/Contents/Developer
173-
174-
- run: bun install
175-
working-directory: example
176-
169+
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
170+
171+
- name: Check Swift version
172+
run: swift --version
173+
177174
- run: pod install
178-
working-directory: example/ios
175+
working-directory: apps/example/ios
176+
177+
- run: cp .swiftlint.yml apps/example/ios
179178

180179
- run: xcodebuild test -workspace reactnativedeviceactivityexample.xcworkspace -scheme Tests -allowProvisioningUpdates -destination "platform=iOS Simulator,OS=latest,name=iPhone 16"
181-
working-directory: example/ios
180+
working-directory: apps/example/ios
182181

183182

184183

.gitignore

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,34 @@ android/keystores/debug.keystore
6565
!.yarn/plugins
6666
!.yarn/releases
6767
!.yarn/sdks
68-
!.yarn/versions
68+
!.yarn/versions
69+
70+
# Expo
71+
.expo/
72+
dist/
73+
web-build/
74+
75+
# Native
76+
*.orig.*
77+
*.jks
78+
*.p8
79+
*.p12
80+
*.key
81+
*.mobileprovision
82+
83+
# Metro
84+
.metro-health-check*
85+
86+
# debug
87+
npm-debug.*
88+
yarn-debug.*
89+
yarn-error.*
90+
91+
# macOS
92+
*.pem
93+
94+
# local env files
95+
.env*.local
96+
97+
# typescript
98+
*.tsbuildinfo

.husky/pre-commit

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
swift format -i -p -r ./targets ./ios ./example/ios
2-
example/ios/Pods/SwiftLint/swiftlint lint --fix
1+
swift format -i -p -r ./packages/react-native-device-activity/ios ./packages/react-native-device-activity/targets ./apps/example/ios
2+
apps/example/ios/Pods/SwiftLint/swiftlint lint --fix

.swiftlint.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ disabled_rules:
99
- cyclomatic_complexity
1010
- file_length
1111
- function_parameter_count
12+
- inclusive_language
1213

1314
included:
14-
- ios
15-
- example/ios
16-
- targets
15+
- packages/react-native-device-activity/ios
16+
- apps/example/ios
17+
- packages/react-native-device-activity/targets

.vscode/extensions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"recommendations": [
33
"vknabel.vscode-swiftlint",
4-
"sswg.swift-lang"
4+
"swiftlang.swift-vscode"
55
],
66
}

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"cSpell.words": [
33
"Triggerable"
44
],
5-
"swiftlint.path": "example/ios/Pods/SwiftLint/swiftlint"
5+
"swiftlint.path": "apps/example/ios/Pods/SwiftLint/swiftlint",
6+
"swift.SDK": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.2.sdk"
67
}

README.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ The package requires native code, which includes a custom app target. Currently
8888
"expo-build-properties",
8989
{
9090
"ios": {
91-
"deploymentTarget": "15.0"
91+
"deploymentTarget": "15.1"
9292
},
9393
},
9494
],
@@ -110,6 +110,18 @@ For Expo to be able to automatically handle provisioning you need to specify ext
110110

111111
You can potentially modify the targets manually, although you risk the library and your app code diverging. If you want to disable the automatic copying of the targets, you can set `copyToTargetFolder` to `false` in the plugin configuration [as seen here](https://github.com/Intentional-Digital/react-native-device-activity/blob/main/example/app.json#L53).
112112

113+
## Some notes
114+
- It's not possible to 100% know which familyActivitySelection an event being handled is triggered for in the context of the Shield UI/actions. We try to make a best guess here - prioritizing apps/websites in an activitySelection over categories, and smaller activitySelections over larger ones (i.e. "Instagram" over "Instagram + Facebook" over "Social Media Apps"). This means that if you display a shield specific for the Instagram selection that will take precedence over the less specific shields.
115+
- When determining which familyActivitySelectionId that should be used it will only look for familyActivitySelectionIds that are contained in any of the currently monitored activity names (i.e. if familyActivitySelectionId is "social-media-apps" it will only trigger if there is an activity name that contains "social-media-apps"). This might be a limitation for some implementations, it would probably be nice to make this configurable.
116+
117+
## Data model
118+
Almost all the functionality is built around persisting configuration as well as event history to UserDefaults.
119+
120+
- familyActivitySelectionId mapping. This makes it possible for us to tie a familyActivitySelection token to an id that we can reuse and refer to at a later stage.
121+
- Triggers. This includes configuring shield UI/actions as well as sending web requests or notifications from the Swift background side, in the context of the device activity monitor process. Prefixed like actions_for_${goalId} in userDefaults. This is how we do blocking of apps, updates to shield UI/actions etc.
122+
- Event history. Contains information of which events have been triggered and when. Prefixed like events_${goalId} in userDefaults. This can be useful for tracking time spent.
123+
- ShieldIds. To reduce the storage strain on userDefaults shields are referenced with shieldIds.
124+
113125
# Installation in bare React Native projects
114126

115127
For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
@@ -130,7 +142,7 @@ As early as possible you want to [request approval from Apple](https://developer
130142

131143
Note that until you have approval for all bundleIdentifiers you want to use, you are stuck with local development builds in XCode. I.e. you can't even build an Expo Dev Client.
132144

133-
For every base bundleIdentifier you need approval for 4 bundleIdentifiers:
145+
For every base bundleIdentifier you need approval for 4 bundleIdentifiers (if you want to use all the native extensions that is, you can potentially just use the Shield-related ones if you have no need to listen to the events, or similarly just use the ActivityMonitor if you do not need control over the Shield UI):
134146

135147
- com.your-bundleIdentifier
136148
- com.your-bundleIdentifier.ActivityMonitor
@@ -141,12 +153,33 @@ Once you've gotten approval you need to manually add the "Family Controls (Distr
141153

142154
⚠️ If you don't do all the above you will run in to a lot of strange provisioning errors.
143155

144-
## Limitations and weird/notable things
145-
146-
- The DeviceActivitySelectionView is prone to crashes, which is outside of our control. The best we can do is provide fallback views that allows the user to know what's happening and reload the view.
147-
- If you've asked about the authorization status once and the user after that revokes it outside the app, the native APIs won't reflect this until the app is restarted.
148-
- requestAuthorization() can be called multiple times, even when the user has already denied permission.
149-
150156
# Contributing
151157

152158
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).
159+
160+
# Weird behaviors ⚠️
161+
162+
- Authorization changes outside app not captured
163+
When we've asked whether the user has authorized us to use screen time, and the state is changed outside the app, the native API doesn't update until the app restarts, i.e. this flow:
164+
1. Ask for current permission
165+
2. Change permission outside the app
166+
3. Ask for current permission again will return same as (1)
167+
4. **Workaround: restart the app**
168+
169+
- We can both request and revoke permissions as we like, and how many times we like, even when the user has denied permissions. This is very unlike most authorization flows on iOS.
170+
171+
- When calling `getAuthorizationStatus` it can sometimes return `notDetermined` even though the user has already made a choice, this comes with a delay. Workaround: keep polling the status for a while (`pollAuthorizationStatus` is a convenience function for this).
172+
173+
- The DeviceActivitySelectionView is prone to crashes, which is outside of our control. The best we can do is provide fallback views that allows the user to know what's happening and reload the view.
174+
175+
# Troubleshooting 📱
176+
The Screen Time APIs are known to be very finnicky. Here are some things you can try to troubleshoot events not being reported:
177+
178+
- Disable Low Power Mode (mentioned by Apple Community Specialist [here](https://discussions.apple.com/thread/254808070)) 🪫
179+
- Turn off/turn on app & website activity
180+
- Disable/reenable sync between devices for screen time
181+
- Restart device
182+
- Make sure device is not low on storage (mentioned by Apple Community Specialist [here](https://discussions.apple.com/thread/254808070)) 💾
183+
- Upgrade iOS version
184+
- Content & Privacy Restrictions: If any restrictions are enabled under Screen Time’s Content & Privacy Restrictions, ensure none are blocking your app.
185+
- Reset all device settings

0 commit comments

Comments
 (0)