web-dev-qa-db-ja.com

NSUserDefaultsがWatchOS2のXcodeベータで機能しない

Xcodeの最新ベータ版をインストールして試してみましたSwift 2と、Apple時計開発セクションに加えられた改善点。

iOSWatch OS2の間で情報を共有するこの基本的なNSUserDefaultsメソッドが機能しない理由を実際に理解するのに苦労しています。

thisstep-by-steptutorial に従って、電話アプリケーションとの両方で同じグループをオンにするなど、プロセスで何かを見逃していないかどうかを確認しました。拡張機能ですが、これが私が得たものです:[〜#〜] nothing [〜#〜]

これが私がiPhoneアプリのViewControllerのために書いたものです:

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()
    }
}

そして、これが私がWatchKitのInterfaceControllerに持っているものです:

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()
    }
}

私はいくつかのテストを行いましたが、iOSアプリとWatch OSは異なるグループを利用しているようです...情報を共有していません、ローカルに保存しています。

誰かが同じ問題を抱えていますか?それを修正する方法はありますか?

23
Sara Canducci

ウォッチOS2では、共有グループコンテナを使用できなくなりました。 Apple Docs:

共有グループコンテナを使用してiOSアプリとデータを共有するWatchアプリは、データを異なる方法で処理するように再設計する必要があります。 watchOS 2では、各プロセスがローカルコンテナディレクトリ内の共有データの独自のコピーを管理する必要があります。両方のアプリで実際に共有および更新されるデータの場合、これには、WatchConnectivityフレームワークを使用してそのデータをアプリ間で移動する必要があります。

42
rmp

NSUserDefaults(アプリグループを使用している場合でも)は、watchOS 2のiPhoneとWatchの間で同期しません。iPhoneアプリまたはSettings-Watch.bundleから設定を同期する場合は、自分で同期を処理する必要があります。

この場合、WatchConnectivityのユーザー情報転送を使用すると非常にうまく機能することがわかりました。以下に、これを実装する方法の例を示します。このコードは、電話から時計への一方向の同期のみを処理しますが、他の方法も同じように機能します。

iPhoneアプリ
1)同期が必要な設定の辞書を準備する

- (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)設定をウォッチにプッシュする必要があるタイミングを決定します
(ここには表示されていません)

3)設定を時計にプッシュします

- (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];  
     }  
}  

時計拡張機能
4)ユーザー情報の転送を受信します

- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo  
{  
    NSDictionary *settings = [userInfo objectForKey:@"settings"];  
    if (settings != nil) {  
        // Import the settings  
        [self importSettingsFromCompanionApp:settings];  
     }  
} 

5)受信した設定をウォッチのユーザーデフォルトに保存します

- (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];  
}  
18
Fabian Kreiser

古い機能を再現する簡単な方法があります。古いグループのユーザーデフォルトを辞書にエクスポートし、それをWatchConnectivityフレームワークに送信してから、反対側のユーザーデフォルトに再インポートします。

電話アプリと時計アプリの両方で:

  1. WatchConnectivtyフレームワークを追加します
  2. _#import <WatchConnectivity/WatchConnectivity.h>_そしてWCSessionDelegateとして宣言します
  3. アプリの起動後にセッションを開始するコードを追加します。

    _if ([WCSession isSupported]) {
            WCSession* session = [WCSession defaultSession];
            session.delegate = self;
            [session activateSession];
        }
    _
  4. これを使用して、更新されたデフォルトを他のデバイスに送信します(現在の_[defaults synchronize]_の後に呼び出します):

_[[WCSession defaultSession] updateApplicationContext:[[[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"] dictionaryRepresentation] error:nil];_

  1. 設定を受信して​​デフォルトに戻します-これを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];
    }
    _

WC以外のデバイスのサポートを維持するように注意してください-updateApplicationContext呼び出しをif ([WCSession isSupported])でラップします

12
A.Badger

すでに述べたように、共有NSUserDefaultsはWatchOS2では機能しなくなりました。

これが@RichAbleの答えのSwiftバージョンで、さらにいくつかのメモがあります。

iPhoneアプリで、次の手順に従います。

データをApple Watch fromからプッシュし、フレームワークを上部に追加するビューコントローラーを選択します。

import WatchConnectivity

次に、時計とのWatchConnectivityセッションを確立し、データを送信します。

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)
        }
    }
}

デリゲートの設定をスキップすると、これは機能しないことに注意してください。したがって、デリゲートを使用したことがない場合でも、デリゲートを設定して、次の拡張機能を追加する必要があります。

extension MyViewController: WCSessionDelegate {

}

これで、時計アプリ(この正確なコードはGlanceやその他の時計キットアプリタイプでも機能します)にフレームワークを追加します。

import WatchConnectivity

次に、接続セッションを設定します。

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)
    let watchSession = WCSession.defaultSession()
    watchSession.delegate = self
    watchSession.activateSession()
}

iOSアプリからのメッセージを聞いて処理するだけです。

extension InterfaceController: WCSessionDelegate {

    func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
        print("\(applicationContext)")
        dispatch_async(dispatch_get_main_queue(), {
            //update UI here
        })
    }

}

これですべてです。

注意事項:

  1. 新しいapplicationContextは何度でも送信できます。時計が近くにあり、接続されているかどうか、または時計アプリが実行されているかどうかは関係ありません。これにより、インテリジェントな方法でバックグラウンドでデータが配信され、そのデータはウォッチアプリの起動時に待機します。
  2. 時計アプリが実際にアクティブで実行されている場合、ほとんどの場合、すぐにメッセージを受信するはずです。
  3. このコードを逆にして、時計に同じ方法でiPhoneアプリにメッセージを送信させることができます。
  4. 時計アプリが表示されたときに受信するapplicationContextは、最後に送信したメッセージのみになります。時計アプリが表示される前に20通のメッセージを送信した場合、最初の19通は無視され、20通目のメッセージが処理されます。
  5. 2つのアプリ間で直接/ハード接続を行う場合、またはバックグラウンドファイル転送やキューに入れられたメッセージングを行う場合は、 WWDCビデオ oを確認してください。
7
William T.

これを手に入れるのに何時間もかかりました。この非常に便利なビデオをご覧ください! WatchConnectivityを使用してiPhoneアプリとwacthの間でNSUserDefaultを共有する方法の基本的な考え方を説明します。

https://www.youtube.com/watch?v=VblFPEomUtQ

1
Mona