I do a Parse
query in my iPhone
app, but I am getting errors trying to do the same Parse
query in my Watch
app.
This is the query in my iPhone app:
- (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];
});
}
}];
}
This is the query I tried in my WatchKit app...
- (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];
});
}
}];
}
But I get errors on these two lines...
`PFQuery *query = [self queryForTable];`
`[self.tableView reloadData];`
because I can't do the same exact code related to a table
I'm guessing, but I'm just not sure what to change it to.
EDIT: Adding code per @cnoon answer
WatchKit
InterfaceController.m
:
How would I ask for my query to run here?
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
[WKInterfaceController openParentApplication:nil reply:^(NSDictionary *replyInfo, NSError *error) {
// What to put here?
NSLog(@"Open Parent Application");
}];
-and-
iPhone AppDelegate.h
How would I ask for my PFQuery
to run?
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply {
// What to put here?
}
There's no such thing as a UITableView
in a WatchKit application. Instead, you have to work with WKInterfaceTable. Before you continue, I'd also suggest you read through the documentation in the WatchKit Programming Guide. It will give you a MUCH better understanding of all the toolsets available to you as an aspiring Apple Watch developer.
WKInterfaceTable
Once you know the ins and outs of a WKInterfaceTable
, you'll quickly see why your approach is flawed for two reasons. First off, you don't have a reloadData
method. The alternative in WatchKit is to setNumberOfRows(_:withRowTypes:)
. Then you need to iterate through each row and configure it.
PFQuery in WatchKit Extension
The second reason you are going to have issues is due to your use of PFQuery
.
This is a bit of side advice, so take it or leave it. I'm speaking from experience here having already built a very large page-based Watch App that heavily communicates with the iOS App.
I would advise you to stop making PFQuery
s in your WatchKit Extension. The reason is that users using your Watch App are only going to have the app open for a second or two. Everything will happen extremely fast. Because of this, it is extremely difficult to guarantee the success of network calls before the Watch App is terminated by the user. This makes things MUCH more difficult, but is simply the way it is.
Instead, you want to run your PFQuery
calls on the iOS App and return that information back to the Watch Extension through the following calls:
WKInterfaceController
- openParentApplication(_:reply:)
UIApplicationDelegate
- handleWatchKitExtensionRequest(_:reply:)
You can also cache the PFQuery
into the shared app group using MMWormhole or a similar approach alternative. Below is an example of how you can have your Watch Extension request the iOS Application to run a PFQuery, cache the data in MMWormhole and notify the Watch Extension once it is finished. By always reading the data out of the cache, you have a consistent mechanism whether the Watch Extension was still running as well as closed and re-opened.
Objective-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)});
}
Swift
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])
}
Hopefully that helps break down the complexity of having a consistent way to read data from the cache as well as offload network requests (or PFQueries) to the iOS App.