@bacons/apple-targets 0.1.5 → 0.1.7
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 +79 -0
- package/app.plugin.js +1 -1
- package/build/ExtensionStorage.d.ts +6 -0
- package/build/ExtensionStorage.js +48 -0
- package/build/{index.js → config-plugin.js} +2 -1
- package/build/withXcodeChanges.js +75 -105
- package/expo-module.config.json +6 -0
- package/ios/ExtensionStorage.podspec +21 -0
- package/ios/ExtensionStorageModule.swift +53 -0
- package/package.json +6 -3
- /package/build/{index.d.ts → config-plugin.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -312,3 +312,82 @@ Some workarounds:
|
|
|
312
312
|
|
|
313
313
|
- Prebuild without React Native: `npx expo prebuild --template node_modules/@bacons/apple-targets/prebuild-blank.tgz --clean`
|
|
314
314
|
- If the widget doesn't show on the home screen when building the app, use iOS 18. You can long press the app icon and select the widget display options to transform the app icon into the widget.
|
|
315
|
+
|
|
316
|
+
## Sharing data between targets
|
|
317
|
+
|
|
318
|
+
To share values between the app and the target, you must use App Groups and NSUserDefaults. I've added a native module to make the React Native API a bit easier.
|
|
319
|
+
|
|
320
|
+
### Configuring App Groups
|
|
321
|
+
|
|
322
|
+
Start by defining an App Group, a good default is `group.<bundle identifier>`. App Groups can be used across apps so you may want something more generic or less generic if you plan on having multiple extensions.
|
|
323
|
+
|
|
324
|
+
First, define your main App Group entitlement in your `app.json`:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"expo": {
|
|
329
|
+
"ios": {
|
|
330
|
+
"entitlements": {
|
|
331
|
+
"com.apple.security.application-groups": ["group.bacon.data"]
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
"plugins": ["@bacons/apple-targets"]
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Second, define the same App Group in your target's `expo-target.config.js`:
|
|
340
|
+
|
|
341
|
+
```js
|
|
342
|
+
/** @type {import('@bacons/apple-targets').ConfigFunction} */
|
|
343
|
+
module.exports = (config) => ({
|
|
344
|
+
type: "widget",
|
|
345
|
+
entitlements: {
|
|
346
|
+
// Use the same app groups:
|
|
347
|
+
"com.apple.security.application-groups":
|
|
348
|
+
config.ios.entitlements["com.apple.security.application-groups"],
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Now you can prebuild to generate the entitlements. You may need to create an EAS Build or open Xcode to sync the entitlements.
|
|
354
|
+
|
|
355
|
+
### Setting shared data
|
|
356
|
+
|
|
357
|
+
To define shared data, we'll use a native module (`ExtensionStorage`) that interacts with `NSUserDefaults`.
|
|
358
|
+
|
|
359
|
+
Somewhere in your Expo app, you can set a value:
|
|
360
|
+
|
|
361
|
+
```js
|
|
362
|
+
import { ExtensionStorage } from "@bacons/apple-targets";
|
|
363
|
+
|
|
364
|
+
// Create a storage object with the App Group.
|
|
365
|
+
const storage = new ExtensionStorage(
|
|
366
|
+
// Your app group identifier. Should match the values in the app.json and expo-target.config.json.
|
|
367
|
+
"group.bacon.data"
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
// Then you can set data:
|
|
371
|
+
storage.set("myKey", "myValue");
|
|
372
|
+
|
|
373
|
+
// Finally, you can reload the widget:
|
|
374
|
+
ExtensionStorage.reloadWidget();
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`ExtensionStorage` has the following API:
|
|
378
|
+
|
|
379
|
+
- `set(key: string, value: string | number | Record<string, string | number> | Array<Record<string, string | number>> | undefined): void` - Sets a value in the shared storage for a given key. Setting `undefined` will remove the key.
|
|
380
|
+
- `ExtensionStorage.reloadWidget(name?: string): void` - A static method for reloading the widget. Behind the scenes, this calls `WidgetCenter.shared.reloadAllTimelines()`. If given a name, it will reload a specific widget using `WidgetCenter.shared.reloadTimelines(ofKind: timeline)`.
|
|
381
|
+
|
|
382
|
+
### Accessing shared data
|
|
383
|
+
|
|
384
|
+
Assuming this is done using Swift code, you'll access data using `NSUserDefaults` directly. Here's an example of how you might access the data in a widget:
|
|
385
|
+
|
|
386
|
+
```swift
|
|
387
|
+
let defaults = UserDefaults(suiteName:
|
|
388
|
+
// Use the App Group from earlier.
|
|
389
|
+
"group.bacon.data"
|
|
390
|
+
)
|
|
391
|
+
// Access the value you set:
|
|
392
|
+
let index = defaults?.string(forKey: "myKey")
|
|
393
|
+
```
|
package/app.plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = require(
|
|
1
|
+
module.exports = require("./build/config-plugin");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare class ExtensionStorage {
|
|
2
|
+
private readonly appGroup;
|
|
3
|
+
static reloadWidget(name?: string): void;
|
|
4
|
+
constructor(appGroup: string);
|
|
5
|
+
set(key: string, value?: string | number | Record<string, string | number> | Array<Record<string, string | number>>): void;
|
|
6
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ExtensionStorage = void 0;
|
|
5
|
+
// @ts-expect-error
|
|
6
|
+
const ExtensionStorageModule = (_a = expo === null || expo === void 0 ? void 0 : expo.modules) === null || _a === void 0 ? void 0 : _a.ExtensionStorage;
|
|
7
|
+
const nativeModule = ExtensionStorageModule !== null && ExtensionStorageModule !== void 0 ? ExtensionStorageModule : {
|
|
8
|
+
setInt() { },
|
|
9
|
+
setString() { },
|
|
10
|
+
reloadWidget() { },
|
|
11
|
+
setObject() { },
|
|
12
|
+
remove() { },
|
|
13
|
+
setArray() { },
|
|
14
|
+
};
|
|
15
|
+
const originalSetObject = nativeModule.setObject;
|
|
16
|
+
// Sweet API doesn't support doing this natively.
|
|
17
|
+
nativeModule.setObject = (key, value, suite) => {
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
return nativeModule.setArray(key, value, suite);
|
|
20
|
+
}
|
|
21
|
+
return originalSetObject(key, value, suite);
|
|
22
|
+
};
|
|
23
|
+
class ExtensionStorage {
|
|
24
|
+
static reloadWidget(name) {
|
|
25
|
+
nativeModule.reloadWidget(name);
|
|
26
|
+
}
|
|
27
|
+
constructor(appGroup) {
|
|
28
|
+
this.appGroup = appGroup;
|
|
29
|
+
}
|
|
30
|
+
set(key, value) {
|
|
31
|
+
if (typeof value === "number") {
|
|
32
|
+
nativeModule.setInt(key, value, this.appGroup);
|
|
33
|
+
}
|
|
34
|
+
else if (Array.isArray(value)) {
|
|
35
|
+
nativeModule.setArray(key, value, this.appGroup);
|
|
36
|
+
}
|
|
37
|
+
else if (typeof value === "string") {
|
|
38
|
+
nativeModule.setString(key, value, this.appGroup);
|
|
39
|
+
}
|
|
40
|
+
else if (value == null) {
|
|
41
|
+
nativeModule.remove(key, this.appGroup);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
nativeModule.setObject(key, value, this.appGroup);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.ExtensionStorage = ExtensionStorage;
|
|
@@ -42,7 +42,8 @@ const withTargetsDir = (config, _props) => {
|
|
|
42
42
|
});
|
|
43
43
|
});
|
|
44
44
|
(0, withPodTargetExtension_1.withPodTargetExtension)(config);
|
|
45
|
-
|
|
45
|
+
(0, withXcparse_1.withXcodeProjectBetaBaseMod)(config);
|
|
46
|
+
return config;
|
|
46
47
|
};
|
|
47
48
|
exports.withTargetsDir = withTargetsDir;
|
|
48
49
|
module.exports = exports.withTargetsDir;
|
|
@@ -648,8 +648,7 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
648
648
|
}
|
|
649
649
|
const targets = getExtensionTargets();
|
|
650
650
|
const productName = props.name;
|
|
651
|
-
|
|
652
|
-
const targetToUpdate = (_a = targets.find((target) => target.props.productName === productName)) !== null && _a !== void 0 ? _a : targets[0];
|
|
651
|
+
let targetToUpdate = (_a = targets.find((target) => target.props.productName === productName)) !== null && _a !== void 0 ? _a : targets[0];
|
|
653
652
|
if (targetToUpdate) {
|
|
654
653
|
console.log(`Target "${targetToUpdate.props.productName}" already exists, updating instead of creating a new one`);
|
|
655
654
|
}
|
|
@@ -661,9 +660,14 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
661
660
|
function applyDevelopmentTeamIdToTargets() {
|
|
662
661
|
var _a, _b;
|
|
663
662
|
var _c, _d, _e;
|
|
663
|
+
// Set to the provided value or any value.
|
|
664
|
+
const devTeamId = developmentTeamId ||
|
|
665
|
+
project.rootObject.props.targets
|
|
666
|
+
.map((target) => target.getDefaultBuildSetting("DEVELOPMENT_TEAM"))
|
|
667
|
+
.find(Boolean);
|
|
664
668
|
project.rootObject.props.targets.forEach((target) => {
|
|
665
|
-
if (
|
|
666
|
-
target.setBuildSetting("DEVELOPMENT_TEAM",
|
|
669
|
+
if (devTeamId) {
|
|
670
|
+
target.setBuildSetting("DEVELOPMENT_TEAM", devTeamId);
|
|
667
671
|
}
|
|
668
672
|
else {
|
|
669
673
|
target.removeBuildSetting("DEVELOPMENT_TEAM");
|
|
@@ -675,7 +679,7 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
675
679
|
(_b = (_d = project.rootObject.props.attributes.TargetAttributes)[_e = target.uuid]) !== null && _b !== void 0 ? _b : (_d[_e] = {
|
|
676
680
|
CreatedOnToolsVersion: "14.3",
|
|
677
681
|
ProvisioningStyle: "Automatic",
|
|
678
|
-
DevelopmentTeam:
|
|
682
|
+
DevelopmentTeam: devTeamId,
|
|
679
683
|
});
|
|
680
684
|
}
|
|
681
685
|
}
|
|
@@ -779,95 +783,48 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
779
783
|
// Create new build phases
|
|
780
784
|
targetToUpdate.props.buildConfigurationList =
|
|
781
785
|
createConfigurationListForType(project, props);
|
|
782
|
-
configureTargetWithEntitlements(targetToUpdate);
|
|
783
|
-
configureTargetWithPreview(targetToUpdate);
|
|
784
|
-
configureTargetWithKnownSettings(targetToUpdate);
|
|
785
|
-
configureJsExport(targetToUpdate);
|
|
786
|
-
applyDevelopmentTeamIdToTargets();
|
|
787
|
-
syncMarketingVersions();
|
|
788
|
-
return project;
|
|
789
|
-
}
|
|
790
|
-
const productType = (0, target_1.productTypeForType)(props.type);
|
|
791
|
-
const isExtension = productType === "com.apple.product-type.app-extension";
|
|
792
|
-
const isExtensionKit = productType === "com.apple.product-type.extensionkit-extension";
|
|
793
|
-
const appExtensionBuildFile = xcode_1.PBXBuildFile.create(project, {
|
|
794
|
-
fileRef: xcode_1.PBXFileReference.create(project, {
|
|
795
|
-
explicitFileType: isExtensionKit
|
|
796
|
-
? "wrapper.extensionkit-extension"
|
|
797
|
-
: "wrapper.app-extension",
|
|
798
|
-
includeInIndex: 0,
|
|
799
|
-
path: productName + (isExtension ? ".appex" : ".app"),
|
|
800
|
-
sourceTree: "BUILT_PRODUCTS_DIR",
|
|
801
|
-
}),
|
|
802
|
-
settings: {
|
|
803
|
-
ATTRIBUTES: ["RemoveHeadersOnCopy"],
|
|
804
|
-
},
|
|
805
|
-
});
|
|
806
|
-
project.rootObject.ensureProductGroup().props.children.push(
|
|
807
|
-
// @ts-expect-error
|
|
808
|
-
appExtensionBuildFile.props.fileRef);
|
|
809
|
-
const extensionTarget = project.rootObject.createNativeTarget({
|
|
810
|
-
buildConfigurationList: createConfigurationListForType(project, props),
|
|
811
|
-
name: productName,
|
|
812
|
-
productName,
|
|
813
|
-
// @ts-expect-error
|
|
814
|
-
productReference: appExtensionBuildFile.props.fileRef /* alphaExtension.appex */,
|
|
815
|
-
productType: productType,
|
|
816
|
-
});
|
|
817
|
-
configureTargetWithKnownSettings(extensionTarget);
|
|
818
|
-
configureTargetWithEntitlements(extensionTarget);
|
|
819
|
-
configureTargetWithPreview(extensionTarget);
|
|
820
|
-
extensionTarget.ensureFrameworks(props.frameworks);
|
|
821
|
-
extensionTarget.getSourcesBuildPhase();
|
|
822
|
-
extensionTarget.getResourcesBuildPhase();
|
|
823
|
-
configureJsExport(extensionTarget);
|
|
824
|
-
// Add the target dependency to the main app, should be only one.
|
|
825
|
-
mainAppTarget.props.dependencies.push(xcode_1.PBXTargetDependency.create(project, {
|
|
826
|
-
target: extensionTarget,
|
|
827
|
-
targetProxy: xcode_1.PBXContainerItemProxy.create(project, {
|
|
828
|
-
containerPortal: project.rootObject,
|
|
829
|
-
proxyType: 1,
|
|
830
|
-
remoteGlobalIDString: extensionTarget.uuid,
|
|
831
|
-
remoteInfo: productName,
|
|
832
|
-
}),
|
|
833
|
-
}));
|
|
834
|
-
const WELL_KNOWN_COPY_EXTENSIONS_NAME = (() => {
|
|
835
|
-
switch (props.type) {
|
|
836
|
-
case "clip":
|
|
837
|
-
return "Embed App Clips";
|
|
838
|
-
case "watch":
|
|
839
|
-
return "Embed Watch Content";
|
|
840
|
-
case "app-intent":
|
|
841
|
-
return "Embed ExtensionKit Extensions";
|
|
842
|
-
default:
|
|
843
|
-
return "Embed Foundation Extensions";
|
|
844
|
-
}
|
|
845
|
-
})();
|
|
846
|
-
// Could exist from a Share Extension
|
|
847
|
-
const copyFilesBuildPhase = mainAppTarget.props.buildPhases.find((phase) => {
|
|
848
|
-
if (xcode_1.PBXCopyFilesBuildPhase.is(phase)) {
|
|
849
|
-
// TODO: maybe there's a safer way to do this? The name is not a good identifier.
|
|
850
|
-
return phase.props.name === WELL_KNOWN_COPY_EXTENSIONS_NAME;
|
|
851
|
-
}
|
|
852
|
-
});
|
|
853
|
-
if (copyFilesBuildPhase) {
|
|
854
|
-
// Assume that this is the first run if there is no matching target that we identified from a previous run.
|
|
855
|
-
copyFilesBuildPhase.props.files.push(appExtensionBuildFile);
|
|
856
786
|
}
|
|
857
787
|
else {
|
|
858
|
-
|
|
859
|
-
|
|
788
|
+
const productType = (0, target_1.productTypeForType)(props.type);
|
|
789
|
+
const isExtension = productType === "com.apple.product-type.app-extension";
|
|
790
|
+
const isExtensionKit = productType === "com.apple.product-type.extensionkit-extension";
|
|
791
|
+
const appExtensionBuildFile = xcode_1.PBXBuildFile.create(project, {
|
|
792
|
+
fileRef: xcode_1.PBXFileReference.create(project, {
|
|
793
|
+
explicitFileType: isExtensionKit
|
|
794
|
+
? "wrapper.extensionkit-extension"
|
|
795
|
+
: "wrapper.app-extension",
|
|
796
|
+
includeInIndex: 0,
|
|
797
|
+
path: productName + (isExtension ? ".appex" : ".app"),
|
|
798
|
+
sourceTree: "BUILT_PRODUCTS_DIR",
|
|
799
|
+
}),
|
|
800
|
+
settings: {
|
|
801
|
+
ATTRIBUTES: ["RemoveHeadersOnCopy"],
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
project.rootObject.ensureProductGroup().props.children.push(
|
|
805
|
+
// @ts-expect-error
|
|
806
|
+
appExtensionBuildFile.props.fileRef);
|
|
807
|
+
targetToUpdate = project.rootObject.createNativeTarget({
|
|
808
|
+
buildConfigurationList: createConfigurationListForType(project, props),
|
|
809
|
+
name: productName,
|
|
810
|
+
productName,
|
|
811
|
+
// @ts-expect-error
|
|
812
|
+
productReference: appExtensionBuildFile.props.fileRef /* alphaExtension.appex */,
|
|
813
|
+
productType: productType,
|
|
860
814
|
});
|
|
815
|
+
const copyPhase = mainAppTarget.getCopyBuildPhaseForTarget(targetToUpdate);
|
|
816
|
+
if (!copyPhase.getBuildFile(appExtensionBuildFile.props.fileRef)) {
|
|
817
|
+
copyPhase.props.files.push(appExtensionBuildFile);
|
|
818
|
+
}
|
|
861
819
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
});
|
|
820
|
+
configureTargetWithKnownSettings(targetToUpdate);
|
|
821
|
+
configureTargetWithEntitlements(targetToUpdate);
|
|
822
|
+
configureTargetWithPreview(targetToUpdate);
|
|
823
|
+
targetToUpdate.ensureFrameworks(props.frameworks);
|
|
824
|
+
targetToUpdate.getSourcesBuildPhase();
|
|
825
|
+
targetToUpdate.getResourcesBuildPhase();
|
|
826
|
+
configureJsExport(targetToUpdate);
|
|
827
|
+
mainAppTarget.addDependency(targetToUpdate);
|
|
871
828
|
const assetsDir = path_1.default.join(magicCwd, "assets");
|
|
872
829
|
// TODO: Maybe just limit this to Safari extensions?
|
|
873
830
|
const explicitFolders = !fs_1.default.existsSync(assetsDir)
|
|
@@ -877,22 +834,35 @@ async function applyXcodeChanges(config, project, props) {
|
|
|
877
834
|
.filter((file) => file !== ".DS_Store" &&
|
|
878
835
|
fs_1.default.statSync(path_1.default.join(assetsDir, file)).isDirectory())
|
|
879
836
|
.map((file) => path_1.default.join("assets", file));
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
837
|
+
const protectedGroup = ensureProtectedGroup(project, path_1.default.dirname(props.cwd));
|
|
838
|
+
if (!protectedGroup.props.children.find((child) => child.props.path === path_1.default.basename(props.cwd))) {
|
|
839
|
+
const syncRootGroup = xcode_1.PBXFileSystemSynchronizedRootGroup.create(project, {
|
|
840
|
+
path: path_1.default.basename(props.cwd),
|
|
841
|
+
exceptions: [
|
|
842
|
+
xcode_1.PBXFileSystemSynchronizedBuildFileExceptionSet.create(project, {
|
|
843
|
+
target: targetToUpdate,
|
|
844
|
+
membershipExceptions: [
|
|
845
|
+
// TODO: What other files belong here, why is this here?
|
|
846
|
+
"Info.plist",
|
|
847
|
+
// Exclude the config path
|
|
848
|
+
path_1.default.relative(magicCwd, props.configPath),
|
|
849
|
+
].sort(),
|
|
850
|
+
}),
|
|
851
|
+
],
|
|
852
|
+
explicitFileTypes: {},
|
|
853
|
+
explicitFolders: [
|
|
854
|
+
// Replaces the previous `lastKnownFileType: "folder",` system that's used in things like Safari extensions to include folders of assets.
|
|
855
|
+
// ex: `"Resources/_locales", "Resources/images"`
|
|
856
|
+
...explicitFolders,
|
|
857
|
+
],
|
|
858
|
+
sourceTree: "<group>",
|
|
859
|
+
});
|
|
860
|
+
if (!targetToUpdate.props.fileSystemSynchronizedGroups) {
|
|
861
|
+
targetToUpdate.props.fileSystemSynchronizedGroups = [];
|
|
862
|
+
}
|
|
863
|
+
targetToUpdate.props.fileSystemSynchronizedGroups.push(syncRootGroup);
|
|
864
|
+
protectedGroup.props.children.push(syncRootGroup);
|
|
893
865
|
}
|
|
894
|
-
extensionTarget.props.fileSystemSynchronizedGroups.push(syncRootGroup);
|
|
895
|
-
ensureProtectedGroup(project, path_1.default.dirname(props.cwd)).props.children.push(syncRootGroup);
|
|
896
866
|
applyDevelopmentTeamIdToTargets();
|
|
897
867
|
syncMarketingVersions();
|
|
898
868
|
return project;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Pod::Spec.new do |s|
|
|
2
|
+
s.name = 'ExtensionStorage'
|
|
3
|
+
s.version = '1.0.0'
|
|
4
|
+
s.summary = 'A sample project summary'
|
|
5
|
+
s.description = 'A sample project description'
|
|
6
|
+
s.author = ''
|
|
7
|
+
s.homepage = 'https://docs.expo.dev/modules/'
|
|
8
|
+
s.platform = :ios, '15.1'
|
|
9
|
+
s.source = { git: '' }
|
|
10
|
+
s.static_framework = true
|
|
11
|
+
|
|
12
|
+
s.dependency 'ExpoModulesCore'
|
|
13
|
+
|
|
14
|
+
# Swift/Objective-C compatibility
|
|
15
|
+
s.pod_target_xcconfig = {
|
|
16
|
+
'DEFINES_MODULE' => 'YES',
|
|
17
|
+
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
21
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import WidgetKit
|
|
3
|
+
|
|
4
|
+
public class ExtensionStorageModule: Module {
|
|
5
|
+
public func definition() -> ModuleDefinition {
|
|
6
|
+
Name("ExtensionStorage")
|
|
7
|
+
|
|
8
|
+
Function("remove") { (forKey: String, suiteName: String?) in
|
|
9
|
+
UserDefaults(suiteName: suiteName)?.removeObject(forKey: forKey)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Function("reloadWidget") { (timeline: String?) in
|
|
13
|
+
if let timeline = timeline {
|
|
14
|
+
WidgetCenter.shared.reloadTimelines(ofKind: timeline)
|
|
15
|
+
} else {
|
|
16
|
+
WidgetCenter.shared.reloadAllTimelines()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Function("setArray") { (forKey: String, data: [[String: Any]], suiteName: String?) -> Bool in
|
|
21
|
+
// Convert the incoming array of dictionaries directly to JSON data
|
|
22
|
+
do {
|
|
23
|
+
let jsonData = try JSONSerialization.data(withJSONObject: data, options: [])
|
|
24
|
+
UserDefaults(suiteName: suiteName)?.set(jsonData, forKey: forKey)
|
|
25
|
+
return true
|
|
26
|
+
} catch {
|
|
27
|
+
// If encoding fails for some reason, return false
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Function("setObject") { (forKey: String, data: [String: Any], suiteName: String?) -> Bool in
|
|
33
|
+
do {
|
|
34
|
+
let jsonData = try JSONSerialization.data(withJSONObject: data, options: [])
|
|
35
|
+
UserDefaults(suiteName: suiteName)?.set(jsonData, forKey: forKey)
|
|
36
|
+
return true
|
|
37
|
+
} catch {
|
|
38
|
+
// If encoding fails for some reason, return false
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Function("setInt") { (key: String, value: Int, group: String?) in
|
|
44
|
+
let userDefaults = UserDefaults(suiteName: group)
|
|
45
|
+
userDefaults?.set(value, forKey: key)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Function("setString") { (key: String, value: String, group: String?) in
|
|
49
|
+
let userDefaults = UserDefaults(suiteName: group)
|
|
50
|
+
userDefaults?.set(value, forKey: key)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacons/apple-targets",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Generate Apple Targets with Expo Prebuild",
|
|
5
|
-
"main": "build/
|
|
5
|
+
"main": "build/ExtensionStorage.js",
|
|
6
|
+
"types": "build/ExtensionStorage.d.ts",
|
|
6
7
|
"files": [
|
|
7
8
|
"app.plugin.js",
|
|
8
9
|
"build",
|
|
10
|
+
"ios",
|
|
11
|
+
"expo-module.config.json",
|
|
9
12
|
"prebuild-blank.tgz"
|
|
10
13
|
],
|
|
11
14
|
"scripts": {
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"dependencies": {
|
|
32
35
|
"@react-native/normalize-colors": "^0.76.1",
|
|
33
36
|
"glob": "^10.2.6",
|
|
34
|
-
"@bacons/xcode": "
|
|
37
|
+
"@bacons/xcode": "1.0.0-alpha.24",
|
|
35
38
|
"fs-extra": "^11.2.0"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
File without changes
|