Create variables programmatically - objective-c

I want to change the number of tableviews depending on my input, which will change depending on the user and i need to be able to reference these tableviews in my data source, as they should retain different data depending on the tableview. I figured this might be done if I could create a unique variable for each of the created tableviews, but i would need to be able to reference these variables through my whole class?

You can create an NSMutable Array and add UITableView in them directly . You can make create global variable store using static reference and access it globally .
#interface Singleton : NSObject {
NSMutableArray *TableArray;
}
+ (Singleton *)instance;
#end
#implementation Singleton
+ (Singleton *)instance {
static Singleton *instance;
#synchronized(self) {
if(!instance) {
instance = [[Singleton alloc] init];
}
}
return instance;
}
The other alternative would be to include NSMutableArray in you appdelegate and access it throughout your application globally.
Creating objects dynamically is not a problem .
you can create as many UITableview's and add them to an NSMutableArray using.
UITableview *temp = [UITableview alloc] initWithFrame : ... ];
temp.delegate = self ;
tableArray.addObject(temp);
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath *)indexPath{
if(tableView = [tableArray objectAtIndex:x]{
// do this
}
else if (select appropriate table view from array ){
}
//do this for the rest
}
What you are facing is an design issue . For other alternatives please refer Apple Guides for Objects Communication

Why not create an NSMutableArray member variable to store them all? When you need to change the data source, set the specific tableview to the data source. I'm not sure of the details of what exactly you're trying to do, but adding them dynamically and changing the data source shouldn't be a problem.

Related

One NSDictionary visible everywhere in application

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;

How to set AND get my array to access throughout my app using a class?

I have an app that needs to access customers from the DB. I've got the array of data but I need to share it in a few views within my app.
I created this class called Customers but I'm not sure how to call and set/get my NSMutableArray of customers.
Is there a good example or maybe a code snippet that someone can show me?
#import "Customers.h"
#implementation Customers
static NSMutableArray *customers;
// I need to set/access the customers array class from all views.
+ (NSMutableArray *)allCustomers
{
if !(customers)
{
customers = [NSMutableArray array];
}
return customers;
}
#end
I recommend you read about the singleton pattern. Using the singleton pattern, you make sure a class is initialized once, and persisted. In that way, you can easily reach this class from anywhere, and in that way get and set its array from any class.
Singletons in obj-c: http://www.galloway.me.uk/tutorials/singleton-classes/
It would look something like this:
interface:
#property (nonatomic, strong) NSMutableArray *customers;
+ (Customers *)sharedCustomers;
implementation:
+ (Customers *)sharedCustomers
{
static Customers *sharedCustomers;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedCustomers= [[Customers alloc] init];
});
return sharedCustomers;
}
Then from anywhere, by importing "Customers.h", you can get and set the array.
Getting:
[[Customers sharedCustomers] customers];
Setting:
[[Customers sharedCustomers] setCustomers:...];
Seems like you are using the class object as a singleton, to grant access to a file-private variable.
You could go ahead and add (class) methods to:
Read the database from file/network or wherever it is
Search the array
e.g.
+ (id) customerAtIndex:(NSUInteger) index
{
return [customers objectAtIndex:index];
// (perhaps you can add a bounds check)
}
+ (void) insertCustomer:(id) customer atIndex:(NSUInteger) index
{
[customers insertObject:customer atIndex:index];
// (perhaps you can add a bounds check)
}

How to populate a NSTableView?

I am currently populating an NSTableView through the controller (MVC design pattern) where I initialise one entry of a NSMutableArray in the controller's init method.
How would I:
Populate my NSMutableArray which is an array of Person objects
Should I populate the NSMutableArray in my mainViewDidLoad method of my base class instead? I have not found any examples or resources for this.
Model (Person.m)
#import "Person.h"
#implementation Person
#synthesize name;
#synthesize gender;
- (id)init
{
self = [super init];
if (self) {
name = #"Bob";
gender = #"Unknown";
}
return self;
}
- (void)dealloc
{
self.name = nil;
self.gender = nil;
[super dealloc];
}
#end
Controller (PersonController.m)
#import "PersonController.h"
#import "Person.h"
#implementation PersonController
- (id)init
{
self = [super init];
if (self) {
PersonList = [[NSMutableArray alloc] init];
// [personList addObject:[[Person alloc] init]];
//
// [personTable reloadData];
}
return self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [personList count];
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
Person *person = [personList objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
return [person valueForKey:identifier];
}
- (void)dealloc
{
[super dealloc];
}
#end
Base file (Main.h):
#import "Main.h"
#implementation Main
- (void)mainViewDidLoad
{
}
#end
How would I:
Populate my NSMutableArray which is an array of Person objects
Step 1: Create Person objects.
Step 2: Add them to the array.
Your commented-out code does exactly this, although you should probably create the Person separately in case you want to configure it (e.g., set its name).
Should I populate the NSMutableArray in my mainViewDidLoad method of my base class instead?
It doesn't really matter how far in advance of the user seeing your model you create it, but conceptually, it kind of smells to me. It doesn't have anything to do with the view, so I say it belongs in init.
Of course, if the main view—and every view in it—has already loaded, you'll need to tell the table view to reload your data to get it to show any changes you made to the array. Conversely, if you create the model before loading the view, you don't need to reload initially, because the table view will have already asked you for your model once.
Offhand, your commented out code in PersonController looks right. I am assuming that the NSTableRow has the right identifier. The only issue is that your person object is blank, so there are no strings to display. I bet what was happening is that your row was trying to display nil in the first row, and displayed empty strings instead. Does creating your person object, setting the name and gender fields, and then putting it into the NSMutableArray and calling reloadData work (basically, what your commented code does, except now you are providing some actual data to display)?
Well, I am assuming that your controller has an IBOutlet NSTableView *personTable property, which is bound in the interface builder?
Also, in the controller's interface the protocol should be declared, but again, since the controller's implementation has the appropriate methods, I am assuming you have this set up correctly too.
Another detail, have the table columns' identifiers been set correctly in the interface builder? From this example, it is not clear to me how the column identifiers relate to the Person's properties (name and gender). Shouldn't the array personList hold dictionary objects, where a dictionary's object is the person, and the dictionary's key maps to the column identifier that you set in interface builder?
Yet another technicality, a property's name (PersonList) should not start with a capital letter. Just a typo I think, the compiler should at least protest when you try to get the personList, using a lower-case p.

Is it true to use extern to access variables from other classes in Objective c?

i have a DetailViewController and a Messages class(this is a TableViewController class). I parse some web information in the first class and want to use some values amongst them in the second class. As i looked arround in here and google for a few haurs and by now i think i need to define some extern variables in my second class and initialize them with the objects of the first class.. I tried a few ways but all failed.
In my first clas i have an NSMutableArray variable called messID, in the second class i do this:
#import DetailViewController
.
.
extern NSMutableArray *myArray;
DetailViewController *myObject;
myArray=myObject.messID;
But i got the error below:
Undefined symbols for architecture i386:
"_myArray", referenced from:
-[messages tableView:cellForRowAtIndexPath:] in messages.o
What am i doing wrong and what can i do please can any one help?..
EDIT
I've imported needed class,
in the .h file of second class i use this:
#interface messages : UITableViewController{
BNT_1DetailViewController *myObject;
}
#property(retain, nonatomic)BNT_1DetailViewController *myObject;
And its .m file is:
...
#sysnthesize myObject;
viewDidLoad{
myObject=[[BNT_1DetailViewController alloc]init];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
// Return the number of rows in the section.
return myObject.mesID.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
//extern NSMutableArray *messID;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}NSLog(#" -> %#",myObject.mesID );
cell.textLabel.text= [myObject.mesID objectAtIndex:indexPath.row];
// Configure the cell...
// [tableView reloadData];
return cell;
}
I would not use extern for what you are trying to do, which means you are also defining some global variables (and trying to access them from the files where you use the extern keyword). For the records, anyway, the error you are getting depends on the fact that extern is just a directive that does not define your objects -- it simply declares them as defined elsewhere. So you should add in some .m file the proper definition for your objects, without forgetting to also initialize them at some point in time so that they point to meaningful objects.
A better way to do what you are trying to do is through public properties declared in your first class allowing access to the NSMutableArray from the second class.
An example of this could be:
#interface DetailViewController : ... {
NSMutableArray* array;
}
#property (...) NSMutableArray* array;
#end
#interface Messages : ... {
DetailViewController* detailViewController;
}
...
#end
#implementation Messages;
...
... detailViewController.array...
...
#end
This solution is better because it does not use global variables and has a better encapsulation.
Actually, you could try and define a Model (like in model-view-controller) which contains all of your data and have them shared among all the controllers that need it. This would be an better approach.
EDIT: on how to connect one controller to another...
Say that at some point you create a new controller:
messageDetail *detailViewController = [[messageDetail alloc] initWithNibName:#"messageDetail" bundle:nil];
Now, detailViewController needs retrieving its data from messages, which is by the way the controller which is creating it. You have several options for doing that. An easy one is having a public property in messageDetail that links to messages:
#interface messageDetail : ... {
....
}
#property (nonatomic, retain) messages* messagesRef;
...
#end
(don't forget to synthesize this property in messageDetail implementation).
Then, when you instantiate messageDetail, you simply do this:
messageDetail *detailViewController = [[messageDetail alloc] initWithNibName:#"messageDetail" bundle:nil];
detailViewController.messagesRef = self;
Once you do this, your detailViewController will have a pointer, correctly initialized, to point to the other controller.
If your other controller exposes (like I explained above) a property with the NSArray, your are done.

Singleton - Reading from a Data Store

I'm trying to do the right thing by not using global variables in my Xcode project. I've successfully created a singleton. I have a simple data store which I named "myContactsStore" that contains an array, with each object in the array having only 3 instance variables (name, phoneNum, and eMail). I have no problem creating, modifying, saving etc. the data in the array when I'm executing the view controller that created the array.
My problem is trying to access the data store from another view controller. I'm halfway there, as proven by my ability to print the contents of the entire test array from another view controller by using the following code in a for loop:
NSLog(#"%#", myContactsStore.description);
Here's the output:
"Mary, 0938420839, PaulDoe#Mac.com",
"John, 9932097372, PaulDoe#Mac.com",
"Mary, 0726756893, RedCat#iwon.com",
"Mary, 8556327199, xxxbct#mac.com",
"John, 0640848317, xxxbct#mac.com"
How do I access just one instance variables? For example, I want to create a read-only array in another view controller that contains just the email addresses of every contact in the "myContactsStore" array. I've tried several things, but I'm new at this and I must be missing something very basic.
Thanks for you help and any code example you might have the time to include.
While Singletons are an easy way to share data across classes they cause cohesion problems in your overall design where your classes start to "import the world". The issue is coming up with exactly the right dependency for a given class and this is very easily missed when you try to design from classes instead of designing from use cases. You want to ask yourself, "what data does this class use?" You then create a protocol (abstraction) that gives the class the view of the data it wants. In your case one class writes to the data store. It doesn't need to create the data store nor does either class need to know that the data is maintained in an array. Follow these steps exactly in order and follow directly or you'll miss my point. Try a protocol in a separate .h file like the following:
#protocol MyDataStore <NSObject> {
}
Import this header in your 1st class that creates the contacts and declare a property of the protocol's type.
#import "MyDataStore.h"
#interface MyContactCreator : UIViewController
#property (nonatomic, retain) id<MyDataStore> dataStore;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore;
#end
I threw in a custom init method in case you are currently instantiating your view controllers programmatically instead of via InterfaceBuilder. In your implementation you would do something like this:
#implementation MyContactCreator
//other methods...
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.dataStore = aDataStore;
}
return self;
}
//other methods...
#end
If you are using Interface Builder to create your view controller you can drag a custom object into play and call it something like "MyDataStoreImpl". The idea here is that you are giving the datastore to the view controller instead of it creating it directly and knowing about it. Also you want to defer worrying about how the data store works until you really need to. Later in your view controller where you create the contacts you would use the data store to create them. Assuming the info comes from standard screen elements you would write code like this:
-(void) addContactTapped:(id)sender
{
[self.datastore createContactWithName:txtNameField.text phoneNumber:txtPhoneField.text email:txtEmailField.text];
}
Your editor would scream at you (with little red marks like what your 2nd grade teacher would use on your spelling homework) because the datastore doesn't respond to the message you are sending. You go back and add that method to the protocol:
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
}
In your other view controller class that wants the list of email addresses you would import the same data store protocol. You would also declare a datasource property identical as what we did above using a complimentary custom init method or Interface Builder to pass the datasource in. This view controller (assuming it's a table view controller) would probably have some methods like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.datasource numberOfContacts];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *email = [self.datasource emailForContactNumber:indexPath.row];
UITableViewCell *cell = //create tableview cell with the email string
return cell;
}
Your editor will start screaming with the little red lines and all. This is where you go and add more methods to the protocol.
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
-(NSInteger) numberOfContacts;
-(NSString*) emailForContactNumber:(NSInteger)index;
}
The little red lines go away and finally you can begin thinking about how the contacts are stored and retrieved. Create a separate class called MyDataStoreImpl which extends NSObject and imports and follows the "MyDataStore" protocol. Fill out implementations of all of the methods and you should be up and running. It could be as simple as storing NSDisctionary objects containing the contact info in an internal NSMutableArray property.
- (id)init {
self = [super init];
if (self) {
self.allContacts = [NSMutableArray array];
}
return self;
}
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
{
NSDictionary *newContact = [NSDictionary dictionaryWithObjectsAndKeys:
aName, #"name", aPhoneNumber, #"phone", anEmail, #"email",
nil];
[self.allConstacts addObject:newContact];
}
-(NSInteger) numberOfContacts;
{
return [self.allContacts count];
}
-(NSString*) emailForContactNumber:(NSInteger)index;
{
[[self.allContacts objectAtIndex:index] valueForKey:#"email"];
}
The advantages here are many. You can later re-implement the datasource to read/write from a plist file, network server, or database without touching any of your controllers. Also, your app will be easier to optimize for performance because you can design the read write methods to pull directly from a source instead of naively copying data from one array to another as you would if you were worrying about how it is managed too early. All of the above thrown together without testing and likely has errors but given to illustrate a point of how to properly share data between controllers without Singletons while maintaining a testable and easily maintainable codebase.
You can stick this to any class to make it a singleton:
+(MySingleton *)singleton {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
shared.someVar = someValue;
});
return shared;
}
-(void) dealloc {
abort();
[someVar release];
[super dealloc];
}
Better yet, you can add the class as an ivar of the application delegate, which is a singleton you already have. You can get a reference to it like this:
// AppDelegate or whatever name
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then you access the datastore as an ivar on that delegate, and optionally implement one of the several persistence technologies available on iOS, basically plist files through direct file access or NSCoding or even NSUserDefaults (which you shouldn't but it's handy for small tasks), Core Data, or SQLite.
If you are using this data store class only to pass data between controllers, you can do so directly instead. Example:
CitySelectionVC *citySel = [[CitySelectionVC alloc] initWithCities:self.cities];
[self.navigationController pushViewController:citySel animated:TRUE];
[citySel release];
I always use SynthesizeSingleton.h by Mike Gallagher. He has a really informative article relating to this topic here. You should really check it out. It makes the creation of Singleton classes really easy.
Assuming your myContactsStore object has a method contactsArray which returns the internal NSArray object, you can do this:
NSArray *emails = [myContactsStore.contentsArray valueForKey:#"email"];
NSLog(#"output: %#", emails);
Which should output:
output: (
"PaulDoe#Mac.com",
"PaulDoe#Mac.com",
"RedCat#iwon.com",
"xxxbct#mac.com",
"xxxbct#mac.com"
)
You simply have to enumerate through the array and retrieve the data you want. Like so:
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:myContactsStore.count];
for (ContactClassName *contact in myContactsStore) {
NSString *email = contact.eMail; // I'm assuming your ivar eMail is also a property
if (email) [emails addObject:email];
}
You now have an NSMutableArray containing just the list of emails. If you want to make this list immutable simply do:
NSArray *emailList = [emails copy];