I have an iOS app that pulls data from a server and persists it using CoreData. I have a UITableView that I am trying to populate with only select portions from a given core data attribute.
Before the table is populated I cycle through the data and pass what I want into a NSMutableArray. The problem is when I find an item I want it is not being added to the array.
I declare the array in my .h file like so...
#property (strong, nonatomic) NSMutableArray *theNewSource;
And Synthesize it in the .m file
#synthesize theNewSource = _theNewSource;
Here is the method...
-(NSMutableArray *)setDataSourceArray
{
for(int i = 0; i < rcount ; i++)
{
NSIndexPath *countingInteger = [NSIndexPath indexPathForItem:i inSection:0];
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:countingInteger];
NSString *action = [object valueForKey:#"theActionName"];
if (![action isEqual:#"Login"])
{
[_theNewSource addObject:action];
}
}
NSLog(#"the array is now %#",_theNewSource);
return _theNewSource;
}
I've set a breakpoint in the line [_theNewSource addObject:action]. I can see in the console that the variable action does have a value but it is never added to _theNewSource array... I'm sure this is Objective C 101 but I can't get it figured out. Please Help!
Have you even created your _theNewSource array? It seems like you haven't done the following:
_theNewSource = [[NSMutableArray alloc] init];
Make sure you are creating your instance before trying to use it.
You should use a predicate in the NSFetchedResultsController's fetchRequest directly:
[NSPredicate predicateWithFormat:#"theActionName != %#", #"Login"];
NSFetchResultsControllers are particularly useful for driving table views and collection views, so filtering their results to create a separate data source is a code smell.
Doing it this way means that you can use the NSFetchedResultsController directly as the data source for your table instead of using it to create a filtered array to act as the datasource.
Related
I have searched extensively through SO and other sites for an answer... maybe I am missing something and/or don't understand the key/value coding structure of an NSDictionary properly?
I am programmatically preparing a UITableViewController in a custom controller class. I have two NSArrays that are built up in a fixed structure (of my choosing) by an iterative process from data persisted using Core Data.
PROBLEM: I want to combine two NSArrays into an NSDictionary so that I can use it in a UITableView and insert the appropriate data in the tableView:cellForRowAtIndexPath: data source method using NSIndexPath references.
This is my view controller hierarchy...
TVC --> inherits core data MOCs and sets global properties;
TVC_List --> runs FRC to populate any list within a series of polymorphic table view controllers;
TVC_Detail --> prepares a detail view for review/edit for a selected NSManagedObject from one of these lists;
TVC_ContactDetail --> displays a detail view for review/edit for the selected NSManagedObject of type "Contact";
TVC_AddressMethod --> takes the data from one entity relationship already held with strong reference for the selected NSManagedObject "Contact" with the intention of displaying in a custom built table view.
Yes I have considered using a separate FRC. The greater app uses a FRC for every other TVC through structured controller polymorphism and heirarchy.
I have attempted using a standalone FRC for this special case controller, however it does not seem to provide the flexibility I need regarding the special case data preparation I choose to use in this case. I have learned a lot about FRC and table views and their benefits and limitations but again, maybe I am missing something?
To explain in more detail...
I have a custom NSManagedObject ("DD_AddressMethod") that is passed to a controller. The custom NSManagedObject contains the data for contact information for the "contact".
For example it includes the following (Core Data model) entity attributes/relationships:
#property (nonatomic, retain) NSString *data;
#property (nonatomic, retain) DD_Contact *contact;
#property (nonatomic, retain) DD_Type *type;
#property (nonatomic, retain) DD_TypeChild *typeChild;
Note: the three that commence with "DD_" are relationships to other custom NSManagedObjects.
I am able to programmatically construct the dynamic UITableView as per the following screenshot (for the iOS Contact "Daniel Higgins"). I am not able to properly allocate the data into the correct section:row:.
NOTE: to be clear, this screenshot is provided to detail the table view structure, however note that the data assigned to the labels is incorrect. It is for reference only.
To continue my detailed description, the custom NSManagedObject is assigned to a strongly referenced variable.
From that I obtain two dynamically constructed NSArrays...
Array No.1 : The section headers (which are extracted from the entity relationship "type"):
Printing description of mutableArraySectionHeaders:
<__NSArrayM 0x7b65e610>(
Telephone,
Facsimile,
Email
)
This dynamically constructed array provides the data to set values for my numberOfSectionsInTableView: and tableView:titleForHeaderInSection: data source methods.
Array No.2 : The number of rows per section:
Printing description of mutableArrayRowNumbers:
<__NSArrayM 0x7b661ef0>(
2,
1,
1,
0
)
This dynamically constructed array provides the data to set the value for my tableView:numberOfRowsInSection: data source method.
REITERATE PROBLEM: What I want to achieve is the combination of these two dynamically constructed arrays (or similar arrays) into a dynamically constructed NSDictionary that can be used to populate cells within the tableView:cellForRowAtIndexPath data source method.
For example, something that creates a dictionary so that this line of code in my tableView:cellForRowAtIndexPath data source method will work correctly...
cellData = self.dictionaryOfTwoArrays[indexPath];
I understand that the NSDictionary will be constructed of pairs. In this particular example the two arrays have a different number of elements, so for this particular example the dynamically constructed dictionary will need to drop the 0 from the Array No.2. But for another contact that for example does not have a "Facsimile" contact type, there might only be two pairs to insert into the dynamically constructed dictionary.
For information, note that the data itself is described as:
Printing description of mutableArrayAddressMethods:
<__NSArrayM 0x7b65d010>(
<DD_AddressMethod: 0x7b6e2140> (entity: AddressMethod; id: 0x7b644280 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/AddressMethod/p3> ; data: {
data = "555-478-7672";
contact = "0x7b73b7a0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Contact/p1>";
type = "0x7b6d62c0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Type/p60>";
typeChild = "0x7b6a7670 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/TypeChild/p54>";
}),
<DD_AddressMethod: 0x7b6d6530> (entity: AddressMethod; id: 0x7b6d3dc0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/AddressMethod/p4> ; data: {
data = "(408) 555-5270";
contact = "0x7b73b7a0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Contact/p1>";
type = "0x7b6d62c0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Type/p60>";
typeChild = "0x7b6436a0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/TypeChild/p23>";
}),
<DD_AddressMethod: 0x7b63f260> (entity: AddressMethod; id: 0x7b6abc20 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/AddressMethod/p1> ; data: {
data = "(408) 555-3514";
contact = "0x7b73b7a0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Contact/p1>";
type = "0x7b6b72d0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Type/p108>";
typeChild = "0x7b6a7670 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/TypeChild/p54>";
}),
<DD_AddressMethod: 0x7b64e890> (entity: AddressMethod; id: 0x7b6b4480 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/AddressMethod/p2> ; data: {
data = "d-higgins#mac.com";
contact = "0x7b73b7a0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Contact/p1>";
type = "0x7b6dc6e0 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/Type/p9>";
typeChild = "0x7b6a7670 <x-coredata://C17E9176-4105-488B-86DA-3A94638E7FAF/TypeChild/p54>";
})
)
Not really sure if I've got question right, but anyway, maybe just try to combine all those models in two dimensions array, e.g. your helper (view-model) class might look like that:
#interface Helper
#property (nonatomic, strong) NSArray *sections; // 2D array which contains arrays
#end
#implementation Helper
- (NSInteger)numberOfSections {
return self.sections.count;
}
- (NSInteger)numberOfRowsInSection:(NSInteger)section {
return [self.sections[section] count];
}
- (MVVMModel *)modelAtIndexPath:(NSIndexPath *)indexPath {
return self.sections[indexPath.section][indexPath.row];
}
#end
and afterwards:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
id model = [self.helper modelAtIndexPath:indexPath];
...
}
If an FRC is out of the question, then this is the "brute force attack", so to speak, for your stated problem:
NSMutableDictionary *dictionaryOfTwoArrays = [NSMutableDictionary new];
NSInteger arrayIndex = 0;
for (NSInteger sectionIndex = 0; sectionIndex < [mutableArrayRowNumbers count]; sectionIndex++) {
NSInteger rowCount = [mutableArrayRowNumbers[sectionIndex] integerValue];
for (NSInteger rowIndex = 0; rowIndex < rowCount; rowIndex++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
dictionaryOfTwoArrays[indexPath] = mutableArrayAddressMethods[arrayIndex];
arrayIndex++;
}
}
NSLog(#"dictionaryOfTwoArrays is %#",dictionaryOfTwoArrays);
You will obviously need to amend or rebuild this dictionary whenever the underlying mutable arrays change. Also, note that your Array No.1 is irrelevant for these purposes, though you can still use it to populate the section titles.
The other option some have suggested in comments is an array of arrays. That could be constructed along these lines:
NSInteger arrayIndex = 0;
NSMutableArray *sections = [NSMutableArray new];
for (NSInteger sectionIndex = 0; sectionIndex < [mutableArrayRowNumbers count]; sectionIndex++) {
NSInteger rowCount = [mutableArrayRowNumbers[sectionIndex] integerValue];
NSMutableArray *sectionRows = [NSMutableArray new];
for (NSInteger rowIndex = 0; rowIndex < rowCount; rowIndex++) {
[sectionRows addObject:mutableArrayAddressMethods[arrayIndex]];
arrayIndex++;
}
[sections addObject:sectionRows];
}
NSLog(#"Array of arrays is %#",sections);
You would then have to unpack your data with something like this:
NSArray *rowArray = sections[indexPath.section];
addressMethod = rowArray[indexPath.row];
I have an array - placeObjectsArray, that hold a lot of objects called place. Place is object of class PlaceHolder, in which i create different properties, filled with data:
self.place = [[PlaceHolder alloc]init];
// A lot of code here during parson XML with data
[self.placeObjectsArray addObject:self.place];
Header of this file look like this:
#interface PlaceHolder : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *webPage;
It actually a container for an entity, each one hold data for name, description, image links etc. At now, i have array with place objects. What i want to, to manipulate with that objects inside an array. For example, how could i find all of data for specific "name"? (Name is one of properties in PlaceHolder class). How could i make an array that contain only names? How could i see in console 10 random "description"?
Any advice would be appreciated, thanks!
You're asking a bunch of separate questions.
First, how to select items in your array that match a particular name: Create an NSPredicate and use filteredArrayUsingPredicate. Google NSPredicate and you should find lots of examples.
Alternately you could use indexesOfObjectsPassingTest to get an index set of the items in the array that match your search criteria, and then use objectsAtIndexes: to turn the index set into a sub-array.
As for how to get all the names from the entries in your array, you can use a very cool trick in key value coding.
If you send an array the valueForKey message, it tries to fetch an item from each entry in the array using that key, and return them all in a new array. The code would look like this:
NSArray *names = [placeObjectsArray valueForKey #"name"];
Fetching 10 random descriptions is a little more complicated. You would need to write code that loops through the array, selecting 10 random items, and appends the description of each one into a new mutable array.
The trick there is to use arc4random_uniform to get a random index in your array:
NSUInteger random_index = arc4random_uniform(placeObjectsArray.count);
I leave the rest to you as a learning exercise.
If you want to fetch 10 random descriptions and make sure you never fetch the same description twice it's more complicated. You need to create a mutable copy of your array, then loop through the copy, fetching a random item, adding it's description to an array, and deleting the item from the array.
You can use NSPredicates:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name LIKE[cd] %#", nameSearch];
NSArray *filtered = [self.placeObjectsArray filteredArrayUsingPredicate:predicate];
You could iterate over your array looking for the PlaceHolder with a given name, like:
PlaceHolder *namedPlaceholder = nil;
for (PlaceHolder *placeholder in theArray) {
if ([placeholder.name isEqualToString:"whateverName"]) {
namedPlaceholder = placeholder;
break;
}
}
If you want to find PlaceHolders by name efficiently you might consider using a dictionary instead of an array. With a dictionary you can map names to objects, like:
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
myDictionary[#"foo"] = somePlaceholder;
myDictionary[#"bar"] = someOtherPlaceholder;
and retrieve them like:
PlaceHolder *somePlaceholder = myDictionary[#"foo"];
To get random objects from an array, I recommend getting random indexes using arc4random_uniform. This gives pseudo-random numbers with a better uniform distribution than rand or random, and does not require you to explicitly seed the sequence with srand or srandom.
PlaceHolder *randomPlaceholder = theArray[arc4random_uniform(theArray.count)];
or
const NSUInteger arrayCount = theArray.count;
for (NSUInteger j = 0; j < 10; j++) {
PlaceHolder *randomPlaceholder = theArray[arc4random_uniform(arrayCount)];
// Do something with randomPlaceholder.
}
I have a list of items showing up on a table view.
Every item has its properties such as name, pic, rank etc'.
My goal is, every time the user selects a row the item with its properties will be added to a new list.
I've created a new list called listOfBugs and because i want it to be global i've allocated and initialized it inside viewDidLoad. (Is that a proper thing to do?)
Here is my code:
MasterViewController.h
#interface MasterViewController : UITableViewController
{
NSMutableArray *listOfBugs;
}
#property (strong) NSMutableArray *bugs;
MasterViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
listOfBugs = [[NSMutableArray alloc]init];
self.title = #"Scary Bugs";
}
...
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:bug.data.title delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[messageAlert show];
[listOfBugs addObject:bug];
NSLog(#"this is %#",listOfBugs);
}
Using NSLog I can see that the objects are added:
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
2012-12-05 17:45:13.100
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
I have a few questions.
1.How can I access the properties of the objects inside of the array listOfBugs ?
Update: This worked for me:
NSLog(#"this is %#",((ScaryBugDoc *)[listOfBugs objectAtIndex:0]).data.title);
But I can't access the listOfBugs from another class.
I turned it into a property as suggested to make my life easier but still can't access it from another class.
For example in listOfBugsViewController.m return [_listOfBugs count]; will give me the error Use of undeclared identifier '_listOfBugs'
2.I want to be abale to populate a table view with the customized list, how can i do that?
After accomplishing that I would like to save the list as a plist and also add and remove objects from it at ease so I need to take that under consideration.
This is the code that I'm based on, I only made a few adjustments to create the new list
This is really two questions:
1) How do I make my property a public property which can be accessed by other classes?
You do this just like you did with your bugs property. Add this to your .h file:
#property (strong) NSMutableArray *newList;
Note that if you aren't using different threads, you can make it a little more efficient by using the nonatomic property as well (#property (nonatomic, strong)).
Once you do that, you don't need your iVar declaration because it will automatically be generated for you. (i.e. you can remove NSMutableArray *newList;.)
2) How do I access an object in an array?
Objects in an array are stored as an id object, meaning that it is a "generic" object. If you know what type of object is stored, then you need to tell the compiler what it is so that it knows what properties and methods are appropriate for that class. You do this by casting the variable to the proper type:
ScaryBugDoc *bug = (ScaryBugDoc *)[self.newList objectAtIndex:0];
Then, you can access the properties of the object, assuming that they are public (as covered in point 1 above) like this:
NSLog(#"this is %s", bug.data.tile);
Okay, so based from the comments, this should work:
Album* tempAlbum = [albumList objectAtIndex:i];
//now you can access album's properties
Song* tempSong = [album.songs objectAtIndex:j];
//now you can access song's properties
This can be simplified down to:
Song* someSong = [((Album)[albumList objectAtIndex:i]).songs objectAtIndex:j];
When returning an object from an NSArray, or a collection object like that it will return a generic id object. This will need to be typecasted to the expected object so you can access the right properties.
I am wondering what the correct way is to make a copy of an object defined in the app delegate or a singleton object. In short, I am making an app which requires a user to login. This login view is just a modal view controller on top of the 'real' app, which consists of a tabbarcontroller, plus some tableview controllers. After a successful login, there is send a data request to a remote server, and the modal view controller is dismissed, revealing the tabbar controller and table views holding the XML data. To parse the incoming data, I have created a singleton object named DataParser, which has interface
...
#interface DataParser : NSObject {
// Data objects that hold the data obtained from XML files
NSMutableDictionary *personnel;
NSMutableDictionary *schedule;
NSMutableDictionary *today;
}
#property (nonatomic, retain) NSMutableDictionary *personnel;
#property (nonatomic, retain) NSMutableDictionary *schedule;
#property (nonatomic, retain) NSMutableDictionary *today;
...
Now in these dictionaries I store (mutable) dictionaries and arrays holding NSString objects with the parsed XML data. Since I do not want to modify these original objects holding the parsed data (that is to say, I only want to modify them at the login stage, but not in any of the tableview controllers), I am creating a new dictionary object which holds a copy of the content of one of the dictionaries above in each tableview controller. So for instance, in the loadView of a view controller called ScheduleViewController I have
...
#interface ScheduleViewController : UITableViewController {
NSDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [NSDictionary dictionaryWithDictionary:sharedSingleton.schedule];
}
...
Now this seems to work fine. The only difficulty arises however, when the user 'logs out', which entails popping the login modal view controller back on the stack. When the user presses the login button again, then a new XML data request is send to the server and the dictionaries in the singleton object get refreshed with the (new) data (I check if they contain any data, if so I call removeAllObjects before filling them up again with newly parsed data). At this point the dictionaries in all view controllers should be updated too, however I am not quite sure how to go about this the right way. I have noticed that loadView is not always called again in this case and so to this end I have added the same code as above in loadView to every viewWillAppear method. After navigating back and forth between the different views or navigating back and forth between child views of a tableview a couple of times, I receive an EXC_BAD_ACCESS error however. I suspect this has to do with not properly retaining the copies of the original dictionaries, but I don't seem to be able to find a solution around this. Instead of using dictionaryWithDictionary, which I suspect is not the right way to go anyway, I also tried a different approach, where instead of using objects of type NSDictionary in ScheduleViewController I use NSMutableDictionary. So:
...
#interface ScheduleViewController : UITableViewController {
NSMutableDictionary *copyOfSchedule;
}
#property (nonatomic, retain) NSMutableDictionary *copyOfSchedule;
...
#end
#implementation ScheduleViewController
#synthesize copyOfSchedule;
- (void)loadView {
[super loadView];
DataParser *sharedSingleton = [DataParser sharedInstance];
self.copyOfSchedule = [[NSMutableDictionary alloc] initWithDictionary:sharedSingleton.schedule];
}
- (void)viewWillAppear {
DataParser *sharedSingleton = [DataParser sharedInstance];
[self.copyOfSchedule removeAllObjects];
[self.copyOfSchedule addEntriesFromDictionary:sharedSingleton.schedule];
[self.tableView reloadData];
}
...
But this doesn't get rid of the EXC_BAD_ACCESS errors. To make a very long story short: what would be the best way to go about making independent copies of objects defined in a singleton object or app delegate and which can be dynamically updated at request? Since I am already rather into the project and lots is going on, I realize that my question may be a bit vague. Nonetheless I hope there is somebody who could enlighten me somehow.
Deep copies are often made recursively. One way to do it would be to add -deepCopy methods to NSDictionary and NSArray. The dictionary version might go like this:
- (NSDictionary*)deepCopy
{
NSMutableDictionary *temp = [self mutableCopy];
for (id key in temp) {
id item = [temp objectForKey:key];
if ([item respondsToSelector:#sel(deepCopy)] {
// handle deep-copyable items, i.e. dictionaries and arrays
[temp setObject:[item deepCopy] forKey:key]
}
else if ([item respondsToSelector:#(copy)]) {
// most data objects implement NSCopyable, so will be handled here
[temp setObject:[item copy] forKey:key];
}
else {
// handle un-copyable items here, maybe throw an exception
}
}
NSDictionary *newDict = [[temp copy] autorelease];
[temp release]
return newDict;
}
I haven't tested that, so be a little careful. You'll want to do something similar for NSArray.
Note that views are not copyable.
It is quite a typical pattern that you build an array or dictionary with some code, so clearly it must be mutable while you add bits to it, and when you're done you don't want it ever to change. To do this:
Have a property like
#property (...) NSArray* myArray;
When you calculate the contents of myArray, use a mutable array to build it, like
NSMutableArray* myMutableArray = [NSMutableArray array];
When you're done building the array, just use
self.myArray = [NSArray arrayWithArry:myMutableArray];
I am polling an HTTP API - it returns one item at a time, in real-time (about every 4 seconds). As each item is received, I would like a new UITableView cell to be populated. The full list of received items must remain in a class property, I'm guessing an NSMutableArray. What is the best way to initialize an NSMutableArray as a class property, update it as new information comes in, and then use the count to update a new UITableViewCell?
Here's how I'm adding content to an NSMutableDictionary:
NSMutableDictionary *messageContents = [[NSMutableDictionary alloc] init];
[messageContents retain];
[messageContents setValue:messageText forKey:#"text"];
[messageContents setValue:image forKey:#"image"];
[self addMessageToDataArray:messageContents];
Here's the method stuffing objects into the array:
- (void)addMessageToDataArray:(NSArray *)messageDictionary {
[self.messageDataArray addObject:messageDictionary];
NSLog(#"count = %#", [self.messageDataArray count]);
[self reloadTableData];
}
At this point, calling count on the messageDataArray class property crashes the application. I'm very used to working with arrays in Actionscript, Obj-C is obviously totally different. Please explain the method for instantiating an NSMutableArray as a class property, filling it with NSMutableDictionary's and then finding the NSMutableArray count (which will be dynamically updating in real-time) so I can use that info to update a UITableView (on the fly).
Or... tell me I'm being silly and suggest a much easier solution.
From your description I would guess you're not allocating the messageDataArray before using it.
The init function for your table view (controller?) class should have a line like this
messageDataArray = [[NSMutableArray alloc] initWithCapacity:20];
It's also worth checking that you have [messageDataArray release]; in your dealloc method.