I have a class with the following property and method:
header file below - note I did not copy/paste all code (only pertinant information):
#interface SQLiteDB : NSObject
#property (nonatomic, strong) NSMutableArray *allAccountsArray;
#property (nonatomic, strong) NSString *accountId, *accountName, *accountDescription, *accountTags, *accountPhoto, *accountCreationDate;
+(id) populateAccountObjectWithId:(NSString *)id andName:(NSString *)name andDescription:(NSString *)description andTags:(NSString *)tags andPhoto:(NSString *)photo andCreationDate:(NSString *)creationDate;
#end
implementation file below - note I did not copy/paste all code (only pertinant information):
+(id) populateAccountObjectWithId:(NSString *)id andName:(NSString *)name andDescription:(NSString *)description andTags:(NSString *)tags andPhoto:(NSString *)photo andCreationDate:(NSString *)creationDate
{
SQLiteDB *mySQLiteDB = [[self alloc] init];
mySQLiteDB.accountId = id;
mySQLiteDB.accountName = name;
mySQLiteDB.accountDescription = description;
mySQLiteDB.accountTags = tags;
mySQLiteDB.accountPhoto = photo;
mySQLiteDB.accountCreationDate = creationDate;
return mySQLiteDB;
}
Then, another method in the implementation file fetches all accounts from the SQLite database:
-(id) fetchAccountList
{
// do some database stuff here
// create prepared statement, open database, etc...
allAccountsArray = [[NSMutableArray alloc] init];
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *thisAccountId = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement,0)];
NSString *thisAccountName = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
NSString *thisAccountDescription = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
NSString *thisAccountTags = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 3)];
NSString *thisAccountPhoto = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)];
NSString *thisAccountCreationDate = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 5)];
[allAccountsArray addObject:[SQLiteDB populateAccountObjectWithId:thisAccountId andName:thisAccountName andDescription:thisAccountDescription andTags:thisAccountTags andPhoto:thisAccountPhoto andCreationDate:thisAccountCreationDate]];
}
// error handling code, etc.
// finalize, & close code here...
return allAccountsArray;
}
Now finally the question. In other classes I want to do stuff with the array of objects that this returns. For instance I would do this in a TableVeiw controller:
-(void)loadView
{
[super loadView];
mySQLiteDB = [[SQLiteDB alloc] init];
allAccountsArray = [mySQLiteDB fetchAccountList];
}
I would use this later to for instance populate the table list in the cellForRowAtIndexPath method. Perhaps each cell of the table would contain the accountName, accountDescription, and accountCreationDate. I do not however know how to access that name, desc, date from within the array of objects...
This obviously produces an error:
cell.textLabel.text = [allAccountsArray objectAtIndex:indexPath.row];
because the object at "row" is an "object" containing name, desc, date, etc...
So Stackoverflow, I ask you... How do I accomplish getting the object variables at each element of the array?
You should be able to do something as simple as this:
SqliteDB *mySqliteDB = (SQliteDB *)[allAccountsArray objectAtIndex:indexPath.row];
NSString *myText = mySqliteDB.thisAccountID;
myText = [myText stringByAppendingString:mySqliteDB.thisAccountName];
.... etc.
cell.textLabel.text = myText;
I think enumerateObjects:usingBlock: is what you want for iterating, i.e. enumerating, objects. You might have missed it because it's in the superclass.
Related
I am new to Objective-C. I am trying to create a weather app where I parsed data from open weather map. I have stored the parsed data to an array. Now want to access the array value from other class but getting null value.
Can anyone help me?
What I have tried:
Here is my NSObject class where I am storing data and trying to send that to view controller:
- (void)getCurrentWeather:(NSString *)query
{
NSString *const BASE_URL_STRING = #"http://api.openweathermap.org/data/2.5/weather?q=";
NSString *const API_KEY = #"&APPID=APIKEYSTRING";
NSString *weatherURLText = [NSString stringWithFormat:#"%#%#%#",
BASE_URL_STRING, query,API_KEY];
NSURL *weatherURL = [NSURL URLWithString:weatherURLText];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:weatherURL];
[self performSelectorOnMainThread:#selector(fetchedDataSmile | :) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSString* cityName = [json objectForKey:#"name"];
int currentTempCelsius = (int)[[[json objectForKey:#"main"] objectForKey:#"temp"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int maxTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_max"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int minTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_min"] intValue] - ZERO_CELSIUS_IN_KELVIN;
NSString *weatherDescription = [[[json objectForKey:#"weather"] objectAtIndexBlush | :O ] objectForKey:#"description"];
weatherArray = [[NSMutableArray alloc] initWithObjects:cityName, weatherDescription,
[NSString stringWithFormat:#"%d", currentTempCelsius],
[NSString stringWithFormat:#"%d", maxTemp],
[NSString stringWithFormat:#"%d", minTemp],nil];
I have NSObject.h file as:
#interface WeatherData : NSObject
#property (nonatomic) NSString *weatherDescription;
#property (strong, nonatomic) NSString *currentTemp;
#property (nonatomic) int maxTempCelsius;
#property (nonatomic) int minTempCelsius;
#property (nonatomic, retain) NSMutableArray *weatherArray;
- (void)getCurrentWeather:(NSString *)query;
#end
In my view controller:
.h file:
#property (nonatomic, retain) NSMutableArray *weatherResultArray;
.m file:
-(void)searchButtonClicked:(UIButton*)sender
{
[self.view endEditing:YES];
WeatherData *weather = [[WeatherData alloc] init];
[weather getCurrentWeather:_textField.text];
self.weatherResultArray = weather.weatherArray;
//temperatureLabel.text = [NSString stringWithFormat:#"%d°",weather.currentTempCelsius];
}
I just want to show the results in UILabel.
Have you tried returning NSMutable array in this method
- (NSMutableArray*)getCurrentWeather:(NSString *)query
instead of this,
- (void)getCurrentWeather:(NSString *)query
This would be the easiest way to verify and also value can be retrieved in single statement as:
self.weatherResultArray = [weather getCurrentWeather:_textField.text];
One more thing, Don't forget to allocate and initialise your weatherResultArray as:
self.weatherResultArray = [[NSMutableArray alloc]init];
In NSObject class, define a weather protocol.
//NSObject.h file
#protocol WeatherDelegate<NSObject>
-(void)getWeatherData:(YourNSObjectClass*)viewController getWeatherData:(NSMutableArray*)array;
#end
//NSObject.m file, in
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSString* cityName = [json objectForKey:#"name"];
int currentTempCelsius = (int)[[[json objectForKey:#"main"] objectForKey:#"temp"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int maxTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_max"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int minTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_min"] intValue] - ZERO_CELSIUS_IN_KELVIN;
NSString *weatherDescription = [[[json objectForKey:#"weather"] objectAtIndexBlush | :O ] objectForKey:#"description"];
weatherArray = [[NSMutableArray alloc] initWithObjects:cityName, weatherDescription,
[NSString stringWithFormat:#"%d", currentTempCelsius],
[NSString stringWithFormat:#"%d", maxTemp],
[NSString stringWithFormat:#"%d", minTemp],nil];
id<WeatherDelegate> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(getWeatherData:getWeatherData:)])
{
[strongDelegate getWeatherData:self getWeatherData:weatherArray];
}
}
In yourViewController class,Add this WeatherData protocol and add the delegate function in .m file to fetch the data.
#interface yourViewControllerClass()<WeatherDelegate>
{
YourNSObjectClass *nsClass;
NSMutableArray *dataArray;
}
-(void)getWeatherData:(YourNSObjectClass*)viewController getWeatherData:(NSMutableArray*)array{
dataArray = [[NSMutableArray alloc]initWithArray:array];
}
-(void)searchButtonClicked:(UIButton*)sender
{
[self.view endEditing:YES];
WeatherData *weather = [[WeatherData alloc] init];
[weather getCurrentWeather:_textField.text];
self.weatherResultArray = dataArray;
//temperatureLabel.text = [NSString stringWithFormat:#"%d°",weather.currentTempCelsius];
}
My array is not saving the values I put in it...
I am defining my nsmutablearray *arrayClientList in .h file
#interface StartupTableViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property NSMutableArray *arrayClientList;
#property BOOL boolAddToClient;
//#property (strong, nonatomic) NSMutableArray *arrayAddClient;
#end
in .m file I am initializing like so
- (void)viewDidLoad {
[super viewDidLoad];
//initialize variables
self.arrayClientList = [[NSMutableArray alloc] init];
arraySelectedInformation = [[NSMutableArray alloc] init];
self.boolAddToClient = NO;
NSString *tstring = #"hello";
[self.arrayClientList addObject:tstring];
but then once I get to another method in this same class... the array is nil again. I must be doing something stupid for the array not to hold the values
-(void)viewDidAppear:(BOOL)animated{
//NSLog(#"appeared");
if (self.boolAddToClient) {
NSLog(#"add client to list");
self.boolAddToClient = NO;
[self.tableView reloadData];
}
else{
NSLog(#"startup");
}
}
I am trying to use it in another class
- (IBAction)buttonSubmit:(id)sender {
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
//check to make sure user filled out all fields
if (![userDescription isEqual:#""] && ![userUsername isEqual:#""] && ![userPassword isEqual: #""]){
NSLog(#"correct");
NSArray *arrayVC = self.navigationController.viewControllers;
StartupTableViewController *parentViewController = [arrayVC objectAtIndex:0];
parentViewController.boolAddToClient = YES;
NSMutableArray *arrayNewObjects = [[NSMutableArray alloc] initWithObjects:userDescription, userUsername, userPassword, nil];
NSMutableArray *tarray = parentViewController.arrayClientList;
[tarray addObject:arrayNewObjects];
[parentViewController.arrayClientList addObject:arrayNewObjects];
[self.navigationController popViewControllerAnimated:YES];
}
else{
NSLog(#"something missing");
}
}
Since I can't comment without rep, I must try with answer.
Try this:
In ViewDidLoad do alloc init with Strings you create in implementation and also change if block to this:
#implementation
{
NSString *userDescription;
NSString *userUsername;
NSString *userPassword;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
}
- (IBAction)buttonSubmit:(id)sender {
if (self.textfieldDescription.text.lenght != 0 && self.textfieldUserID.text.lenght != 0 && self.textfieldPW.text.lenght != 0) {
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
....... and the rest
}
Please comment if it's not working, and I also think that you're not passing the informations right. Try searching an answer on how to pass arrays between TableViewControllers. Good Luck!
I have a variable usuario of type TuplaUsuario. When I insert its data, I want to initialize NSMutableArray cups to an Array of type TuplaCups. How can I do it?
Here is my code up to date:
TuplaUsuario:
//TuplaUsuario.h:
#interface TuplaUsuario : NSObject
{
NSMutableString* mensaje;
NSString* usuario;
NSString* password;
NSMutableArray* cups;
//More variables
}
//Property for each variable
- (id)initWithString:(NSString *)identifier;
//TuplaUsuario.m:
//Synthesize for each variable
- (id)initWithString:(NSString *)identifier {
if ( self = [super init] ) {
mensaje = [[NSMutableString alloc] initWithString:identifier];
usuario = [[NSString alloc] initWithString:identifier];
password = [[NSString alloc] initWithString:identifier];
cups = [[NSMutableArray alloc] init];
}
return self;
}
TuplaCups:
//TuplaCups.h:
#interface TuplaCups : NSObject {
NSString* cups;
NSString* tarifa;
NSString* direccion;
NSString* nombreUsuario;
NSMutableArray* facturas;
}
//Property for each variable
- (id)initWithString:(NSString *)identifier;
//TuplaCups.m:
//Synthesize for each variable
- (id)initWithString:(NSString *)identifier {
if ( self = [super init] ) {
cups = [[NSMutableString alloc] initWithString:identifier];
tarifa = [[NSString alloc] initWithString:identifier];
direccion = [[NSString alloc] initWithString:identifier];
nombreUsuario = [[NSString alloc] initWithString:identifier];
facturas = [[NSMutableArray alloc] init];
}
return self;
}
Arrays (as in NSArray and NSMutableArray) in Objective-C are not typed, they can hold any object type and you can't easily restrict what's going in. So you are responsible for ensuring that only objects of the type you want go in. Usually, this isn't a problem unless you expose the mutable array so that other objects might put stuff into your array. In this case, it's better to provide accessors:
- (void)addFoo:(Foo *)foo {
[myMutableArray addObject:foo];
}
- (void)removeFoo:(Foo *)foo {
[myMutableArray removeObject:foo];
}
// Return an array of Foo instances.
- (NSArray *)foos {
return [[myMutableArray copy] autorelease];
}
Objective-C doesn't have a concept of Generics like C# or C++ do. There are no templates after all. You just have to know what type of objects you've put into an array. If you are going to intermix them, you can use isKindOfClass: to test for the class.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I was having problem allocating the NSMutableArray in the player class, there is no warning in the compile time when I tried this way in the (id)init method: playerInventory = [[NSMutableArray alloc] init]; But when I run the program, it gives me an error (EXC_BAD_ACCESS). I have checked over and over, but found nothing help. I am new to objective c and I don't have much knowledge in memory management in this language. But seemed that I cannot find the answer after my best effort.
the .h file
#interface Player : NSObject
{
int playerLevel;
int playerExp;
NSMutableArray *playerInventory;
NSString *playerName;
BOOL isLastPlayerExist;
NSData *xmlData;
GDataXMLDocument *_xmlDoc;
}
#property (assign,readwrite) int playerLevel;
#property (retain,readonly) NSString *playerName;
#property (assign,readonly) BOOL isLastPlayerExist;
#property (retain,readwrite) NSMutableArray *playerInventory;
+(Player *)currentPlayer;
-(void)loadXMLFile;
-(void)loadRecentPlayer;
-(void)loadPlayerStats;
-(void)loadPlayerInventory;
-(void)releaseXMLFile;
#end
the .m file , deleted irrelevant implementations.
#implementation Player
static Player *sharedInstance = nil;
#synthesize playerLevel;
#synthesize playerName;
#synthesize isLastPlayerExist;
#synthesize playerInventory;
-(id) init{
self = [super init];
if (self != nil){
playerInventory = [[NSMutableArray alloc] init];
[self loadXMLFile];
[self loadRecentPlayer];
[self loadPlayerStats];
}
return self;
}
-(void) loadXMLFile{
NSString *filePath = [self dataFilePath: NO];
xmlData = [[NSMutableData alloc] initWithContentsOfFile:filePath];
NSError *error;
_xmlDoc = [[GDataXMLDocument alloc] initWithData:xmlData
options:0 error:&error];
}
-(void) loadRecentPlayer{
NSArray *playerInfo = [_xmlDoc.rootElement elementsForName:#"Player"];
GDataXMLElement *currentName = (GDataXMLElement *)[playerInfo objectAtIndex:0];
playerName = currentName.stringValue;
NSLog(#"%#",playerName);
}
-(void) loadPlayerStats{
NSString *xPath;
NSArray *playerInfo;
xPath = [NSString stringWithFormat:#"//Users/Player[Name = \"%#\"]/Level",playerName];
playerInfo = [_xmlDoc.rootElement nodesForXPath:xPath error: nil];
GDataXMLElement *level = (GDataXMLElement *)[playerInfo objectAtIndex:0];
playerLevel = level.stringValue.intValue;
xPath = [NSString stringWithFormat:#"//Users/Player[Name = \"%#\"]/Experience",playerName];
playerInfo = [_xmlDoc.rootElement nodesForXPath:xPath error: nil];
GDataXMLElement *exp = (GDataXMLElement *)[playerInfo objectAtIndex:0];
playerExp = exp.stringValue.intValue;
xPath = [NSString stringWithFormat:#"//Users/Player[Name = \"%#\"]/Inventory/CardID",playerName];
playerInfo = [_xmlDoc.rootElement nodesForXPath:xPath error: nil];
for(id obj in playerInfo){
GDataXMLElement *card = (GDataXMLElement *)obj;
[playerInventory addObject: card.stringValue.intValue];
NSLog(#"%d",card.stringValue.intValue);
}
NSLog(#"%#",playerInventory);
}
+(Player *)currentPlayer{
if (sharedInstance == nil){
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (NSString *)dataFilePath:(BOOL)forSave {
return [[NSBundle mainBundle] pathForResource:#"userData" ofType:#"xml"];
}
#end
change
#synthesize playerInventory = _ playerInventory;
and allocate like following
_playerInventory = [[NSMutableArray alloc] init];
In my app, I have list of contacts which are displayed in ascending order.When user clicks on any alphabet say 'b' then the list should scrolls to the contact starting from 'b'.Is this built-In functionality of AddressBook?Can anyone knows how I can achieve this?
Thanks in advance!
My pretty dirty method. It sorts by email, first name and last name omitting middle name cause I didn't needed that one. Oh and it finds only those contacts which have email address. You can avoid that if you slightly edit code starting with if (ABMultiValueGetCount(emailRef))
Your view controller:
- (NSArray *)sortedContactsFromPeople:(CFArrayRef)people {
NSMutableArray *contacts = [NSMutableArray array];
for (int i = 0; i < CFArrayGetCount(people); i++) {
ABRecordRef record = CFArrayGetValueAtIndex(people, i);
ABMultiValueRef emailRef = ABRecordCopyValue(record, kABPersonEmailProperty);
CFStringRef email;
if (ABMultiValueGetCount(emailRef)) {
BOOL hasValidEmail = NO;
for (int j = 0; j < ABMultiValueGetCount(emailRef); j++) {
if (!hasValidEmail) {
email = ABMultiValueCopyValueAtIndex(emailRef, j);
if ([Validator validateEmail:(NSString *)email] == kValNoErr)
hasValidEmail = YES;
else
CFRelease(email);
}
}
if (hasValidEmail) {
CFStringRef name = ABRecordCopyValue(record, kABPersonFirstNameProperty);
CFStringRef lastname = ABRecordCopyValue(record, kABPersonLastNameProperty);
NSData *contactImageData = (NSData*)ABPersonCopyImageData(record);
UIImage *img = [[[UIImage alloc] initWithData:contactImageData] autorelease];
[contactImageData release];
if (lastname == nil)
lastname = (CFStringRef)#"";
if (name == nil)
name = (CFStringRef)#"";
Contact *contact = [[[Contact alloc] initWithName:(NSString *)name
lastname:(NSString *)lastname
email:(NSString *)email
profileIcon:img] autorelease];
if (![(NSString *)lastname isEqualToString:#""])
contact.sortChar = [(NSString *)lastname substringToIndex:1];
else if (![(NSString *)name isEqualToString:#""])
contact.sortChar = [(NSString *)name substringToIndex:1];
else if (![(NSString *)email isEqualToString:#""])
contact.sortChar = [(NSString *)email substringToIndex:1];
contact.idNumber = ABRecordGetRecordID(record);
[contacts addObject:contact];
if (lastname)
CFRelease(lastname);
if (name)
CFRelease(name);
CFRelease(email);
}
}
CFRelease(emailRef);
}
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"sortChar" ascending:YES selector:#selector(caseInsensitiveCompare:)];
[contacts sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
return contacts;
}
- (void)initBaseValues {
sections = [[NSMutableDictionary alloc] init];
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
NSInteger section = 0;
NSString *prevChar = nil;
NSArray *contacts = [self sortedContactsFromPeople:people];
for (int i = 0; i < contacts.count; i++) {
Contact *contact = [contacts objectAtIndex:i];
BOOL sectionExists = NO;
if ([prevChar isEqualToString:contact.sortChar])
sectionExists = YES;
if (!sectionExists) {
[sections setObject:[NSMutableArray array] forKey:[NSString stringWithFormat:#"%d", section]];
section++;
}
[prevChar autorelease];
prevChar = [contact.sortChar copy];
[[sections objectForKey:[NSString stringWithFormat:#"%d", section-1]] addObject:contact];
}
if (prevChar != nil)
[prevChar release];
CFRelease(people);
CFRelease(addressBook);
}
Contact.h
#interface Contact : NSObject {
NSString *name;
NSString *lastname;
NSString *email;
UIImage *profileIcon;
NSInteger idNumber;
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *lastname;
#property (nonatomic, copy) NSString *email;
#property (nonatomic, retain) UIImage *profileIcon;
#property (nonatomic) NSInteger idNumber;
#property (nonatomic, copy) NSString *sortChar;
- (id)initWithName:(NSString *)name_
lastname:(NSString *)lastname_
email:(NSString *)email_
profileIcon:(UIImage *)profileIcon_;
#end
Doh! I wasn't vigilant enough, to read the whole thing carefully. :) Try creating NSMutableDictionary and each time headerForSection: method is being called store it's offset in the dictionary with appropriate letter as key. Then when user selects "B" letter send your UITableView setContentOffset:animated: method with appropriate offset taken from that dictionary.