Using Storyboard I have setup an application with three tab bars. When I click on one of the tabs and have a singleton datasource class perform an action, and than immediately switch to another tab, when the singleton finishes fetching data externally it tries to send it to the current tab which causes a crash because the current tab I am on does not respond to the specific delegate method that I have implemented in the singleton delegate, and should not implement since there is no reason for that specific tab to perform that action. Here is how my delegate is currently setup.
#class DataHolder;
#protocol DataHolderControllerDelegate <NSObject>
#required
-(void)logout;
#optional
-(void)friendsQuarryDidFinishWithData;
-(void)pendingFriendsQuarryDidFinishWithData;
-(void)allUsersQuarryDidFinishWithData;
-(void)additionalFriendsFoundAndAdded;
-(void)messageQuarryFinishedWithData;
-(void)thumbnailQuaryDidFinishWithData;
-(void)sentRequestUsersFoundWithData;
#end
#interface DataHolder : NSObject;
#property (nonatomic,weak) id <DataHolderControllerDelegate>delegate;
The delegate is called within the method when data is queried.
-(void)messageQuarry{
PFQuery *messageQUery = [PFQuery queryWithClassName:#"Message"];
messageQUery.cachePolicy = kPFCachePolicyCacheThenNetwork;
[messageQUery whereKey:#"recipientIds" equalTo:[[PFUser currentUser] objectId]];
[messageQUery whereKey:#"file_type" equalTo:#"original_image.png"];
[messageQUery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#"ERROR: %#, %#", error, [error userInfo]);
}else{
messagesArray = [NSMutableArray arrayWithArray:objects];
[delegate messageQuarryFinishedWithData];
}
}];
}
Than, the view controller has a delegate method, this is the inboxViewController delegate method that is called when the delegate method above is called.
-(void)messageQuarryFinishedWithData{
self.messages = [NSMutableArray arrayWithArray:dataHolder.getMessages];
[self.tableView reloadData];
}
Remember that Delegates are meant to be intimate, meaning that its a 1-1 type relationship. It seems that what you are looking for is having a one-to-many relationship which is what NSNotificationCenter is used for. I recommend looking up the NSNotificationCenter documentation form Apple.
Related
I am trying to fetch a list of PFObjects of a PFUser to display in the iOS 8 Today Widget.
Following this blog post by Parse, I've enabled the same App Groups and Keychain Sharing in both my main app and extension in Xcode.
I've also enabled the following in the AppDelegate of my main app and the viewDidLoad of my Today Extension:
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.me.myapp" containingApplication:#"com.me.myapp"];
[Parse setApplicationId:#"myAppId" clientKey:#"myClientId"];
In widgetPerformUpdateWithCompletionHandler, I constructed and performed my query:
- (void) widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
PFQuery *query = [PFQuery queryWithClassName:#"Note"];
[query whereKey:#"User" equalTo:[PFUser currentUser]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
// check for difference between current and new data
if([self hasNewData:objects]) {
// fresh data
notes = objects;
[self.tableView reloadData];
[self updatePreferredContentSize];
completionHandler(NCUpdateResultNewData);
} else {
// Data is the same
completionHandler(NCUpdateResultNoData);
}
} else {
// Failed
completionHandler(NCUpdateResultFailed);
}
}];
}
}
The first load seems to work fine - I'm able to get my list of PFObjects. However, whenever the extension reloads a second time, the following exception: enableDataSharingWithApplicationGroupIdentifier:containingApplication:' must be called before 'setApplicationId:clientKey'' is thrown at the enableDataSharingWithApplicationGroupIdentifier call in viewDidLoad.
I can replicate this reload by swiping the Notification Center to the "Notifications" tab and swiping it back, which would cause viewDidLoad to be invoked again.
I've double-checked that the order of calling the methods are right, and even tinkered with the order but am still getting the crash.
Any ideas? Thanks in advance!
Try this
if(![Parse isLocalDatastoreEnabled]) {
[Parse enableLocalDatastore];
[Parse enableDataSharingWithApplicationGroupIdentifier:#"group.com.me.myapp" containingApplication:#"com.me.myapp"];
[Parse setApplicationId:#"myAppId" clientKey:#"myClientId"];
}
I am using this query to find users, it works but it just shows me the first user. I want it to show me the user with the text of an UITextField.
How can I do that ?
(I have a textfield and there I type in a name and then it should show the parsed users with the name)
PFQuery *query = [PFUser query];
NSArray *users = [query findObjects];
userQuerys.text = users[0][#"username"];
Thanks very much
This code will fetch you all the PFUsers in which username is equal to the name parameter:
- (void)searchUsersNamed:(NSString *)name withCompletion:(void (^)(NSArray *users))completionBlock {
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:name];
[query findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
if (!error) {
// we found users with that username
// run the completion block with the users.
// making sure the completion block exists
if (completionBlock) {
completionBlock(users);
}
} else {
// log details of the failure
NSLog(#"Error: %# %#", error, [error description]);
}
}];
}
An example, if you need to update the UI with the result, for example, a table:
- (void)someMethod {
// we will grab a weak reference of self to perform
// work inside the completion block
__weak ThisViewController *weakSelf = self;
//replace ThisViewController with the correct self class
[self searchUsersNamed:#"Phillipp" withCompletion:^(NSArray *users) {
//perform non-UI related logic here.
//set the found users inside the array used by the
//tableView datasource. again, just an example.
weakSelf.users = users;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//pefrorm any UI updates only
//for example, update a table
[weakSelf.tableView reloadData];
}];
}];
}
A small note: the completionBlock here wont run if there is an error, but it will run even if no users were found, so you gotta treat that (if needed. in this example, it was not needed).
Avoid running non-UI related logic on that mainQueue method, you might lock the Main thread, and that`s bad user experience.
I am new with Parse and Ios-development.
I develop a ios-app that use Parse as backend.
I have got the main-function to work now, but i have a BIG problem.
I want to create a separate class for my API-handling to Parse. As i set it up now i have my parse-code directly in my view-controllers and as far as i know that not that nice coding.
But, the issue is to handle the background-jobs. Let say if i want to do a GET from the server, this can be done in a background-thread, just using "findObjectsInBackgroundWithBlock"
The problem is when i move this method to a separate API-class. Then my ViewController ask my API-class to get all the objects an the API-class will return it as soon its done. It will nor run in the background, i cant return a NSMutableArray with objects to the viewController until the fetch is done.
I have thinking that i maybe can get the data from parse synchronously in my API-class by using [query findObjects:&error] , as long as i figure out how to create my get-method in the API-class to run asynchronously.
I have try to create my API-method as a asynchronously method using blocks but will not run in background on a separate thread. (I am new to blocks an dont evan no if thats the correct way to crate a method that will run in a separate thread when using it)
Here is my API-method (Class: APIClient)
+ (void) GETAllShoppingGroups:(void (^) (NSMutableArray*))completionBlock{
//Create a mutable array (nil)
NSMutableArray *shoppingGroupsArray = nil;
//Create query for class ShoppingGroupe
PFQuery *query = [ShoppingGroupe query];
//Filter - find only the groups the current user is related to
[query whereKey:#"members" equalTo:[PFUser currentUser]];
//Sort Decending
[query orderByDescending:#"createdAt"];
//Tell Parse to also send the real member-objects and not only id
[query includeKey:#"members"];
//Send request of query to Parse with a "error-pointer"and fetch in a temp-array
NSError *error = nil;
NSArray *tempArray = [NSArray arrayWithArray:[query findObjects:&error]];
//Check for success
if (!tempArray) {
NSLog(#"%#", error);
NSLog(#"ERROR: %#", [error userInfo][#"error"]);
return completionBlock(shoppingGroupsArray);
} else {
//Seccess
shoppingGroupsArray = tempArray.mutableCopy;
completionBlock(shoppingGroupsArray);
}
}
Here is my ViewController Class (Class: ShoppingGruopViewController)
- (void) getAllObjects{
//Init array if nil
if (!self.shoppingGroupeArray) {
self.shoppingGroupeArray = [[NSMutableArray alloc]init];
}
//Remove old objects
[self.shoppingGroupeArray removeAllObjects];
//Get objects
[APIClient GETAllShoppingGroups:^(NSMutableArray* completionBlock){
if (completionBlock) {
[self.shoppingGroupeArray addObjectsFromArray:completionBlock]; }
[self.tableView reloadData];
}];
}
I am calling these to get all user list from class userlist in parse sdk ios
PFQuery *query = [PFQuery queryWithClassName:#"userlist"];
[query orderByAscending:kUserID];
query.cachePolicy = kPFCachePolicyNetworkElseCache;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
for (PFObject *object in objects)
{
NSDictionary *dictUser = [NSDictionary dictionaryWithObjectsAndKeys:[object valueForKey:kUserName],kUserName,[object valueForKey:kUserID],kUserID,[object valueForKey:kUserImage],kUserImage,nil];
//NSLog(#"%#",dictUser);
[mutArrUserList addObject:dictUser];
}
}
else
{
NSLog(#"Error while getting data");
}
//NSLog(#"\nArray - %#",mutArrUserList);
[tblViewList reloadData];
}];
My question is how parse sdk ios can notify when data changes in class userlist?
Alternate solution is that, i have to continously make call to check by myself that this changes have been done to notify user!!!!!!
You can do this using the delegate pattern:
Creating a protocol like this:
#protocol myDelegate
#optional -(void)myCustomAction;
#end
With a property like this:
#property (nonatomic, strong) id<myDelegate> delegate;
After that you call you delegate method in your completion block, like this:
[delegate myCustomAction];
Then you need to implement that protocol in another class, just like you do with UITableViewDelegate.
Hope it helps.
If your asking in the parse can notify the app that something is updated on server, than the answer is no. Its your job to go checking for new data. If you dont want to go checking the data to many times then the you add NSDate attribute to parse object and when you want to update data check if the date is old enough to update. And every time you update the date from server also update this date to currenct date.
There is also an option to notify your app via silent push notifications, but that one is not recommended.
Or if your problem is how to get notify about data changes inside ios app, the delegates are the right way. If you'll be more specific about the problem I'll add some more code. Please explain a bit more.
Okay so the problem I'm having in one of my functions is when users are logging in through Facebook, and my app is checking wether or not their Facebook username already exists in our app (Parse.com) database.
If so, present a view controller (which is where I'm having a problem since this is in a NSObject) if not, simply write their facebook username to parse username string. Error I get is: No known class method for selector 'presentViewController:animated:completion'
Here is my code:
PFQuery *query = [PFUser query]; //1
[query whereKey:#"username" equalTo:me.username];//2
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {//4
if (!error) {
NSLog(#"Successfully retrieved: %#", objects);
if ([objects count] > 0)
{
// LOOKING TO PRESENT VIEW CONTROLLER HERE?
// TRIED:
// [self presentViewController:[#"RegisterUsername" animated:YES completion:nil];
} else {
[[PFUser currentUser] setObject:me.username forKey:#"username"];
}
} else {
NSString *errorString = [[error userInfo] objectForKey:#"error"];
NSLog(#"Error: %#", errorString);
}
}];
and since this is inheriting from NSObject, it won't allow me to use the presentViewController. Not sure what else to do from here. :-( This view controller was going to make them set a username before continuing with our app. This code is currently in a login function in a header Comms.h (NSOBJECT) for Facebook iOS SDK
NSObject does not declare the method presentViewController:. You'll need to add a UIViewController subclass to the window and then present from that. In the completion block of your dismissViewController you should remove the parent from the window.
Alternatively you could create a delegate protocol for checking the username and then notify a UIViewController of the need to either set the username internally or present a login view.