swift - NSUserDefaults no funciona en Xcode beta con Watch OS2
operating-system watchkit (5)
Acabo de instalar la última versión beta de Xcode para probar Swift 2 y las mejoras realizadas en la sección de desarrollo de Apple Watch.
De hecho, estoy teniendo dificultades para entender por qué este método básico NSUserDefaults
para compartir información entre iOS y Watch OS2 no funciona.
Seguí este tutorial paso a paso para comprobar si me perdí algo en el proceso, como activar el mismo grupo tanto para la aplicación del teléfono como para la extensión, pero esto es lo que obtuve: NADA .
Esto es lo que escribí para ViewController en la aplicación de iPhone:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var lb_testo: UITextField!
let shared_defaults:NSUserDefaults = NSUserDefaults(suiteName: "group.saracanducci.test")!
var name_data:NSString? = ""
override func viewDidLoad() {
super.viewDidLoad()
name_data = shared_defaults.stringForKey("shared")
lb_testo.text = name_data as? String
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
@IBAction func upgrade_name(sender: AnyObject) {
name_data = lb_testo.text
shared_defaults.setObject(name_data, forKey: "shared")
lb_testo.resignFirstResponder()
shared_defaults.synchronize()
}
}
Y esto es lo que tengo en el InterfaceController para WatchKit:
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
@IBOutlet var lb_nome: WKInterfaceLabel!
let shared_defaults:NSUserDefaults = NSUserDefaults(suiteName: "group.saracanducci.test")!
var name_data:NSString? = ""
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}
override func willActivate() {
super.willActivate()
if (shared_defaults.stringForKey("shared") != ""){
name_data = shared_defaults.stringForKey("shared")
lb_nome.setText(name_data as? String)
}else{
lb_nome.setText("No Value")
}
}
override func didDeactivate() {
super.didDeactivate()
}
}
Hice algunas pruebas y parece que la aplicación iOS y Watch OS se aprovechan de diferentes grupos ... no están compartiendo información , sino que la almacenan localmente.
¿Alguien tiene el mismo problema? ¿Alguna de idea de cómo arreglarlo?
Como se mencionó anteriormente, NSUserDefaults compartidos ya no funcionan en WatchOS2.
Aquí está la versión rápida de la respuesta de @ RichAble con algunas notas más.
En tu aplicación de iPhone , sigue estos pasos:
Elija el controlador de vista desde el que desea insertar datos en el Apple Watch y agregue el marco en la parte superior.
import WatchConnectivity
Ahora, establezca una sesión WatchConnectivity con el reloj y envíe algunos datos.
if WCSession.isSupported() { //makes sure it''s not an iPad or iPod
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
if watchSession.paired && watchSession.watchAppInstalled {
do {
try watchSession.updateApplicationContext(["foo": "bar"])
} catch let error as NSError {
print(error.description)
}
}
}
Tenga en cuenta que esto NO funcionará si omite configurar el delegado, por lo que incluso si nunca lo usa, debe configurarlo y agregar esta extensión:
extension MyViewController: WCSessionDelegate {
}
Ahora, en su aplicación de reloj (este código exacto también funciona para Glances y otros tipos de aplicaciones de kits de vigilancia), agrega el marco:
import WatchConnectivity
Luego configura la sesión de conectividad:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
}
y simplemente escucha y maneja los mensajes de la aplicación de iOS:
extension InterfaceController: WCSessionDelegate {
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print("/(applicationContext)")
dispatch_async(dispatch_get_main_queue(), {
//update UI here
})
}
}
Eso es todo al respecto.
Elementos de la nota:
- Puede enviar un nuevo ApplicationContext tantas veces como desee y no importa si el reloj está cerca y conectado o si la aplicación de visualización se está ejecutando. Esto entrega los datos en segundo plano de una manera inteligente y esa información está esperando allí cuando se inicia la aplicación de vigilancia.
- Si su aplicación de vigilancia está realmente activa y ejecutándose, debería recibir el mensaje de inmediato en la mayoría de los casos.
- Puede revertir este código para que el reloj envíe mensajes a la aplicación de iPhone de la misma manera.
- applicationContext que su aplicación de reloj recibe cuando se lo visualiza SOLO será el último mensaje enviado. Si envió 20 mensajes antes de ver la aplicación de observación, ignorará los primeros 19 y se encargará de la vigésima.
- Para hacer una conexión directa / difícil entre las 2 aplicaciones o para transferencias de archivos en segundo plano o mensajes en cola, consulte la developer.apple.com/videos/play/wwdc2015-713 o.
Con Watch OS2, ya no puede usar contenedores de grupos compartidos. Documentos de Apple:
Mire las aplicaciones que compartieron datos con sus aplicaciones de iOS utilizando un contenedor de grupo compartido y deben rediseñarse para manejar los datos de manera diferente. En watchOS 2, cada proceso debe administrar su propia copia de los datos compartidos en el directorio local del contenedor. Para los datos que ambas aplicaciones comparten y actualizan, esto requiere usar el marco de Watch Connectivity para mover esos datos entre ellos.
Hay una manera simple de reproducir la funcionalidad anterior, exportar los valores predeterminados del usuario del grupo anterior a un diccionario, enviarlos a través del marco WatchConnectivity y luego volver a importarlos a los valores predeterminados del usuario en el otro lado:
En aplicaciones de teléfono y reloj:
- Agregue el marco WatchConnectivty
-
#import <WatchConnectivity/WatchConnectivity.h>
y declarar comoWCSessionDelegate
Agregue código para iniciar sesión una vez que la aplicación se haya iniciado:
if ([WCSession isSupported]) { WCSession* session = [WCSession defaultSession]; session.delegate = self; [session activateSession]; }
Use esto para enviar los valores predeterminados actualizados al otro dispositivo (llame después de su
[defaults synchronize]
):
[[WCSession defaultSession] updateApplicationContext:[[[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"] dictionaryRepresentation] error:nil];
Reciba y guarde la configuración en su valor predeterminado: agregue esto a WCDelegate:
-(void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext { NSLog(@"New Session Context: %@", applicationContext); NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"]; for (NSString *key in applicationContext.allKeys) { [defaults setObject:[applicationContext objectForKey:key] forKey:key]; } [defaults synchronize]; }
Tenga cuidado de mantener el soporte para dispositivos que no sean WC: ajuste sus llamadas a updateApplicationContext con if ([WCSession isSupported])
Me llevó horas y horas conseguir esto. Mire este video muy útil! ¡Te da la idea básica de cómo usar WatchConnectivity para compartir NSUserDefault entre la aplicación de iPhone y wacth!
NSUserDefaults (incluso con un grupo de aplicaciones) no se sincronizan entre el iPhone y Watch en watchOS 2. Si desea sincronizar configuraciones desde su aplicación de iPhone o Settings-Watch.bundle, debe manejar la sincronización usted mismo.
Descubrí que el uso de las transferencias de información de usuario de WatchConnectivity funciona muy bien en este caso. A continuación encontrará un ejemplo de cómo podría implementar esto. El código solo maneja la sincronización en un sentido desde el teléfono al reloj, pero a la inversa, funciona de la misma manera.
En la aplicación de iPhone :
1) Prepare el diccionario de configuraciones que deben sincronizarse
- (NSDictionary *)exportedSettingsForWatchApp
{
NSUserDefaults *userDefaults = [self userDefaults]; // the user defaults to sync
NSSet *keys = [self userDefaultKeysForWatchApp]; // set of keys that need to be synced
NSMutableDictionary *exportedSettings = [[NSMutableDictionary alloc] initWithCapacity:keys.count];
for (NSString *key in keys) {
id object = [userDefaults objectForKey:key];
if (object != nil) {
[exportedSettings setObject:object forKey:key];
}
}
return [exportedSettings copy];
}
2) Determine cuándo se deben pasar las configuraciones al reloj
(no se muestra aquí)
3) Presione la configuración para mirar
- (void)pushSettingsToWatchApp
{
// Cancel current transfer
[self.outstandingSettingsTransfer cancel];
self.outstandingSettingsTransfer = nil;
// Cancel outstanding transfers that might have been started before the app was launched
for (WCSessionUserInfoTransfer *userInfoTransfer in self.session.outstandingUserInfoTransfers) {
BOOL isSettingsTransfer = ([userInfoTransfer.userInfo objectForKey:@"settings"] != nil);
if (isSettingsTransfer) {
[userInfoTransfer cancel];
}
}
// Mark the Watch as requiring an update
self.watchAppHasSettings = NO;
// Only start a transfer when the watch app is installed
if (self.session.isWatchAppInstalled) {
NSDictionary *exportedSettings = [self exportedSettingsForWatchApp];
if (exportedSettings == nil) {
exportedSettings = @{ };
}
NSDictionary *userInfo = @{ @"settings": exportedSettings };
self.outstandingSettingsTransfer = [self.session transferUserInfo:userInfo];
}
}
En la extensión de Watch :
4) Recibir la transferencia de información del usuario
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo
{
NSDictionary *settings = [userInfo objectForKey:@"settings"];
if (settings != nil) {
// Import the settings
[self importSettingsFromCompanionApp:settings];
}
}
5) Guarde las configuraciones recibidas para los valores predeterminados del usuario en el reloj
- (void)importSettingsFromCompanionApp:(NSDictionary *)settings
{
NSUserDefaults *userDefaults = [self userDefaults]; // the user defaults to sync
NSSet *keys = [self userDefaultKeysForWatchApp]; // set of keys that need to be synced
for (NSString *key in keys) {
id object = [settings objectForKey:key];
if (object != nil) {
[userDefaults setObject:object forKey:key];
} else {
[userDefaults removeObjectForKey:key];
}
}
[userDefaults synchronize];
}