Native Access on Preferences (iOS)
Bug Report
Im using the Preferences Plugin to store the current used locale on the device.
// language.ts
await Preferences.set({
key: 'locale',
value: updatedLocale.value,
});
Now I want to access the locale in the Notification Service Extension (iOS) to modify my Notification based on the language. On Android I access the Preferences by using shared Preferences with the Prefix CapacitorStorage and everything works fine. On iOS I tried but I can't retrieve the key/value:
//NotificationServiceExtension.swift
let i = UserDefaults.standard.string(forKey: "CapacitorStorage.locale")
let u = UserDefaults.standard.string(forKey: "locale")
let sharedDefaults = UserDefaults(suiteName: "CapacitorStorage")
if let locale = sharedDefaults?.string(forKey: "locale") {
print("Locale in Notification Service Extension: \(locale)")
} else {
print("Locale not found")
}
Plugin(s)
@capacitor/preferences V 6.0.1
Capacitor Version
Im using Quasar so quasar info:
Operating System - Darwin(24.0.0) - darwin/arm64
NodeJs - 20.17.0
Global packages
NPM - 10.8.2
yarn - Not installed
@quasar/cli - 2.4.1
@quasar/icongenie - Not installed
cordova - Not installed
Important local packages
quasar - 2.17.0 -- Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time
@quasar/app-vite - 1.10.0 -- Quasar Framework App CLI with Vite
@quasar/extras - 1.16.12 -- Quasar Framework fonts, icons and animations
eslint-plugin-quasar - Not installed
vue - 3.5.11 -- The progressive JavaScript framework for building modern web UI.
vue-router - 4.4.5
pinia - 2.2.4 -- Intuitive, type safe and flexible Store for Vue
vuex - Not installed
vite - 2.9.18 -- Native-ESM powered web dev build tool
eslint - 8.57.1 -- An AST-based pattern checker for JavaScript.
electron - Not installed
electron-packager - Not installed
@electron/packager - Not installed
electron-builder - Not installed
register-service-worker - 1.7.2 -- Script for registering service worker, with hooks
@capacitor/core - 6.1.0 -- Capacitor: Cross-platform apps with JavaScript and the web
@capacitor/cli - 6.1.0 -- Capacitor: Cross-platform apps with JavaScript and the web
@capacitor/android - 6.1.0 -- Capacitor: Cross-platform apps with JavaScript and the web
@capacitor/ios - 6.1.2 -- Capacitor: Cross-platform apps with JavaScript and the web
Quasar App Extensions
*None installed*
Networking
Host - MacBook-Pro
en0 - 192.168.188.39
Platform(s)
iOS only
Current Behavior
I don't receive any key/value pair buy trying to access via native code.
Expected Behavior
Get Key/Value by using native Code
UserDefaults.standard.string(forKey: "locale") or
UserDefaults.standard.string(forKey: "CapacitorStorage.locale")
like it is on android
Other Technical Details
Testet with Xcode Version 15.1 on iPhone 16 Pro
This issue needs more information before it can be addressed. In particular, the reporter needs to provide a minimal sample app that demonstrates the issue. If no sample app is provided within 15 days, the issue will be closed.
Please see the Contributing Guide for how to create a Sample App.
Thanks! Ionitron 💙
@Excel1 can you provide a GitHub repo with an app where the issue can be replicated?
@alexgerardojacinto
The reason wyh this doesnt work is, that on iOS SharedPreferences are protected from access in outer Services like NotificationServiceExtension. So you can access SharedPreferences from the app set by the plugin not from the NotificationServiceExtension. You manually have to write a sync code - so on startup you save all preferences from your app in prefernces where both (Service Extensions and the App) can access. For this you have to create an App group.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
migrateUserDefaultsToAppGroups()
return true
}
// Workaround: cant access UserDefaults set by capacitor from an extension - only by using group
func migrateUserDefaultsToAppGroups() {
// User Defaults - Old
let userDefaults = UserDefaults.standard
// App Groups Default - New
let groupDefaults = UserDefaults(suiteName: "group.mygroup.app")
if let groupDefaults = groupDefaults {
for key in userDefaults.dictionaryRepresentation().keys {
if key == "CapacitorStorage.locale" {
groupDefaults.set(userDefaults.dictionaryRepresentation()[key], forKey: key)
}
}
groupDefaults.synchronize()
print("Successfully migrated defaults")
} else {
print("Unable to create NSUserDefaults with given app group")
}
}
So this issue can be closed. Maybe a hint in the documentation would be good.
How to call this swift func proactively within ionic web?
The reason wyh this doesnt work is, that on iOS SharedPreferences are protected from access in outer Services like NotificationServiceExtension. So you can access SharedPreferences from the app set by the plugin not from the NotificationServiceExtension. You manually have to write a sync code - so on startup you save all preferences from your app in prefernces where both (Service Extensions and the App) can access. For this you have to create an App group.
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. migrateUserDefaultsToAppGroups() return true }// Workaround: cant access UserDefaults set by capacitor from an extension - only by using group func migrateUserDefaultsToAppGroups() { // User Defaults - Old let userDefaults = UserDefaults.standard // App Groups Default - New let groupDefaults = UserDefaults(suiteName: "group.mygroup.app") if let groupDefaults = groupDefaults { for key in userDefaults.dictionaryRepresentation().keys { if key == "CapacitorStorage.locale" { groupDefaults.set(userDefaults.dictionaryRepresentation()[key], forKey: key) } } groupDefaults.synchronize() print("Successfully migrated defaults") } else { print("Unable to create NSUserDefaults with given app group") } }So this issue can be closed. Maybe a hint in the documentation would be good.
This doc helps. https://capacitorjs.com/docs/ios/custom-code