I'm having a lot of difficulty getting core data to work in my application. I'm perfectly comfortable with core data in a table view controller and even with core data in a single view application. I'm unable however to get my program to function properly in with multiple view controllers.
I've read through Zarra's core data book and bought pro core data for iOS and have gone through the projects listed but every one of them is used in a TableView controller. with the exception of the shapes application in pro core data for iOS.
Does anyone know of any examples (code or tutorials) that would demonstrate how to do a program with multiple view controllers and core data?
What I would like to do is have buttons on the first (instead of tableview cells) that will segue to the the next viewController. On the second view controller I would like that information populated with information from the set of the first entity,
so I have something like this so far where the first entity is:
House
houseName (attribute)
occupants (relationship)
Person
personName (attribute)
household (relationship)
occupants <-->>household (one to many )
{
...
int i = //house selected on previous view controller;
NSManagedObject *people = [[self sortOccupants] objectAtIndex:i];
textField01.text = [NSString stringWithFormat:#"%#",[[people valueForKey:#"personName"]description]];
}
the sort occupants looks like this:
-(NSArray *)sortOccupants
{
NSSortDescriptor *sortPeopleInHouse = [[NSSortDescriptor alloc] initWithKey:#"personName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortPeopleInHouse, nil];
return [[(NSSet *)[house valueForKey:#"occupants"] allObjects] sortedArrayUsingDescriptors:sortDescriptors];
}
Any Ideas would be great but if you can point me to sample code that would show this I would be most appreciative.
Thanks,
The way I would approach this is to generate NSManagedObject subclasses for your entities (makes it much more readable and type-safe).
Then, I would create a new init method in the second view controller. initWithHouse:(House *)house or something:
#property (nonatomic, strong) House * currentHouse;
#property (nonatomic, strong) NSArray * sortedOccupants; // Array of People objects
-(id) initWithHouse:(House *)house
{
if (self = [super init])
{
// Managed Object Context available from [currentHouse managedObjectContext]
currentHouse = house;
sortedOccupants = [self sortOccupants:house.occupants];
}
return self;
}
-(NSArray *)sortOccupants: (NSSet *)occupants
{
NSSortDescriptor *sortPeopleInHouse = [[NSSortDescriptor alloc] initWithKey:#"personName" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortPeopleInHouse, nil];
return [occupants allObjects] sortedArrayUsingDescriptors:sortDescriptors];
}
Hope that helps.
Maybe the key is that each view controller should have
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
in its header.
Before you push a view controller you give it a managed object context. In this new view controller you can have your typical methods for searching and saving the context.
You start with a home view controller, which is really a list of people. You select a person and launch a person view controller. You should pass a managed object context and an instance of Person to this person view controller.
Related
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I am having trouble passing my NSMutableArray data from one view to the other.
My view transition works the following way
Send View -> Recipients View
Recipients View -> Send View
In the recipients view I am grabbing the people I want to send my data to. Once I grab the people using an array, I go back to my send view to send those people that data.
In my send view I have the following NSMutableArray
#property (nonatomic, strong) NSMutableArray *sendTo;
I initialize it in viewDidLoad like so:
self.sendTo = [[NSMutableArray alloc] init];
In my Recipients View I have the following NSMutableArray
#property (nonatomic, strong) NSMutableArray *recipientsArray;
I initialize it in the viewDidLoad like so:
self.recipientsArray = [[NSMutableArray alloc] init];
I also have a back button in this controller which tries to handle the passing of the data in the recipientsArray. Please note this array does have data in it up until the point of the view switch.
- (IBAction)back {
SendView *send = [self.storyboard instantiateViewControllerWithIdentifier:#"View"];
send.sendTo = [NSMutableArray arrayWithArray:self.recipients];
[self dismissViewControllerAnimated:YES completion:nil];
}
Any idea why the array in my Send View is returning nil?
This code creates a brand new instance of SendView...
SendView *send = [self.storyboard instantiateViewControllerWithIdentifier:#"View"];
This code sets the sendTo property on the brand new instance of SendView.
send.sendTo = [NSMutableArray arrayWithArray:self.recipients];
At the end of the back method, this instance of SendView goes out of scope and is gone forever.
In conclusion, you must get a reference to your existing instance of SendView in order to do anything meaningful.
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.
and thanks for looking...
I'm not even sure how to phrase this question, let alone search for the answer... I have tried, honestly, so all help needed!
It's probably pretty simple as I am sure this is a pattern that happens all the time:
I have an entity in my model (MainLevel*) that has a relationship to itself.
The entity is for levels of a law, and the only requirement is that each law has at least one (the top) level. Beyond that the number of sublevels is, technically, infinite (but in reality about 5 normally and probably no more than 8-10 at most). As might be expected each child level has only one parent (MainLevel.parentLevel) and any parent can have multiple (or zero) children (NSSet *childLevels).
What I would like to do is to get all the structure of this relationship to put in a UITableView or a collectionView.
I have a recursive function as follows:
- (NSDictionary *)getStructureOfLawForLevel:(MainLevel *)level
{
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc]initWithCapacity:50];
MainLevel *currentLevel = level;
[mutableDict setObject:currentLevel.shortName forKey:#"name"];
if (level.childLevels) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [currentLevel.childLevels sortedArrayUsingDescriptors:sortdescriptors];
for (MainLevel *childLevel in children)
{
NSDictionary *dict = [self getStructureOfLawForLevel:childLevel];
[mutableDict setObject:dict forKey:#"sublevels"];
}
}
return [mutableDict copy];
}
Then in viewWillAppear: I have this:
self.structure = [self getStructureOfLawForLevel:self.selectedLevel]
With this I hope I am on the right lines...(untested due to another issue I am sorting right now).
I still cant figure out how to configure a UITableView or a UICollectionView from this though. I mean I am sure I can do it by adding a counter or two and getting the number of lines, and sections, that way. It just seems way, way overcomplicated and I am certain there must be a more obvious method I am just not seeing...
The only criteria for the data is that it must be ordered by the .order attribute of the entity instance, and that is not unique. I mean, for example, each childLevel can have a childLevel with order number 1. It is the order in THAT parent level.
Sorry if this has been asked a thousand times. I have tried to search for an answer but nothing seems to fint the search terms I am using.
I am not doing anything with this data except putting on screen, no editing, adding, deleting... Not sure if that is relevant.
Edit for clarity...
I am not looking to do a drill-down type table view. I want a snapshot of the whole structure in one view, and then I may need to drill down from that (using relationships to other entities in the model).
EDIT FOR MY SOLUTION
Here's what I ended up doing...
- (NSArray *)getStructureAsArrayForLevel:(MainLevel *)child
{
NSMutableArray *thisChildAndChildren = [[NSMutableArray alloc]initWithCapacity:2];
[thisChildAndChildren addObject:child];
if (child.childLevels)
{
// children exist
// sort the children into order
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [child.childLevels sortedArrayUsingDescriptors:sortdescriptors];
// get an array for each child via recursion
for (MainLevel *child in children)
{
[thisChildAndChildren addObject:[self getStructureAsArrayForLevel:child]];
}
}
return [thisChildAndChildren copy];
}
Then I am using similar recursive function to convert the array to NSAttributedString and display in textView.
I really DO NOT like recursion. I don't know why but I find it SOOOOOOO hard to get my head around the logic, and when it's done it seems so obvious... Go figure!
Thanks to everyone for suggestions, help etc...
If you can use a 3rd-party controller, take a look at TLIndexPathTools. It handles tree structures. For example, try running the Outline example project.
Your view controller would look something like this (not much to it):
#import "TLTableViewController.h"
#interface TableViewController : TLTableViewController
#end
#import "TableViewController.h"
#import "TLIndexPathTreeItem.h"
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MainLevel *topLevel = nil;//get the top level object here
TLIndexPathTreeItem *topItem = [self treeItemForLevel:topLevel depth:0];
self.indexPathController.dataModel = [[TLTreeDataModel alloc] initWithTreeItems:#[topItem] collapsedNodeIdentifiers:nil];
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
// customize cell configuration here
TLIndexPathTreeItem *item = [self.dataModel itemAtIndexPath:indexPath];
MainLevel *level = item.data;
cell.textLabel.text = [level description];
}
- (TLIndexPathTreeItem *)treeItemForLevel:(MainLevel *)level depth:(NSInteger)depth
{
NSMutableArray *childItems = [NSMutableArray arrayWithCapacity:50];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [level.childLevels sortedArrayUsingDescriptors:sortdescriptors];
for (MainLevel *child in children) {
TLIndexPathTreeItem *childItem = [self treeItemForLevel:child depth:depth + 1];
[childItems addObject:childItem];
}
//set identifier to some unique identifier, if one exists. Otherwise, the item itself
//will be used as the identifier
id identifier = nil;
//set cell identifier based on depth. This can be set to something unique for each
//depth, or set to a constant value. If nil, the value "Cell" is assumed.
NSString *cellIdentifier = [NSString stringWithFormat:#"Cell%d", depth];
//pass "level" to the data argument. Or pass any other data, e.g. include the depth #[#(depth), level]
TLIndexPathTreeItem *item = [[TLIndexPathTreeItem alloc] initWithIdentifier:identifier sectionName:nil cellIdentifier:cellIdentifier data:level andChildItems:children];
return item;
}
#end
You can subclass TLTreeTableViewController instead of TLTableViewController if you want collapsable levels. Let me know if you need more help.
EDIT
Sorry, I missed the part that says you want to display it all at once. Basically, I think the easiest way to do this would be to basically have a recursive structure that gets the description of each object. This could be a string or even a UIView that you could then place inside your tableviewcell.
Lets stick with a dictionary for now. Each dictionary representation can have information about itself and its children. The template can be:
<LevelInfoDictionary>
<NSObject>someObjectThatRepresentsInfoAboutThisLevel
<NSArray>arrayOfInfoDictionariesThatRepresentChildren
</LevelInfoDictionary>
Then to implement your recursive method:
- (NSDictionary *)getLevelInfo
{
NSMutableArray *childInfo = [NSMutableArray array];
for(ClassName *child in self.children)
{
[childInfo addObject:[child getLevelInfo]];
}
return [NSDictionary dictionaryWithObjectsAndKeys:
<descriptionOfThisLevel>, #"CurrentLevel",
childInfo, #"children>, nil];
}
END EDIT
Basically, as some of these other guys have said, you should create your tableview that displays all of your top level objects. From there, after you select an object, you should be pushed to a new tableview that uses a different fetch request with the predicate like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"parent = %#",
selectedParentObject];
Then you can use sort descriptors to sort the NSFetchRequest you are using.
Alternatively, you could just fetch the children by using the property on the parent object and store that in an array sorted by your sort descriptors.
One other thing that I should mention is that currently the sort descriptor does not accomplish anything. You may not notice this because there are other parts of the design that you should change, but since an NSDictionary does not have an order (it is a hash table), sorting objects before placing them in a dictionary does nothing.
From the ViewController for the TableView or CollectionView you should start by showing all of the top level objects (No parent). From there as a user selects an object that parent becomes the current level and the ViewController should refresh its data source to show all of the child elements at that level. You can then traverse back up to the parent via back button.
Let me know if you need any more detail.
Your loop doesn't make sense, because you want a list of the dictionaries for the structure of the children, but what you actually do it to overwrite it each time. You probably want something like:
NSMutableArray *subLevels = [NSMutableArray array];
for (MainLevel *childLevel in children)
{
[subLevels addObject:[self getStructureOfLawForLevel:childLevel]];
}
[mutableDict setObject:subLevels forKey:#"sublevels"];
I guess you want to show each level in a table view and drill to another table view for each subsequent level. That should be simple based on the dictionary which gives you a name to display and an optional array which defines whether drilling is possible and the data to pass to the next view controller.
In case it helps someone else, here's what I ended up doing...
- (NSArray *)getStructureAsArrayForLevel:(MainLevel *)child
{
NSMutableArray *thisChildAndChildren = [[NSMutableArray alloc]initWithCapacity:2];
[thisChildAndChildren addObject:child];
if (child.childLevels)
{
// children exist
// sort the children into order
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sortdescriptors = #[sortDescriptor];
NSArray *children = [child.childLevels sortedArrayUsingDescriptors:sortdescriptors];
// get an array for each child via recursion
for (MainLevel *child in children)
{
[thisChildAndChildren addObject:[self getStructureAsArrayForLevel:child]];
}
}
return [thisChildAndChildren copy];
}
Then I am using similar recursive function to convert the array to NSAttributedString and display in textView.
I really DO NOT like recursion. I don't know why but I find it SOOOOOOO hard to get my head around the logic, and when it's done it seems so obvious... Go figure!
Thanks to everyone for suggestions, help etc...
EDIT
It isn't exactly right, as the first layer has a slightly different structure to those that follow. at some point I need to change this to have the top level as a directory and use the very first level as the key for the directory, then add the complete array as the object for that key. But it works... my brain aches... and I can live with it until I get around to changing it.
I have 3 Scenes each collecting User's input. Each Scene has 5 UITextFields. The 4th Scene shows all the 15 Text Field in a UITableView.
I'm not sure if this is the best way to do do this, but I have the following code for scene 1:
//Meetings is NSManagedObject class. Meetings.h and .m was created from the Meetings entity from Core Data
//I have this code once in the file right before I start saving the data
Meetings *meetings = (Meetings *) [NSEntityDescription insertNewObjectForEntityForName:#"Meetings" inManagedObjectContext:self.managedObjectContext];
// I have similar code below for each user's input.
NSString *date = [[NSString alloc] initWithFormat:#"%#", [dateFormatter stringFromDate:selectedDate]];
DateLabel.text = date;
[meetings setDateLabel:date];
...
[meetings setTimeLabel:time];
..
//Code below is to save. I have this once at the end of the file to save the data
NSError *error = nil;
if (![managedObjectContext save:&error]) {
// Handle the error.
}
//The log below shows the saved data fine. Thus, the data is being saved in managnedObjectContext.
NSLog (#"This is the DateLabel %#", meetings.DateLabel);
Question: How do I access the pointer *meetings from Scene 2 and 3 to save rest of the fields in managedObjectContext? I did a NSLog from Scene 2 and it shows as Null:
//In Scene 2 viewDidLoad method I did the following to check:
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
Meetings *meetings = (Meetings *) [NSEntityDescription insertNewObjectForEntityForName:#"Meetings" inManagedObjectContext:self.managedObjectContext];
NSLog (#"This is the DateLabel from Scene 2 %#", meetings.DateLabel);
The log shows:
2013-02-11 18:04:05.447 MyApp[3505:c07] This is the DateLabel from Scene 2 (null)
You either need to pass a pointer to the Meetings object from the previous screens forward to the next by storing them in a property, or you can pass the object's id's and fetch them as needed for the final screen.
Assume the following is a reflection of your code. The class names may not be identical, but I think you will be able follow and change them as needed.
Scene 1 Header File:
//
// Scene1ViewController.h
// ... etc.
#import <UIKit/UIKit.h>
#import "Meetings.h"
#interface Scene1ViewController : UIViewController
#property (nonatomic, strong) Meetings *meetingsForScene1;
// ... etc.
#end
Scene 2 Header File:
//
// Scene2ViewController.h
// ... etc.
#import <UIKit/UIKit.h>
#import "Meetings.h"
#interface Scene2ViewController : UIViewController
#property (nonatomic, strong) Meetings *meetingsFromScene1;
#property (nonatomic, strong) Meetings *meetingsForScene2;
// ... etc.
#end
meetingsForScene2 may or may not be appropriate depending on your requirements. You might just add data from Scene2 to the meetingsFromScene1 and pass that instance on to the next scene.
Scene 1 Implementation's -prepareForSegue:sender::
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
if ([segue.identifier isEqualToString:#"YourSegueIdentifierForTransistionFromScene1ToScene2"]) {
Scene2ViewController *scene2 = [destinationViewController isKindOfClass:[Scene2ViewController class]] ? (Scene2ViewController *)destinationViewController : nil;
NSAssert(scene2, #"scene2 should not be nil");
NSAssert(self.meetingsForScene1, #"self.meetingsForSecen1 should not be nil");
scene2.meetingsFromScene1 = self.meetingsForScene1;
}
}
Note you might have two properties on Scene2. One for the current scene's data and one for the previous scene's data. The important property is the one that holds the data from the previous scene. The -prepareForSegue:sender: method in Scene1 is an appropriate time to set Scene1's data to the property on Scene2.
I've included some NSAssert calls to do some checking. Consider removing those in your production code. Also, note the check when assigning *scene2. If your destination controller is not the right "kind", you'll find out right away.
I am not necessarily advocating this approach as the "best" for your problem, but this approach will at least provide a path to a solution, if not address the issue entirely.
Well, what you do in your viewDidLoad method is creating a new object in your NSManagedObjectContext instance.
What you should do is to make a fetch request for your previously created Meetings object.
Maybe you should read a tutorial about Core Data. The following is very easy to understand: http://www.raywenderlich.com/934/core-data-on-ios-5-tutorial-getting-started
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];