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
Related
Now I am developing an iOS application which works like this:
User scans QR code,
App searches for a specific key - > value,
it gives out a value to the user.
Currently I have two ViewControllers - the main and "value" ViewController, which is inherited from main. The problem is that if I create NSDictionary in main VC it is not visible in "value" VC. Main VC gives only the string (QR code, the key) through the segue. So, the value VC has to search for key and display the value.
What I ask is some kind of global variable or one DataSource visible across the whole app. Of course, I can implement NSDictionary initialisation inside value ViewDidLoad method and it will work, but this is not the point. New modules are to be added there and the variable has to be global. I googled a lot and got the idea that singleton pattern can be helpful here. I tried to implement it, but no idea how to do. Do I need it, or it is too complex for this kind of DataSource?
Thank you!
The basic idea is, you will still need to #include the header file of the place where this dictionary will be. The solution that Naveen proposes means that you will be including the header for the app delegate wherever you want to access it. Whether to use the app delegate for this purpose or not is kinda grayish. Some people often do this, some say its a bad use of it.
The singleton approach means that you will create a class, that will always contain the same information since the init method will return object that was previously created.
For the singleton aproach, imagine I have a database manager class. So in the header of this class (the DatabaseManagerSingleton.h) ill have this:
#interface DatabaseManager : NSObject
+ (DatabaseManager*)sharedInstance;
// Your dictionary
#property (nonatomic,strong) NSMutableDictionary* someDictionary;
The implementation will look like this: (check how "sharedInstance" initializes the object)
#implementation DatabaseManager
#pragma mark - Singleton Methods
+ (DatabaseManager*)sharedInstance {
static DatabaseManager *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// Custom initialization
_someDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
Now, a VERY important thing is that, any place you want to use this object should first include the header:
EDIT: To use it in your code:
1) add the header
#import "DatabaseManager.h"
2) initialize the object
DatabaseManager *databaseManager = [DatabaseManager sharedInstance];
3) do whatever you need
// Initialize the dictionary
databaseManager.someDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"OBJECT",#"someKey", nil]; // In this case the object is just a NSString.
// Access
[databaseManager.someDictionary objectForKey:#"someKey"];
Put as a property on Appdelegate
#property (nonatomic,strong) NSDictionary * sharedData;
Access anywhere like
NSDictionary *sharedData= ((APPDelegate *) [UIApplication sharedApplication].delegate).sharedData;
So I have a chunk of code that does some magic with a query and gives me a variable called JSONresult which has been told to run in it's own thread. Once I get this result, I would like take that variable from that thread and make it so that when an IBAction method is called on (triggerd by a button press) if data in the variable JSONresult exists that it will display it in an NSTextView.
So far I've been able to get the data to appear in an NSTextView if I specify the data manually within the IBAction method, but trying to use the JSONresult variable doesn't work for obvious reasons.
Here is what I've got so far.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSThread detachNewThreadSelector:#selector(getJSONquery) toTarget:self withObject:nil];
}
-(void)getJSONquery {
[[NSThread currentThread] setName:#"Get JSON Query"];
//Some fancy stuff here
NSArray *JSONresult = [string componentsSeparatedByString:#"<|...|>"];
}
After all this has happened and I've now got my JSONresult I have an IBAction setup so that if the button is pushed, the JSONresult will be taken and displayed in a NSTextView
-(IBAction)showContent:(id)sender {
[content setString:JSONresult];
}
How would I go about passing the variable JSONresult between the two threads/methods?
Thanks in advance! :)
Also if I should update this post with the header file please let me know. I'm new to the world of Objective C and have a very bad understanding of the general how-to so I don't know whether it's relevant to this situation or not
You don't really pass data between threads, as all threads have access to the same data within a process, however you need synchronize access to the data so that multiple threads don't trample over the shared data, which can lead to its corruption.
In your case, however I don't think their is much synchronization required so your question isn't about passing data between threads, it's more to do with how to make the JSON result available to the button method.
As has already been answered by #Leandros, you would normally do this using a private property:
YourClass.m:
#interface YourClass ()
#property NSArray *jsonResult; // Note: strong, atomic
#end
...
-(void)getJSONquery {
[[NSThread currentThread] setName:#"Get JSON Query"];
//Some fancy stuff here
self.jsonResult = [string componentsSeparatedByString:#"<|...|>"];
}
You can create a property.
// You can add this to the .h file.
#property (atomic, strong) NSArray *JSONresult;
// Or this above the #implementation in your .m file.
#interface YourClass()
#property (atomic, strong) NSArray *JSONresult;
#end
- (void)getJSONquery {
[[NSThread currentThread] setName:#"Get JSON Query"];
//Some fancy stuff here
self.JSONresult = [string componentsSeparatedByString:#"<|...|>"];
}
- (IBAction)showContent:(id)sender {
if (self.JSONresult) {
[content setString:self.JSONresult];
}
}
Update with working code. Problem was like #HotLinks state, that I did init instead of initWithBaseURL:url
I am using a Singleton in my App, based on this guide.
Now every time I use the singleton I do like this:
SingletonClass* sharedSingleton = [SingletonClass sharedInstance];
[sharedSingleton callAMethod];
// or
[[SingletonClass sharedInstance] callAMethod];
Is there a way to use a short syntax, especially if I have to use the Singleton several times? Something like:
[sc callAMethod];
I tried already this kind, but it did not work, as the init method was not called...
WebApi.h
#import "AFHTTPRequestOperationManager.h"
#import "SingletonClass.h"
#interface WebApi : AFHTTPRequestOperationManager
#property (nonatomic, strong) SingletonClass *sc;
+(WebApi*)sharedInstance;
-(void)sandbox;
#end
WebApi.m (updated with working code)
#import "WebApi.h"
#implementation WebApi
//-(WebApi*)init {
-(WebApi*)initWithBaseURL:url {
self = [super init];
if (self != nil) {
self.sc = [SingletonClass sharedInstance]; // is never called.
}
return self;
}
#pragma mark - Singleton methods
/**
* Singleton methods
*/
+(WebApi*)sharedInstance
{
static WebApi *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:kApiHost]];
});
return sharedInstance;
}
-(void)sandbox {
DLog(#"test short singleton call: %#", [sc callAMethod];
}
#end
Debug Message
[WebApi sandbox] [Line 42] test short singleton call: (null)
I don't see how you can do this in any language. In Java, you would generally see
<Class>.getInstance().<blah>.
There's nothing stopping you from getting that instance inside a method where it will be used a lot, e.g.
WebApi *api = [WebApi sharedInstance];
then a whole lot of:
[api <method1>];
...
Does that get you there?
(Amusingly, a developer and I were discussing this issue yesterday because the example code Apple has for use of the accelerometer puts the motion manager in the app delegate and the syntax to get a hold of the manager is completely insane:
CMMotionManager *mManager = [(APLAppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
As you can see, they are making a local var and then diddling that from there on in the controller class.
You could declare a global variable and set it in your +sharedInstance method, then make sure you call +sharedInstance once.
But, really, don't bother. Using [SomeClass sharedInstance] makes it easy to quantify all uses of shared instances in your code base, as well as all uses of SomeClass's class level API. Both are quite useful for anyone else that ends up maintaining your code.
Secondly, it doesn't really save that much typing. Not enough to justify requiring everyone to learn about a new global.
(What Rob said):
Finally, if you are calling instance methods on the shared instance repeatedly in a scope, just use a local variable:
ThingManager *thingManager = [ThingManager sharedInstance];
[thingManager foo];
[thingManager bar];
[thingManager baz];
You can do it this way:
In .h file
#interface WebApi : AFHTTPRequestOperationManager
#property (nonatomic, strong) SingletonClass *sc;
...
+(id) methodName;
...
#end
In .m file
+(id) methodName
{
return [[WebApi shareInstance] instanceMethod];
}
- (id) instanceMethod
{
return #"SMTH";
}
I'm writing a REST API layer with AFNetworking for an iOS project. I have some questions about what I have written so far, so I'm turning to stackoverflow for some guidance/answers.
Here are the guidelines of what I'm trying to achieve:
A base class (DRAPI : AFHTTPClient) that will initialize a singleton client, just as the cocoadocs for AFHTTPClient recommend.
A "base" delegate for DRAPI: DRAPIDelegate, that holds methods for displaying errors in an unified format.
Subclasses of DRAPI that deal with certain routes of my REST API. For example, CRUD operations on Users are responsability of DRUserAPI, which is a child class of DRAPI.
Each subclass of DRAPI has its own delegate. For DRUserAPI, there's DRUserAPIDelegate, which extends DRAPIDelegate.
Here is a quick example of how things are written so far:
DRAPI.h
#interface DRAPI : AFHTTPClient
- (void) apiGetCallWithRoute:(NSString*)route
parameters:(NSDictionary*)parameters
onSuccess:(void(^)(id))successBlock
onError:(void(^)(NSArray* errors))errorBlock;
#end
#protocol DRAPIDelegate <NSObject>
-(void) DRAPIErrorFromServer:(NSArray*)errors;
#end
DRAPI.m
#implementation DRAPI
+(DRAPI*) sharedClient
{
static DRAPI *aSharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&once_token, ^{
_sharedClient = [DRAPI alloc] initWithBaseURL:[NSURL URLWithString:#"http://127.0.0.1:3000/api"];
});
return aSharedClient;
}
-(id) initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
// configuration goes here... skipping because it is not important.
}
return self;
}
#pragma mark - Helper methods for Server Calls
- (void) apiGetCallWithRoute:(NSString*)route
parameters:(NSDictionary*)parameters
onSuccess:(void(^)(id))successBlock
onError:(void(^)(NSArray* errors))errorBlock
{
[[DRAPI sharedClient] getPath:route
parameters:addAuthenticationParametersTo(parameters)
success:^(AFHTTPRequestOperation *operation, id responseObject) {
successBlock(responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
errorBlock( processError() );
}];
}
#end
DRUserAPI.h
#interface DRUserAPI: DRAPI
#property (weak, nonatomic) id<DRUserAPIDelegate>delegate;
+(DRUserAPI*) APIWithDelegate:(id<DRUserAPIDelegate>)delegate;
-(void) createUser:(NSString*)username password:(NSString*)password;
// ... more methods would be declared here...
#end
#protocol DRUserAPIDelegate <NSObject, DRAPIDelegate>
-(void) DRUserAPIOnUserCreated:(MyUserModel*)newUser;
// more delegate methods would be here...
#end
DRUserAPI.m
#implementation DRUserAPI
#synthesize delegate;
+(DRUserAPI*) APIWithDelegate:(id<DRUserAPIDelegate>)delegate
{
DRUserAPI * client = [DRUserAPI new];
client.delegate = delegate;
return client;
}
-(void) createUser:(NSString*)username password:(NSString*)password
{
[self apiGetCallWithRoute:#"users/create"
parameters:#{#"username" : username, #"password": password}
onSuccess:^(id response) {
NSDictionary *parsedJSON = response;
[delegate DRUserAPIOnUserCreated:[MyUserModel newModelFromDictionary:parsedJSON];
}
onError:^(NSArray *errors) {
[delegate DRAPIErrorFromServer:errors];
}];
}
#end
A fellow co-worker brought to my attention that delegates and singletons do not mix. I still want to manage delegates, though. I'm thinking that good solution would be pass the singleton instance of the delegate to the method I'm calling inside the API subclass.
Is this a good idea?
Thanks!
I prefer an implementation based on composition instead of subclassing, even if the AFNetworking docs recommend to subclass AFHTTPClient.
I would inject the AFHTTPClient in the DRAPI and this one in the DRUserAPI, making both of them simple subclasses of NSObject. A flat design is cleaner IMO and it makes it easier to unit test your classes.
Instead of having singletons you can create an injector class responsible of creating your whole object graph, calling it only in your application delegate.
For this you should use a block based API instead of delegates since you would only have one instance of DRAPI and you don't want to set its delegate before any call to it (you could have another class like DRUserAPI where you inject the DRAPI instance).
It's not perfect, but it works. Why so many delegates? It seems that you moving to a infinite loop of singletons. I think you should stop...
I have an array "myArr" which contains objects of custom class..e.g. objs of type MyClass
I need to share this array across multiple classes..
Could you please help me with the exact code that I should be using..
I have referred Singleton patter on Apple and other references, but it is all ver confusing to me...So it will be great if you could highlight the things/code that I need to add.
I recommend that you read up on object delegation.
#property (nonatomic,copy) NSArray *myArr;
On your other classes, implement a delegate object that will point to this class, then you could use:
NSArray *retrievedArray = [self.delegate myArr];
Edit: If you are interested to use only Singleton i believe it would be something along this way:
static MyClass *obj = nil;
On your class with the array, create a class method to return a Singleton object
+(MyClass*) sharedInstance {
if (obj) {
obj = [[self alloc]init];
}
return obj;
}
On your other classes you could just use
NSArray *retrievedArray = [[MyClass sharedInstance] myArr];
to get back the array.
Cheers.
I’d stay away from singletons. As the array is some kind of model (in the Model–View–Controller sense), other classes should depend on it:
#interface ControllerA : UIViewController {}
#property(retain) NSArray *array;
#end
#interface ControllerB : UIViewController {}
#property(retain) NSArray *array;
#end
Now the question changes into “how do I supply the depency.” You can do this using Interface Builder or supply the array when the depending objects are built. For example I sometimes have a method called setupObjectGraph in my application delegate that creates class instances and connects them together:
- (void) setupObjectGraph
{
mainController = [[MainController alloc] init…];
[mainController setThis…];
[mainController setThat…];
OtherController *bar = [[OtherController alloc] init…];
[bar setThis…];
[bar setThat…];
[mainController setBar:bar];
[bar release];
…
}
- (void) applicationDidFinishLaunchingOrWhatever
{
[self setupObjectGraph];
[window addSubview:[mainController view]];
[window makeKeyAndVisible];
}
This is not perfect (it does not scale very well), but it works for many applications and it’s much better than singletons. This sounds like a trifle issue, but it affects your overall design a lot, so it makes sense to think it through.