Objective-C Shared variables between classes - objective-c

Basically I've implemented a connection method which parses a JSON from an URL, via sendAsynchronousRequest. Everything's working nicely. But at the end of the sendAsynchronousRequest function, I need to reload a tableView (since the data arrived and I need to show it).
Currently I'm doing it by sending the tableView as parameter to the function of the class that does the connection
#implementation WhosWhereConnection
- (void)setUpConnection:(UITableView *)tableView {
...
[tableView reloadData];
...
}
And calling the function with
[connection setUpConnection:self.tableView];
It's working as I intended, but I feel this ins't the most elegant way of doing it. What would you recommend?
Wish I could accept all your answers, thank you for helping :)

I would recommend using blocks for this. It is convenient and very strong solution.
Something like this:
Method header (.h file)
- (void)setupConnectionWithCompletion:(void(^)())completionBlock;
Method implementation (.m file)
- (void)setupConnectionWithCompletion:(void(^)())completionBlock
{
// Do your stuff
// Call completion block (if set) when everything is done
if(completionBlock) {
completionBlock();
}
}
And call it like this
[connection setupConnectionWithCompletion:^{
[tableView reloadData];
}];

Better to have a delegate method / block which is called on completion, or to post a notification (if multiple instances are interested in the event). This will allow you to break the dependency you currently have by making the actions performed as a result of the completion event anonymous to the WhosWhereConnection class. The simplest change will be to replace the table view parameter with a block.
Using a delegate requires the most code. The other answers show the code for the other options.
For delegation, we want:
A protocol to define method(s) that will be called
A property to hold the delegate object reference
Usage of the delegate
Implementation of the delegate method(s)
1, 2 & 3 are on WhosWhereConnectionDelegate class. 4 is on the table view controller.
1.
#protocol WhosWhereConnectionDelegate < NSObject >
- (void)connection:(WhosWhereConnectionDelegate *)connection didCompleteWithStatus:(BOOL)status;
#end
2.
#property (weak, nonatomic) id < WhosWhereConnectionDelegate > delegate;
3.
You don't show what setUpConnection does, but the delegate call should be made once the connection is complete.
- (void)setUpConnection {
BOOL status = NO;
...
// stuff here to process things and determine the status
...
[self.delegate connection:self didCompleteWithStatus:status];
...
}
4.
The table view controller sets itself as the delegate of the connection before the connection starts.
- (void)connection:(WhosWhereConnectionDelegate *)connection didCompleteWithStatus:(BOOL)status
{
[self.tableView reloadData];
}

NSNotifications is what you need.
- (void)setUpConnection{
//...
[[NSNotificationCenter defaultCenter] postNotificationName:#"notificationName" object:yourData];
//...
}
In your viewController:
- (void)viewDidLoad
{
//...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataDidLoad:) name:#"notificationName" object:nil];
//...
}
- (void)dataDidLoad:(NSNotification*)notification
{
//do your stuff
[tebleView reloadData];
}

Related

Calling a Method on Watchkit

I attended to a watchkit hackathon yesterday and I had some problems regarding calling a method on an NSObject class which uses the Google Maps API and send local notifications. If I call this method from my Watchkit extension, the code doesn't compile, but If I call from the ViewController, for example, everything works perfectly
#import "InterfaceController.h"
#import "Methods.h"
#interface InterfaceController()
#end
#implementation InterfaceController
- (instancetype)initWithContext:(id)context {
self = [super initWithContext:context];
if (self){
// Initialize variables here.
// Configure interface objects here.
NSLog(#"%# initWithContext", self);
}
return self;
}
- (IBAction)butRoute
{
Methods *mt = [[Methods alloc]init];
[mt notif:#"ARRIVING!"];
//***** If I call this method, my code won't compile!!! *****
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
NSLog(#"%# will activate", self);
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
NSLog(#"%# did deactivate", self);
}
#end
The error I get is:
Check the target for your Methods class and make sure it is in your watch kit extensions target.
Alternately, look at building a framework for your shared classes. https://developer.apple.com/videos/wwdc/2014/?id=416
I don't know what xcode version you are using but take into account that the initWithContext method is no longer valid. You should be using:
- (void)awakeWithContext:(id)context
And you shouldn't be overwriting it, just use it.
just remove #import line and replace it with WatchKit framework.

Objective-C iOS 6 delegate NSString query

I'm trying to use a delegate to pass a value from one VC to another. I think I'm am misunderstanding the way it is supposed to work.
In my main ViewController.h I have this:
#protocol defaultLocationChoice <NSObject>
- (NSString *) locChoice;
#end
In both my PreferencesViewController.h and ChooseServerViewController.h I have defaultLocationChoice declared in the #interface section and the property assinged like so:
#property (nonatomic, assign) id <defaultLocationChoice> locationDelegate;
Both are synthesized also.
When the user segues from PreferencesViewController to ChooseServerViewController the prepare for segue code is:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toServerChoice"]) {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.locationDelegate = self;
}
}
When a cell choice is made in ChooseServerViewController I call:
[self locChoice];
Which is:
- (NSString *) locChoice {
NSLog(#"Cell Vale Loc choice %#",cellValue);
return cellValue;
}
The NSLog verifies the correct value is returned.
Now, as I think I understand it, the value of LocChoice in the delegate is now the value returned, no?
When the user goes back (NavController) the PreferencesViewController has:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
defaultLocation = [locationDelegate locChoice];
[self.tableView reloadData];
}
I was expecting the value of defaultLocation to now equal the value passed to locChoice. However when the table reloads the cell in question is still blank, implying what I exepct to happen isn't happening.
Any ideas?
If I followed your code properly, you do not need to adopt the mentioned protocol in your ChooseServerViewController, only PreferencesViewController.
The reasoning is you want to send data back to the previous view controller. Try:
#protocol defaultLocationChoice <NSObject>
- (void) locChoice:(NSString*)choice;
#end
Have your PreferencesViewController implement that method so it receives the selection. You will have to store that in an appropriate instance variable.
// in PreferencesViewController.m
-(void)locChoice:(NSString*)choice {
self.choice = choice; // this just my example
}
When the choice is made (in ChooseServerViewController) to send the choice back, call:
// this is in 'ChooseServerViewController.m' some where appropriate
[self.delegate locChoice:cellValue];
Your implementation is simply doing nothing with cell value (not even storing it, just logging it). When you return to PreferencesViewController, you will now have the selected value and that view controller can what it wants with it.
Protocols are somewhat analgous to interfaces in Java or C#, but more flexible.
Some more concepts about delegation.
Working with Protocols.
UPDATE:
The declaration for ChooseServerViewController should look like:
#import "FileWithProtocolDecalration.h"
#interface ChooseServerViewController
.
.
.
#property ( nonatomic,assign) id<defaultLocationChoice> delegate;
.
.
.
#end
I think you do have some misunderstanding there
protocol and delegates are something that is passed around. in other words somebody need to be receiver and somebody need to be the sender. in your case.
update your protocol to this
#protocol defaultLocationChoice <NSObject>
- (void)locChoice:(NSString *)updateString; // the method from delegate and implementer must be exact
#end
and set implement the protocol to ViewController as a receiver
#interface VCReceiver : UIViewController <defaultLocationChoice>
then later in VCReceiver
- (void)viewDidLoad {
ChooseServerViewController *vcSender = [[ChooseServerViewController alloc] init];
[vcSender setLocationDelegate:self]; // this is like saying. i have implemented method from protocol in this file (self)
}
- (void)locChoice:(NSString *)updateString {
// update the VCReceiver here
// or access vcSender value
// or use the updateString value
}
then in ChooseServerViewController locChoice: method (the one from your example) replace with this one and call [self updateChoice] instead:
- (void)updateChoice {
if ([self.locationDelegate respondsToSelector:#selector(locChoice:)]) {
[self.locationDelegate locChoice:aStringToUpdate]; // this will call VCReceiver locChoice
}
it does not have to return anything because it is actually calling the VCReceiver method to tell it that ChooseServerViewController got the value ready to be read.

performSelectorOnMainThread method not call

I have created a method that is running in new thread.
[NSThread detachNewThreadSelector:#selector(setmostpopularReq:) toTarget:self withObject:mostPopulerstring];
After completed this method i send all data to main thread.
[self performSelectorOnMainThread:#selector(getmostpopularResponse:) withObject:self waitUntilDone:YES];
But some time my main thread method not calling.
i used
dispatch_sync(dispatch_get_main_queue(),^{[self getmostpopularResponse:mostPopularList];});
But this is also have the same problem some time its calling method or some time not calling.
Please help me in this.
I would advise you to create a delegate with which you could notify the main thread after the
completion of the detached thread
Also another solution would be to create an NSOperation and NSOperationQueue instead of a new thread. There you can schedule what you want. For me looks easier, though it depends on you.
Here is a link to help you more with NSOperation
https://developer.apple.com/library/mac/#featuredarticles/ManagingConcurrency/_index.html
I will write this really quickly.
#protocol RespondDelegate
- (void)notifyWithRespond:(NSData *)data;
#end
#interface ContactWebServiceOperation:NSOperation
#property (nonatomic, assign) id delegate;
#end
#implementation ContactWebServiceOperation
#synthesize delegate;
// initialize here.
- (id)initWithDelegate:(id)delegate;
{
if ([self = [super init]) {
self.delegate = delegate;
}
return self;
}
- (void)main
{
if (self.isCancelled) return;
if (nil != delegate) {
// Do your work here...
work();
// When finished notify the delegate with the new data.
[delegate notifyWithRespond:your_data_here];
// Or
[delegate performSelectorOnMainThread:#selector(processImageForDownloadOperation:)
withObject:self waitUntilDone:YES];
}
}
#end
// Now on the view that you want to present the received results
// you have to do one thing.
// Let's say that your view is called View1
#interface View1 : UIViewController<RespondDelegate>
// Here put whatever you like.
#end
#implementation View1
// Put here all your code.
- (void)notifyWithRespond:(NSData *)data
{
// Here you will handle your new data and you will update your view.
}
#end
If I understand correct this should work.
Also, you can change the NSData to whatever you like, as long as you perform the appropriate conversions later.
If it doesn't work take a look on the link from Apple, maybe I have some typo or something.
But in general it looks solid.

Do NSURLConnections belong in my model or controller?

Say I'm using a JSON or XML API to get data about my projects from a URL using an asyncronous NSURLConnection, parsing that into an NSMutableArray and then populating an NSTableView.
I have a model: Project
I have a controller: TableViewController (acting as table data source and delegate)
Where should I put the code that starts the request and parses the result into a NSMutableArray.
Should I have:
1:
A method inside Project called -(NSMutableArray* ) getAllProjects and call this from my Controller.
Or 2:
Should I enumerate an NSMutableArray of Project* objects, called for instance ProjectsArray* inside my Controller; each time calling [[Project alloc] init]?
Option 1 makes much more sense to me because I might want to get all the projects from multiple controllers and this would save repeating code, I only need to call a public method inside my Project model. In this case would I be doing lots of [[self alloc] init] statements? Is this ok? Also my model would need to be an NSURLConnection delegate. Is this correct?
No doubt it must be in your Model.
Reason :
Because you will be requiring to update it many times from different controllers, you can use KVO for that in future.
From my experience, I think the good way is to have the parsing routines in the model (ProjectsArray) and connection stuff in another class, which initiates the connection and returns raw NSData (through a delegate for example), which you pass to the model to parse it. This way your model or viewController won't have multiple roles.
As for calling [[Project alloc] init] each time you need the data—you can use static reference in the model class and then getting it by something like - (ProjectsArray *)instance
/*
* UITableViewController has the word "controller" in it but that
* does not make it a controller... it's a view. Pure and simple.
*
* /This/ is a controller...
*/
#implementation MyController
#synthesize data; // assume readonly #property in interface
-(void)fetchData {
NSURLConnection *connection;
// Set up URL connection, etc. Speaking very loosely, and
// lossing over some important threading details...
NSURLResponse *response = GetResponse();
NSError *__autoreleasing error;
#autoreleasepool {
// build objects. premature optimization is the root of all evil,
// but if you're really worried about too much allocation, you
// can resist duplication with custom logic in parse().
self.data = parse([response data], &error);
}
if (data == nil) {
// Error notification
}
else { // Success notification
NSNotification *didFetch = [NSNotification notificationWithName:#"didFetch" object:self.data userInfo:nil];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:didFetch waitUntilDone:NO];
}
}
#end
#interface MyTableViewController ()
#property (unsafe_unretained) MyController *controller;
#property (strong, nonatomic) NSArray *dataView;
#end
#implementation MyTableViewController
#synthesize controller = _controller;
#synthesize dataView = _dataView;
-(void)viewDidLoad {
_controller = [MyController controller]; // singleton
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateData:)
name:#"didFetch"
object:nil];
}
-(IBAction)buttonPress {
[_controller fetchData]; // again, I'm glossing over threading details...
}
-(void)updateData {
// Controller owns the data, we just get a view of it here.
self.dataView = [[_controller data] arrayOfSomeSort];
[self.view reloadData];
}
#end

Delegates query in Objective - C

So, I have a decent idea of what a delegate does, why use it, how to implement it etc. and I'm working on implementing it in one of my projects. The problem I'm trying to solve is to decouple my Controller objects from my Network Access class. In this context, the ideas get a little messy in my head.
I somehow intuitively feel that the NetworkAccessClass should be the delegate for a Controller object, because the NetworkAccessClass is acting as a helper for the Controller object. But it seems to work in a reverse fashion, because the following is apparently the right way to do it:
NetworkaccessClass.h
#protocol NetworkAccessDelegate
-(void) requestSucceded:(NSData *) data
-(void) requestFailed:(int) responseCode;
#end
#interface NetworkAccessClass : NSObject
{
id<NetworkAccessDelegate> networkDelegate;
}
#property(nonatomic, assign) id networkDelegate;
-(void) initWithDelegate:(id) delegate; //
#end
NetworkAccessClass.m
#implementation
#synthesize networkDelegate
-(void) initWithParams:(id) delegate
{
networkDelegate = delegate;
// Assign GET/POST vals, create request etc
[request startAsynchronous];
}
-(void) requestSucceded:(ASIHTTPRequest *) request
{
if([networkDelegate respondsToSelector:#selector(requestSucceded:)]) {
// Send the data to the controller object for it to use
...
}
}
-(void) requestFailed:(ASIHTTPRequest *) request
{
// Same as above. Send to request failed.
}
#end
And finally in my FirstViewController.h
#import "NetworkAccessClass.h"
#interface FirstViewController<NetworkAccessDelegate>
{
}
-(void) requestSucceded:(NSData *) data;
-(void) requestFailed:(int) responseCode;
#end
And the same in SecondViewController.h and so on.
Although this does decouple my Controllers from my Network class, I can't help feel it's wrong because the controllers in this case are acting as delegates or helper methods to the Network Class and not the other way round. Am I missing something basic? Or is this how it is?
Thanks,
Teja.
Delegates aren't "helper methods". Think of them as objects that get notified when something happens. (Although don't confuse them with "Notifications"--that's a different thing entirely.) In this case, your network class does it's stuff and then calls its delegate method on the View Controller that instantiated and fired, it to report the contents of that response to the view controller. The controller will then, presumably, update the view with the data that the network connector got. Classic delegate pattern, right there.