在 WatchKit 中解析查询

Posted

技术标签:

【中文标题】在 WatchKit 中解析查询【英文标题】:Parse Query in WatchKit 【发布时间】:2015-03-14 16:07:43 【问题描述】:

我在iPhone 应用程序中执行Parse 查询,但在我的Watch 应用程序中尝试执行相同的Parse 查询时遇到错误

这是我的 iPhone 应用程序中的查询:

- (void)viewDidLoad 
    // GMT Date from Phone
    NSDate *gmtNow = [NSDate date];
    NSLog(@"GMT Now: %@", gmtNow);

    // Query Parse
    PFQuery *query = [self queryForTable];
    [query whereKey:@"dateGame" greaterThanOrEqualTo:gmtNow];

    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 
        if (!error) 
            NSMutableArray *localMatchup = [@[] mutableCopy];

            for (PFObject *object in objects) 
                // Add objects to local Arrays
                [localMatchup addObject:[object objectForKey:@"matchup"]];

                // App Group
                NSString *container = @"group.com.me.off";
                NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:container];

                // Matchup
                [defaults setObject:localMatchup forKey:@"KeyMatchup"];
                NSArray *savedMatchup = [defaults objectForKey:@"KeyMatchup"];
                NSLog(@"Default Matchup: %@", savedMatchup);
                savedMatchup = matchupArray;
            

            dispatch_async(dispatch_get_main_queue(), ^
                [self.tableView reloadData];
            );

        
    ];

这是我在 WatchKit 应用中尝试的查询...

- (void)awakeWithContext:(id)context 
    // GMT Date from Phone
    NSDate *gmtNow = [NSDate date];
    NSLog(@"GMT Now: %@", gmtNow);

    // Query Parse
    PFQuery *query = [self queryForTable];
    [query whereKey:@"dateGame" greaterThanOrEqualTo:gmtNow];

    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 
        if (!error) 
            NSMutableArray *localMatchup = [@[] mutableCopy];

            for (PFObject *object in objects) 
                // Add objects to local Arrays
                [localMatchup addObject:[object objectForKey:@"matchup"]];

                // App Group
                NSString *container = @"group.com.me.off";
                NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:container];

                // Matchup
                [defaults setObject:localMatchup forKey:@"KeyMatchup"];
                NSArray *savedMatchup = [defaults objectForKey:@"KeyMatchup"];
                NSLog(@"Default Matchup: %@", savedMatchup);
                savedMatchup = self.matchupArray;
            

            dispatch_async(dispatch_get_main_queue(), ^
                [self.tableView reloadData];
            );
        
    ];

但我在这两行上遇到错误...

`PFQuery *query = [self queryForTable];`

`[self.tableView reloadData];`

因为我猜我无法执行与 table 相关的完全相同的代码,但我只是不确定将其更改为什么。

编辑:为@cnoon 回答添加代码

WatchKitInterfaceController.m:

如何要求我的查询在此处运行? - (void)awakeWithContext:(id)context [super awakeWithContext:context];

    [WKInterfaceController openParentApplication:nil reply:^(NSDictionary *replyInfo, NSError *error) 
        // What to put here?
        NSLog(@"Open Parent Application");
    ];

-和-

iPhoneAppDelegate.h

如何要求我的PFQuery 运行?

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply 
    // What to put here?

【问题讨论】:

你有没有想过用 Obj-C 解决这个问题? @user717452 不,有什么想法吗? 【参考方案1】:

在 WatchKit 应用程序中没有 UITableView 这样的东西。相反,您必须使用WKInterfaceTable。在继续之前,我还建议您通读 WatchKit Programming Guide 中的文档。作为有抱负的 Apple Watch 开发者,它会让您更好地了解所有可用的工具集。

WKInterfaceTable

一旦您了解了WKInterfaceTable 的来龙去脉,您就会很快明白为什么您的方法存在缺陷,原因有二。首先,您没有reloadData 方法。 WatchKit 中的替代方法是setNumberOfRows(_:withRowTypes:)。然后你需要遍历每一行并进行配置。

WatchKit 扩展中的 PFQuery

您遇到问题的第二个原因是您使用了PFQuery

这是一个小建议,所以要么接受,要么放弃。我是根据这里的经验说的,我已经构建了一个非常大的基于页面的 Watch 应用程序,它与 ios 应用程序进行大量通信。

我建议你停止在 WatchKit 扩展中创建PFQuerys。原因是使用您的 Watch 应用程序的用户只会打开应用程序一两秒钟。一切都会发生得非常快。正因为如此,在Watch App被用户终止之前,很难保证网络调用的成功。这让事情变得更加困难,但事实就是如此。

相反,您希望在 iOS 应用上运行 PFQuery 调用,并通过以下调用将该信息返回给 Watch Extension:

WKInterfaceController - openParentApplication(_:reply:) UIApplicationDelegate - handleWatchKitExtensionRequest(_:reply:)

您还可以使用MMWormhole 或类似的替代方法将PFQuery 缓存到shared app group 中。下面是一个示例,说明如何让 Watch Extension 请求 iOS 应用程序运行 PFQuery,将数据缓存在 MMWormhole 中,并在完成后通知 Watch Extension。通过始终从缓存中读取数据,无论 Watch Extension 是否仍在运行以及关闭和重新打开,您都有一个一致的机制。


目标-C

InterfaceController.m

- (void)willActivate 
    [super willActivate];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^
        [WKInterfaceController
         openParentApplication:@@"pfquery_request": @"dumm_val"
         reply:^(NSDictionary *replyInfo, NSError *error) 
             NSLog(@"User Info: %@", replyInfo);
             NSLog(@"Error: %@", error);

             if ([replyInfo[@"success"] boolValue]) 
                 NSLog(@"Read data from Wormhole and update interface!");
             
        ];
    );

AppDelegate.m

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply 
    if (userInfo[@"pfquery_request"]) 
        NSLog(@"Starting PFQuery"); // won't print out to console since you're running the watch extension

        // 1. Run the PFQuery
        // 2. Write the data into MMWormhole (done in PFQuery completion block)
        // 3. Send the reply back to the extension as success (done in PFQuery completion block)

        reply(@@"success": @(YES));
    

    reply(@@"success": @(NO));


斯威夫特

InterfaceController.swift

override func willActivate() 
    super.willActivate()

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Float(NSEC_PER_SEC))), dispatch_get_main_queue()) 
        WKInterfaceController.openParentApplication(["pfquery_request": "dummy_val"])  userInfo, error in
            println("User Info: \(userInfo)")
            println("Error: \(error)")

            if let success = (userInfo as? [String: AnyObject])?["success"] as? NSNumber 
                if success.boolValue == true 
                    println("Read data from Wormhole and update interface!")
                
            
        

        return
    

AppDelegate.swift

func application(
    application: UIApplication!,
    handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!,
    reply: (([NSObject : AnyObject]!) -> Void)!)

    if let pfqueryRequest: AnyObject = (userInfo as? [String: AnyObject])?["pfquery_request"] 
        println("Starting PFQuery") // won't print out to console since you're running the watch extension

        // 1. Run the PFQuery
        // 2. Write the data into MMWormhole (done in PFQuery completion block)
        // 3. Send the reply back to the extension as success (done in PFQuery completion block)

        reply(["success": true])
    

    reply(["success": false])


希望这有助于降低以一致的方式从缓存中读取数据以及将网络请求(或 PFQueries)卸载到 iOS 应用程序的复杂性。

【讨论】:

当我问这个问题***.com/questions/28998918/… 时,我被告知要在 WatchKit 扩展中使用 PFQuery ...所以这就是我尝试它的原因。我尝试在 Watch 应用程序中运行查询的原因是,如果 Watch 应用程序在 iOS 应用程序之前打开,那么我的共享应用程序组中没有数据,因为还没有运行查询。 我想要解决的主要问题是,如果 Watch 应用程序在第一次被使用时打开(在 iPhone 应用程序运行之前),那么它将有没有数据,因为 iPhone 应用程序中的 PFQuery 永远不会运行,因此永远不会将任何数据保存到共享应用程序组中。有意义吗? 对于 WKInterfaceTable 部分,我已经进行了设置和工作,只是我知道如果我要使用 iPhone 应用程序中的相同代码,我将无法使用 [self.tableView reloadData];在我的 Watch 应用中运行查询。你知道吗? @Stephen 的回答是正确的,您“可以”在 Watch Extension 中运行 PFQuerys。我的意思是不推荐它,因为你不能保证它会完成。那有意义吗? iOS 应用程序可以从 Watch Extension 在后台打开并一直运行,直到 PFQuery 完成,无论 Watch Extension 是否仍在运行。 @cnoon 我和 OP 有同样的基本问题,和他一样,我在 Obj-C 中做这一切。你能提供一些样本吗?

以上是关于在 WatchKit 中解析查询的主要内容,如果未能解决你的问题,请参考以下文章

将数据从 Parse tableview 传递到 WatchKit

Watchkit , openParentApplication 和 WatchKit 扩展

如何在 WatchKit 扩展中计算当前位置

WatchKit:在通知短外观和长外观中更改应用名称?

在 WatchKit 应用中播放背景声音

在主要目标中包含 pod,而不是在 WatchKit 扩展中