I'm writing a player software and need to add tags to the songs. For this I've created a core data record.
How do I keep my library in sync with the iOS music library? My approach until now is to get the list of songs in the music library, and the list of songs in CoreData. Then use a dictionary and search for the uuid. If it exists I remove it from my list of CoreData items. After this loop I simply remove all that is left in my CoreData items list.
The problem with this approach, for some reason it crashes with NSFastEnumerationMutationHandler which basicly means I'm trying to access an object in an array in a loop just after I removed it.
- (void)updateMediaLibrary {
NSArray *mediaItems = [self getMediaItems];
NSMutableDictionary *coreDataItems = [self getCoreDataItems];
for (MPMediaItem *item in mediaItems) {
NSNumber *uuid = [item valueForProperty:MPMediaItemPropertyPersistentID];
Song *song = [coreDataItems valueForKey:[uuid stringValue]];
if (!song) {
[Song createSongFromMediaItem:item inContext:self.context];
}
else {
[coreDataItems removeObjectForKey:[uuid stringValue]];
}
}
for (Song *item in coreDataItems) {
[Song deleteSongWithUID:item.libKey inContext:self.threadContext];
}
}
I might just be blind but for some reason I can't see my error in this code.
Do you have alternative/better suggestions, how to keep the songs in sync?
Thanks for your help!
Rather than making a list of the actual coreDataItems, make a list of the persistent keys and do your work with that, something like:
- (void)updateMediaLibrary {
NSArray *mediaItems = [self getMediaItems];
NSMutableSet *coreDataItems = [NSMutableSet setWithArray:[[self getCoreDataItems] allKeys]];
for (MPMediaItem *item in mediaItems) {
NSNumber *uuid = [item valueForProperty:MPMediaItemPropertyPersistentID];
if (![coreDataItems containsObject:[uuid stringValue]]) {
[Song createSongFromMediaItem:item inContext:self.context];
}
else {
[coreDataItems removeObject:[uuid stringValue]];
}
}
for (NSString* key in coreDataItems) {
[Song deleteSongWithUUID:key];
}
}
To me, the problem is probably coming from that method:
[Song createSongFromMediaItem:item inContext:self.context]
Or from that method:
[Song deleteSongWithUID:item.libKey inContext:self.threadContext].
They probably modify the NSArray and/or the NSMutableDictionary during enumeration.
One thing that you could do is:
NSArray *mediaItems = [self getMediaItems].copy;
NSMutableDictionary *coreDataItems = [self getCoreDataItems].mutablecopy;
Related
I am building a music app and I am trying to get the MP3 files metadata with this piece of code:
for (NSString *format in [assest availableMetadataFormats]) {
for (AVMetadataItem *item in [assest metadataForFormat:format]) {
if ([[item commonKey] isEqualToString:#"title"]) {
[SongNameMutableArray addObject:[NSString stringWithFormat:#"%#",[item value]]];
}
if ([[item commonKey] isEqualToString:#"artist"]) {
[ArtistMutableArray addObject:[NSString stringWithFormat:#"%#",[item value]]];
}
if ([[item commonKey] isEqualToString:#"albumName"]) {
}
if ([[item commonKey] isEqualToString:#"artwork"]) {
UIImage *img = nil;
if(floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
img = [UIImage imageWithData:item.dataValue];
}
else {
NSDictionary *dict ;
[item.value copyWithZone:nil];
img = [UIImage imageWithData:[dict objectForKey:#"data"]];
}
}
}
}
The code works fine. Only thing is that when the mutable arrays are filled, they do it in the following way:
SongNameMutableArray => [song1, song2, song3, songX];
ArtistMutableArray => [Artists1, Artist3, Artist 10, ArtistX];
Because not all songs have an artist in the metadata.
According to this specific part of the code
if ([[item commonKey] isEqualToString:#"artist"]) {
[ArtistMutableArray addObject:[NSString stringWithFormat:#"%#",[item value]]];
}
If the item found is the artist, insert it in the array.
Now my question is how to detect when a song has no artist in the metadata? So that i can insert "null" in ArtistMutableArray => [Artists1, null, Artist3, null, Artist 5, ArtistX];
I tried with:
if ([[item commonKey] isEqualToString:#"null"])
if ([[item value] isEqualToString:#"null"])
I am not sure what I am missing in here.
Everything you are doing needs to be done differently. Don't split the data into separate arrays, especially if you want each array to have the same number of elements and each corresponding index refers to the same asset.
Instead, define a class that contains the few properties you want to track for each asset. Then have a single array of those class instances. Then there is no need to worry about keeping multiple arrays in sync and perform tricks by adding "null" values to an array where needed.
There are many other benefits to having one array of data. For example, sorting. By having one array, you can sort that one array of data by artist, for example. Trying to keep multiple arrays in sync when sorting or filtering is a huge pain.
Since you are getting just the common metadata there is no need to iterate over all formats and look at the properties of each format. Just use the commonMetadata property of the asset.
When getting the commonKey value, do not compare it against hardcoded strings. Use the constants provided by the API such as AVMetadataCommonKeyAlbumName.
And since each metadata item can only have one value for commonKey, use if/else to make your code more efficient. And use the stringValue property of the metadata item to get a string value instead of using stringWithFormat.
Here is a start for your metadata class. Name as you see fit and add whatever other properties you need.
#interface MyMetaData: NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *artist;
#property (nonatomic, copy) NSString *albumName;
#property (nonatomic, copy) UIImage *artwork;
#end
#implementation MyMetaData
#end
And here's a replacement for the code you posted in your question:
NSMutableArray *metadata = [NSMutableArray array]; // your array of obtained metadata
AVAsset *asset = // some asset
MyMetaData *data = [[MyMetaData alloc] init];
NSArray *commonData = asset.commonMetadata;
for (AVMetadataItem *item in commonData) {
AVMetadataKey key = item.commonKey;
if ([key isEqual: AVMetadataCommonKeyTitle]) {
data.title = item.stringValue;
} else if ([key isEqual:AVMetadataCommonKeyArtist]) {
data.artist = item.stringValue;
} else if ([key isEqual:AVMetadataCommonKeyAlbumName]) {
data.albumName = item.stringValue;
} else if ([key isEqual:AVMetadataCommonKeyArtwork]) {
data.artwork = // your code to get the image
}
}
[metadata addObject:data];
This is much cleaner and you now only have one array to deal with. If a given asset doesn't have data for, say the artist, that value simply remain nil on the given MyMetaData instance.
I have an NSMutableArray of custom objects. Each contains an ID string that is unique, and each contains an downloadedDate property.
Objects could get added twice so I need to check the ID for duplicates, and if I find a duplicate I need to keep the one that has the newest date.
Currently I am doing the following to remove duplicates, but it doesn't take into account keeping the object with the newest date.
NSArray *originalArray = [NSArray arrayWithArray:mutableItems];
NSMutableArray *uniqueArray = [NSMutableArray array];
NSMutableSet *names = [NSMutableSet set];
for (ZSSObject *g in originalArray) {
NSString *destinationName = g.code;
if (![names containsObject:destinationName]) {
[uniqueArray addObject:g];
[names addObject:destinationName];
}
}
NSArray *uniqueObjects = uniqueArray;
Objects get created like this:
ZSSObject *obj = [ZSSObject alloc] init];
obj.code = #"12345";
obj.downloadedDate = [NSDate date];
Is there an easier way to do that I want than having a bunch of copies of my array and nested loops?
Using the suggestion to use an NSDictionary instead, I came up with this solution:
NSMutableDictionary *objDict = [[NSMutableDictionary alloc] init];
for (ZSSObject *g in mutableItems) {
ZSSObject *addedObj = objDict[g.code];
if (addedObj) {
// Compare dates
if ([addedObj respondsToSelector:#selector(dateDownloaded)]) {
if ([g.dateDownloaded compare:addedObj.dateDownloaded] == NSOrderedDescending) {
[objDict setObject:g forKey:g.code];
}
}
} else {
[objDict setObject:g forKey:g.code];
}
}
NSArray *uniqueObj = objDict.allValues;
I want to create a custom view that is just like contacts in iPhone. Is there any tutorial available to make a custom view just like ABPeoplePickerNavigationController?
Please note that I do not want to open the default people picker controller provided by AddressBookUI framework. Also, I do want to bind this navigation controller into my main view.
For reference that what I exactly want, You can refer contacts tab of Whatsapp on iOS device.
EDIT: I already got a contact list and displayed the first and last name of person in a table view. Now, I want to create an index for alphabets from A-Z and on tapping of that index, the table view should scroll to that contacts. Also, How can I implement search functionality to find user by his / her first or last name?
I have the exact same thing in the app i'm currently building and we also got our inspiration from apps like Whatsapp when it came to contacts.
I used a simple tableview with custom cells for visuals, with simply name & picture.
My process was the following :
Create a contact object in coredata (or another persistent way of keeping your data)
Through the ABAddressbook framework you can browse all your contacts and transform them in your new Contact objets. Keep a reference of your ABPerson in your Contact object, this will allow you to find-and-update your Contacts later just using references. If you don't do that you will have to browse to all your ABPersons every time you want to update your Contacts.
You could use the ABPersons directly but it would just be really painful to code.
Once you've extracted all your contacts, make sure to save your context if you use core data, or store them in .sqlite.
You can then simply extract them in an array of Contacts and display those in a custom cell of your choosing.
This appcoda tutorial is a decent custom cell for tableview tutorial. You can find a thousand more just by googling " tableview custom cell ios" and finding different things that you might like. In the end, you'll just have a cell with a label and a picture, you COULD use the simple UITableViewCell which I used for another tableview of "contact" type.
Keeping that contact list up to date (getting the right numbers, pictures, names, etc) and making sure they exist before updating, checking if a contact has been deleted, added, etc. All that has to be done in order for your list to be accurate, and it's a pretty long/annoying process.
I could share my Contact class but it includes a lot of irrelevant code for you which might confuse you because :
- I'm also checking if those contacts are already users of my app to move them in specific sections of my tableview
- I split my tableview in 27 sections (users, then alphabet letters).
Also, and I'll stop it with that last piece of general programming advice : It would be a good idea to write down first exactly what you need and what you'll need, get all the possibilities on paper, etc. I bumped into a lot of simple problems that it took me a while to resolve, either because I didn't plan properly or because it was hidden.
For example :
Some of your contacts don't have names, at all.
Some of your contacts have a name in the "Title" field (where you write Doctor, or Mister)
Some of your contacts don't have phone numbers (if you're using phone numbers as identifiers)
Some of your contacts have international formatting and some not (+32 46556555 or 0032 46556555)
Some of your contacts have pictures taken with camera, some others from Gmail, which have different formats
You might have duplicate contacts (same name, same everything) due to poor sync from the user
You need to make sure the firstname/lastname is in the right section, case sensitive coding can cause trouble
You need to find a solution for a contact that start with a smiley or non alphanumeric characters
Your users will want an index-list on the side
Of course you'll need to add a search bar because, some people have way more than 1000 contacts.
Many more to come, I guarantee that.
Because this is very app-specific i'm not gonna go over every problem that I had and what I did for it, but you get the idea :) Feel free to ask any very specific questions though and I might already have a very specific solution, since I pretty much had to copy whatsapp's contacts from scratch and, hell, I made it. (I actually got the exact same as Anonymess and iOS)
EDIT : Here are some methods of my ABPerson extracting methods ; the ContactDAO mostly interact with my persistent model (CoreData) and I believe their names are clear enough for you to understand what's happening. I'm kind of happy with the comments and variablenames so you should be to read that without too much trouble.
Here comes a massive block of code.
- (NSMutableArray *)getAllRecordsWithPeople:(CFArrayRef)allPeople andAddressBook:(ABAddressBookRef)addressbook{
NSMutableArray *newRecords = [[NSMutableArray alloc]init];
CFIndex nPeople = ABAddressBookGetPersonCount(addressbook);
for (int i=0;i < nPeople;i++){
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
ABRecordID refId = ABRecordGetRecordID(ref);
NSNumber *recId = [NSNumber numberWithInt:refId];
[newRecords addObject:recId];
}
return newRecords;
}
- (void)getValidContactsFromAddressBookWithCompletionBlock:(void (^)(NSError *error))completion{
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
__block BOOL accessGranted = NO;
if (&ABAddressBookRequestAccessWithCompletion != NULL) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
if (accessGranted) {
NSMutableArray *newRecords = [[NSMutableArray alloc]init];
NSMutableArray *updatedRecords = [[NSMutableArray alloc]init];
NSMutableArray *unchangedRecords = [[NSMutableArray alloc]init];
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
//Checking the last time we updated
NSTimeInterval lastSyncTime;
if ([[NSUserDefaults standardUserDefaults]objectForKey:#"LastSyncTime"] == nil){
//This is the first time we update.
lastSyncTime = 0;
}else{
//Setting the last update in variable
lastSyncTime = [[[NSUserDefaults standardUserDefaults]objectForKey:#"LastSyncTime"]doubleValue];
}
if (lastSyncTime == 0){
//We have to insert everyone, this is the first time we do this.
newRecords = [self getAllRecordsWithPeople:allPeople andAddressBook:addressBook];
}else{
//We have to manually compare everyone to see if something has changed.
for (int i=0;i < nPeople;i++) {
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
ABRecordID refId = ABRecordGetRecordID(ref);
NSNumber *recId = #(refId);
CFDateRef recordCreation = ABRecordCopyValue(ref, kABPersonCreationDateProperty);
NSDate *recCreDate = (__bridge NSDate *)(recordCreation);
NSTimeInterval creDateInterval = [recCreDate timeIntervalSince1970];
if(creDateInterval > lastSyncTime){
//This object was created after my lastSync, this must be a new record
[newRecords addObject:recId];
}else{
//Checking the last update of the given record
CFDateRef recordUpdate = ABRecordCopyValue(ref, kABPersonModificationDateProperty);
NSDate *recUpDate = (__bridge NSDate*)(recordUpdate);
if ([recUpDate timeIntervalSince1970] > lastSyncTime){
//The record was somehow updated since last time, we'll update it
[updatedRecords addObject:recId];
}else{
//The record wasn't updated nor created, it is therefore unchanged.
//We still need to keep it in a separate array to compare deleted contacts
[unchangedRecords addObject:recId];
}
}
}
if(allPeople)
CFRelease(allPeople);
}
[self manageNewContacts:newRecords updatedContacts:updatedRecords andUnchangedContacts:unchangedRecords inAddressBook:addressBook andBlock:^(NSError *error) {
completion(error);
}];
}else{
NSError *error = [NSError errorWithDomain:#"ABAccess access forbidden" code:403 userInfo:nil];
completion(error);
}
}
- (void)manageNewContacts:(NSMutableArray*)newRecords updatedContacts:(NSMutableArray*)updatedRecords andUnchangedContacts:(NSMutableArray*)unchangedRecords inAddressBook:(ABAddressBookRef)addressbook andBlock:(void (^)(NSError *error))completion{
AppDelegate *app = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = app.managedObjectContext;
//Getting all the CoreData contacts IDs to have something to compare
NSArray *coreDataContactsIds = [ContactDAO getAllContactIdsInManagedObjectContext:context];
for (NSDictionary *rec in coreDataContactsIds){
NSNumber *recId = rec[#"record_id"];
if (![unchangedRecords containsObject:recId]){
//The unchanged record doesn't exist locally
if (![updatedRecords containsObject:recId]){
//The updated record doesn't exist locally
if (![newRecords containsObject:recId]){
//The new record doesn't exist locally
//That means the ongoing record has been deleted from the addressbook,
//we also have to delete it locally
[ContactDAO deleteContactWithID:recId inManagedObjectContext:context];
}
}
}
}
for (NSNumber *recId in updatedRecords){
ABRecordID recordID = (ABRecordID)recId.intValue;
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressbook, recordID);
NSDictionary *personDict = [self getPersonDictionaryFromABRecordRef:person];
if (personDict){
[ContactDAO updateContactWithFirstName:personDict[#"firstName"] lastName:personDict[#"lastName"] compositeName:personDict[#"compositeName"] picture:personDict[#"picture"] phoneNumbers:personDict[#"phoneNumbers"] recordID:recId inManagedObjectContext:context];
}
}
for (NSNumber *recId in newRecords){
ABRecordID recordID = (ABRecordID)recId.intValue;
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressbook, recordID);
NSDictionary *personDict = [self getPersonDictionaryFromABRecordRef:person];
if (personDict){
[ContactDAO createContactWithFirstName:personDict[#"firstName"] lastName:personDict[#"lastName"] compositeName:personDict[#"compositeName"] picture:personDict[#"picture"] phoneNumbers:personDict[#"phoneNumbers"] recordID:recId inManagedObjectContext:context];
}
}
NSError *dbError;
[context save:&dbError];
NSTimeInterval lastSyncTime = [[NSDate date]timeIntervalSince1970];
[[NSUserDefaults standardUserDefaults]setObject:#(lastSyncTime) forKey:#"LastSyncTime"];
completion(dbError);
}
- (NSDictionary*)getPersonDictionaryFromABRecordRef:(ABRecordRef)person{
//Get name
NSString * firstName, *lastName;
firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
firstName = (firstName == nil) ? #"" : firstName;
lastName = (lastName == nil) ? #"" : lastName;
NSString *compositeName;
if ([firstName isEqualToString:#""] && [lastName isEqualToString:#""]){
return nil;
}
if ([lastName isEqualToString:#""]){
compositeName = [NSString stringWithFormat:#"%#", firstName];
}
if ([firstName isEqualToString:#""]){
compositeName = [NSString stringWithFormat:#"%#", lastName];
}
if (![lastName isEqualToString:#""] && ![firstName isEqualToString:#""]){
compositeName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
}
//Get picture
CFDataRef imageData = ABPersonCopyImageData(person);
NSData *data = CFBridgingRelease(imageData);
//Get phone numbers
NSMutableSet *phoneNumbers = [[NSMutableSet alloc]init];
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {
CFStringRef str = ABMultiValueCopyValueAtIndex(phones, i);
NSString *num = CFBridgingRelease(str);
[phoneNumbers addObject:num];
/*if(str)
CFRelease(str);*/
}
//Save it in dictionary
NSDictionary *personDict = [[NSDictionary alloc]initWithObjectsAndKeys:firstName, #"firstName",lastName , #"lastName",phoneNumbers,#"phoneNumbers", compositeName, #"compositeName", data, #"picture", nil];
//Release everything.
if(phones)
CFRelease(phones);
return personDict;
}
When it comes to indexes, this tutorial should do fine.
Have a look to this : http://www.appcoda.com/ios-programming-index-list-uitableview/
Methods of table view help out to give your desired result :
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
and
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title
I have a simple NSMutableArray which I am trying to store a few objects in. However in NSLog, the contents of the array always comes as null... I just dont understand why. Here is my code:
In my header file:
NSMutableArray *dataFiles;
In viewDidLoad:
dataFiles = [[NSMutableArray alloc] init];
Later on in my code in a method which is trying to add a string to my NSMutableArray:
[dataFiles insertObject:url atIndex:0]; // 'url' is an an NSURL.
What am I doing wrong? This is always how I have used NSMutableArray's, why are they all of a sudden not working?
UPDATE
I did indeed do an NSLog on the "url" (NSURL) before its being added to the array and it is not null at all. Here is the output:
THE URL: file:///var/mobile/Containers/Data/Application/E991FAFC-80DB-437B-B214-96720B1AA7AF/Documents/19Feb15_072308am.aif
UPDATE 2
I just tried #Dheeraj Singh solution and it did not work:
if ([dataFiles count] == 0) {
[dataFiles addObject:url];
}
else {
[dataFiles insertObject:url atIndex:0];
}
NSLog(#"data in: %#", dataFiles);
Thanks for your time, Dan.
Not sure what is wrong, but below (your) code is working fine with me.
NSMutableArray * arr = [[NSMutableArray alloc]init];
NSString *murl = #"file:///var/mobile/Containers/Data/Application/E991FAFC-80DB-437B-B214-96720B1AA7AF/Documents/19Feb15_072308am.aif";
NSURL *url = [NSURL URLWithString:murl];
[arr insertObject:url atIndex:0];
NSLog(#"Array is %#",arr);
Output
Array is (
"file:///var/mobile/Containers/Data/Application/E991FAFC-80DB-437B-B214-96720B1AA7AF/Documents/19Feb15_072308am.aif"
)
What I strongly feel is you are using NSArray against NSMutableArray. Please confirm the same.
Could you post the actual code so that we can tell you what is going on?
Ok after a bit of playing around I found out what was "wrong" or at least what is stopping my code from working. It is because before I was initialising the NSMutableArray in the viewDidLoad method. As soon as I moved the NSMutableArray initialisation code to method where I wanted to write the data to it, it started working. Anyone know why?? Here is my code now:
// Initialise the audio arrays.
// Originally this line was in the viewDidLoad.
dataFiles = [[NSMutableArray alloc] init];
if ([dataFiles count] == 0) {
[dataFiles addObject:url];
}
else {
[dataFiles insertObject:url atIndex:0];
}
You can do in Following way :
NSMutableArray * arr = [[NSMutableArray alloc]init];
NSString *url = #"www.test.com";
[arr addObject:url];
NSLog(#"Count of Array is %i",[arr count]);
*** if you want to add multiple items then you can do by following way
for (int i =0; i < 5; i++) {
[arr addObject:#"Hello"];
}
NSLog(#"Count of array is %i",[arr count]);
I am in my IOS application in which i am getting ID from server which i am saving in string and then add strings in NSMutableArray.I am not getting perfect method by which i can add the strings in array and use the array outside the scope.
Here is my code Please help me out::
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary
{
NSMutableArray *array=[[NSMutableArray alloc]init];
i=0;
NSLog(#"%s %# %#", __PRETTY_FUNCTION__, inRequest.sessionInfo, inResponseDictionary);
if (inRequest.sessionInfo == kUploadImageStep)
{
snapPictureDescriptionLabel.text = #"Setting properties...";
NSLog(#"%#", inResponseDictionary);
NSString* photoID =[[inResponseDictionary valueForKeyPath:#"photoid"] textContent];
flickrRequest.sessionInfo = kSetImagePropertiesStep;
// for uploading pics on flickr we call this method
[flickrRequest callAPIMethodWithPOST:#"flickr.photos.setMeta" arguments:[NSDictionary dictionaryWithObjectsAndKeys:photoID, #"photo_id", #"PicBackMan", #"title", #"Uploaded from my iPhone/iPod Touch", #"description", nil]];
[self.array addObject:photoID];
arr=array[0];
counterflicker++;
NSLog(#" Count : %lu", (unsigned long)[array count]);
}
How can i add the photoID(Strings) in the array?
Please help me out..
for adding NSString in NSMutableArray is like this
NSString *str = #"object";
NSMutableArray *loArr = [[NSMutableArray alloc] init];
[loArr addObject:str];
In your code Why are you using self.array ? just write like this. [array addObject:photoID];
self keyword is used for global variables but here in your code
"array" is a local variable .So need of self.array
[array addObject:photoID];
Before adding check that photoID is nil or not
if (photoID.length > 0) {
[array addObject:photoID];
}
I observe that in your code. you declare mutable array in local scope.
So just use
[array addObject:photoID];
Instead of
[self.array addObject:photoID];
May be you are create property for this array with same name, then you need to alloc it.
If you create a property for this then remove local declaration and alloc array like this.
self.array=[[NSMutableArray alloc]init];
and then use
[self.array addObject:photoID];