NSMutuableArray & Core Data not behaving properly - ios

I have string of photo urls, pertaining to their location (e.x.: googleusercontent.com/photo.gif). I saved that single string (the string maybe holds say, four other urls) to Core Data using the 'binary data' type. When I retrieve the urls from core data, it displays the amount of strings correctly, but does not display the correct data outside of the for loop.
// Loop through the photo selection to get the urls
for (int i=0; i < self.tempPhotos.count; i++)
{
self.photo = [ self.tempPhotos objectAtIndex:i];
self.image = [ photo originalImage];
NSString *urls = [image.URL absoluteString]; // <-- here we get the urls and store
self.selected_urls = urls;
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:urls];
self.group.selectedurl = data; // assign it to the core data object
}
// Now we're out of the for loop, here is where it will not retrieve and log properly. if i put this inside the for loop, it will. why is that?
NSMutableArray *temp = [NSMutableArray *)[NSKeyedUnarchiver unarchiveObjectWithData:self.group.selectedurl];
NSLog(#"%#",temp);
If I put the NSLog inside the for loop, I get it correctly. It should look something like this:
- "googleusercontent.com/photo1.gif"
- "googleusercontent.com/photo2.gif"
When I retrieve the Core Data object outside of the for loop, it gives me this:
- "googleusercontent.com/photo1.gif"
- "googleusercontent.com/photo1.gif"
I have no clue why it is working inside the for loop, but it won't work properly anywhere outside, and I feel like I am missing an obvious step. Could I have any help?

Your urls variable is an NSString, which you're archiving and then later unarchiving as an NSMutableArray, which is incorrect. I think what you're trying to do is build an NSMutableArray of NSStrings in your for loop:
NSMutableArray *urls = [NSMutableArray arrayWithCapacity:self.tempPhotos.count];
for (int i=0; i < self.tempPhotos.count; i++)
{
...
[urls addObject:[image.URL absoluteString]];
...
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:urls];
...
}

Related

Previous NSDictionary now to JSON array

- (void)retrieveData
{
NSURL * url = [NSURL URLWithString:#"***/connection.php"];
NSData * data = [NSData dataWithContentsOfURL:url];
_json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_questionsArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _json.count; i++)
{
NSString * qID = [[_json objectAtIndex:i] objectForKey:#"id"];
NSString * qTitle = [[_json objectAtIndex:i] objectForKey:#"question_title"];
NSString * qA = [[_json objectAtIndex:i] objectForKey:#"A"];
NSString * qB = [[_json objectAtIndex:i] objectForKey:#"B"];
NSString * qC = [[_json objectAtIndex:i] objectForKey:#"C"];
NSString * qD = [[_json objectAtIndex:i] objectForKey:#"D"];
NSString * qAnswer = [[_json objectAtIndex:i] objectForKey:#"question_answer"];
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[_questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
NSLog( #"%#", _questions );
}
Logs (null)
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
If I understand your question correctly it becomes something like this
self.questions = .... //I assume this is the array you reference 'question' objects that is created by your retrieve data method
//this used to be created by pulling an object out of your questions dictionary with the key i interpreted as a string.
//now that it's an array you should be able to just reference it by index, assuming they were inserted in order
//I'm also assuming that what comes out of the aray is a question object given the code you provided with the signature
//- (id) initWithQuestionID: (NSString *) qID andQuestionName: (NSString *) qName andQuestionA: (NSString *) qA andQuestionB: (NSString *) qB andQuestionC: (NSString *) qC andQuestionD: (NSString *) qD andQuestionAnswer: (NSString *) qAnswer
Question *nextQuestion = self.questions[i];
self.answer = nextQuestion.questionAnswer;
self.questionLabel.text = nextQuestion.questionLabel;
//and so on
I also suggest the following edit to replace your for loop. It uses a for in loop instead, this saves you from having to keep track of an index and looks cleaner. It also helps so you don't keep repeating the [_json objectAtIndex:i] chunk of code. I also use modern objective-c syntax to access the dictionary.
for (NSDictionary *questionDictionary in _json)
{
NSString * qID = questionDictionary[#"id"];
NSString * qTitle = questionDictionary[#"question_title"];
...
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
If you need the key along with the object in the dictionary then you can clean it up in a similar way with the enumerateObjectsUsingBlock
[_json enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
//your code here
}];
EDIT
It sounds like what your really wanting to do is to pull down your JSON but keep all your other code the way it was when you were using a dictionary that you got from your plist. So in this case you want your parsing function to return a dictionary instead of an array. If that's the case it's worth sidestepping into computer science for a second.
NSDictionarys are also known as a hash, map, symbol table, or associative array. Some languages (such as Lua) don't have an array collection like NSArray, they only have dictionaries. From a dictionary you can create many of the other collections your used to like arrays (and sets too). Heres how it works: Instead of an ordered collection of elements with an index, you place the items in a dictionary and use what would have been the index as the key, and the value becomes, well, the value. For example an array and it's equivalent associative array (aka dictionary):
NSArray *array = #[#"hello", #"world", #"!"];
NSDictionary *dictionary = #{#(1): #"hello",
#(2): #"world",
#(3): #"!"};
This is exactly what your doing when you load in the data from your plist because the first elements key is 0 followed by another dictionary, and I'm supposing that the next element in the list is 1 followed by another dictionary. Inside your parsing function it becomes
NSMutableDictionary *questions = [[NSMutableDictionary alloc] init];
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
This of course assumes that your api is going to return the JSON questions in the order you want.

Adding list of URLs to an array?

I'm using MHVideoPhotoGallery to create gallery's of images that are stored on my website. The current way to add images (as shown in the example on Github) is
MHGalleryItem *photo1 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
MHGalleryItem *photo2 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
MHGalleryItem *photo3 = [MHGalleryItem.alloc initWithURL:#"*ENTER IMAGE URL HERE*"
galleryType:MHGalleryTypeImage];
self.galleryDataSource = #[#[photo1,photo2,photo3]];
But I want to add hundreds of images and this is not the most ideal way to do it. What would be an easier way for me to accomplish this?
Thanks!
You have to start with a list of the URLs. What I would do is put this in a text file in my bundle. In code, when the app runs, I would open the text file (as an NSString) and split it into an NSArray. Now I've got an NSArray of the URLs. I would then cycle through the NSArray. So now we're inside a loop. For each item the array, I would initialize the MHGalleryItem and then add it to a previously created NSMutableArray with addObject:. Thus we have a two or three-line loop which is repeated, running through all the URLs.
The following is pseudo-code and untested (so it might contain errors), but it should give the general idea of the structure I'm suggesting:
NSMutableArray* temp = [NSMutableArray new];
NSString* s =
[NSString stringWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"urls" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:nil];
NSArray* urls = [s componentsSeparatedByString:#"\n"];
for (NSString* url in urls) {
MHGalleryItem *item = [[MHGalleryItem alloc] initWithURL:url
galleryType:MHGalleryTypeImage];
[temp addObject:item];
}
self.galleryDataSource = temp;
Loop. If you're putting numbers at the end of your variable names, you need a loop and/or an array.
NSMutableArray * photos = [NSMutableArray new];
NSArray * photoPaths = [NSArray arrayWithContentsOfFile:plistContainingPhotoPaths];
for( NSString * path in photoPaths ){
NSURL * photoURL = [NSURL URLWithString:path];
MHGalleryItem * photo = [[MHGalleryItem alloc] initWithURL:photoURL
galleryType:MHGalleryTypeImage];
[photos addObject:photo];
}
And don't use dot syntax for alloc, or your code will burst into flames.
Use a naming protocol on your website such as:
www.mywebsite.com/appImageGallery/insertImageNumber
And replace the insertImageNumber with the number of you image. Then add this for loop to get all of the images and add them to the array.
NSMutableArray *mutableGalleryDataSource = [self.galleryDataSource mutableCopy]
for(int i = 0; i < numberOfImagesOnWebsite; i++){ //replace numberOfImagesOnWebsite with the number of images on your website.
MHGalleryItem *newItem = [MHGalleryItem.alloc initWithURL:[#"www.mywebsite.com/appImageGallery/" stringByAppendingString:[NSString stringWithFormat: #"%i", i]] galleryType:MHGalleryTypeImage];
[mutableGalleryDataSource addObject:newItem];
}
self.galleryDataSource = mutableGalleryDataSource;
There is also an -addObjectsFromArray method on NSMutableArray.

Load array and display based on state of uiswitch

Im trying to develop an app that displays a random truth or dare type question, however the user has the ability to turn off truths or dares in option. I have successfully managed to get the app to display a random quote from a plist file from either the truth or dare array also i have managed to program two switch buttons in the user options view controller.
My problem is how would i go about displaying only a truth or dare or both if the user has turned on of the uiswitchs off?
- (IBAction)button:(id)sender
{
if (!self.plistArray)
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if ([[defaults objectForKey:#"truthonoff"] isEqualToString:#"YES"])
{
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray1 = plistDict[#"truth"];
}
if ([[defaults objectForKey:#"dareonoff"] isEqualToString:#"YES"])
{
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
}
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
NSLog(#"%#", plistArray);
//check to see if array is empty and display message
if ([plistArray count] == 0)
{
self.text.text = #"array empty";
}
else
{
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
That is my attempt however it will not run and i have the feeling it wont ddo the job i need.
Basicly i need it to display only truth if the user has selected that to true or only dare if that is selected or both if both are set to true.
EDIT
sorry the problem with the above code is the plist isnt being loaded and it is scipping straight to if array ==0 {
How do i ensure it loads the array and then checks which arrays to load from the plist file?
Any help is greatly appreciated
This is the code before i tried to add if statements. Im so confussed how best to do this
- (IBAction)shownext:(id)sender {
//load array and check then cycle through this untill array is empty. Array will add two arrays from plist file.
if (!self.plistArray) {
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray * plistArray1 = plistDict[#"truth"];
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
NSLog(#"%#", plistArray);
//check to see if array is empty and display message
if ([plistArray count] == 0) {
self.text.text = #"array empty";
}
else {
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
First, if you have a switch for truth and one for dare I hope you have something in place to deal with when the user turns both switches off and doesn't understand why they get nothing (trust me it will happen).
For the rest I'm not sure exactly how you app works but I will take a guess. I'm thinking you have a utility style app with the main UI in one view and then an info button that flips to a second view where the switches are. I'm also guessing that there is a button in the main view that retrieves a truth or dare string. My final assumption, based on your code above, is that when the user changes the state of a switch that writes a user default that you've use a #define to keep out spelling mistakes.
When your view loads you should load both arrays in case the user changes their mind in the middle of using your app and turns on both options or changes from one to the other. Depending on how many entries you have in each of those arrays you might consider creating a combined array as well to simplify things.
When the button is pressed you should then look at the defaults and see if you need to look at both arrays or just one (the below is pseudo code)
if(truth && dare) {
// if you have a combined array pick a random element from it.
// otherwise first randomly pick one of the arrays to pick from.
}
else if (truth) {
// get a random element from the truth array
}
else {
// get a random element from the dare array
}
Also, your current checks of the switch values will always return no unless you are doing extra work in the switch view controller. You should be using [defaults setBool:<UISwitch.isOn> forKey:<#definedKeyHere>] and [defaults boolForKey:<#definedKeyHere>].
It would really help to know what part isn't working. For one thing, it might help to store your flags as NSNumber objects instead of strings (could your string comparison be failing?). Then you could do something like:
if ([[defaults objectForKey:#"truthonoff"] boolValue])
Use a literal to add the actual NSNumber - #YES or #NO.
Consider changing your logic to something like:
VERIFY CODE - doing this freehand:
if (!self.plistArray)
{
self.plistArray = [NSMutableArray array];
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
// why are you loading this twice?
NSDictionary *plistDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[defaults valueForKey:#"truthonoff"] boolValue])
{
[self.plistArray addObject:plistDict[#"truth"]];
}
if ([[defaults valueForKey:#"dareonoff"] boolValue])
{
[self.plistArray addObject:plistDict[#"dare"]];
}
}
I am assuming that your code to load the Plists is working. Verify all your keys match what's in the Plist. Set a breakpoint and verify.
Calling a method on a nil object is a no-op in Objective C. So, it'll happily ignore calls to nil objects without telling you. Verify you have what you think you have.
Also, here:
//display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
Consider using arc4random_uniform(self.plistArray.count) as it avoids modulo bias in the generator.
Now, this just gives you say 0 or 1 if you have two elements. Are you sure the two dictionary keys, "truth" and "dare" actually point to arrays?
Ok, everything working to this point. Now, you have an array of ARRAYS! So you need to randomly pick a question array, and THEN randomly pick a question.
Something like:
//get random array
int randArrayIndex = arc4random_uniform(self.plistArray.count);
NSArray* questionArray = self.plistArray[randArrayIndex];
//get random question
int randQuestionIndex = arc4random_uniform([questionArray count]);
NSString* randomQuestion = questionArray[randQuestionIndex];
self.text.text = randomQuestion;
// remove question
[questionArray removeObjectAtIndex:randQuestionIndex];
Something like that anyway. Of course, assuming you are storing NSStrings in those Plist arrays.

NSDictionary App Crash

Trying to read a plist and change my font color depending on the option that was selected in the following settings bundle.
The following is how I am trying to accomplish it:
NSString *path = #"/var/mobile/Library/Preferences/NCNotes.plist";
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
fontSize = [[dict objectForKey:#"slideSwitched"] floatValue];
if ([[dict objectForKey:#"noteColor"] valueForKey:#"Purple"]) {
noteView.textColor = [UIColor purpleColor];
} else {
noteView.textColor = [UIColor blackColor];
}
Any ideas why this is why my app is crashing? How do I read the values and change the color depending on what was selected?
It appears that the top level of your plist is an array, not a dictionary, because at the top it says "Item 1" where all of your content is within that. So you have a dictionary within an array. So you can change your code like this:
NSString *path = #"/var/mobile/Library/Preferences/NCNotes.plist";
NSArray *array = [[NSArray alloc] initWithContentsOfFile:path];
NSDictionary *dict = array[0];
You could also change the structure of your plist so that you have a dictionary as the root instead of an array.
Also, keys are supposed to be on the left-hand side and their values on the right-hand side, so I don't see a key "noteColor". You have a key "key" with a value "noteColor", so you'll need to make that correction. I'm also not seeing a "slideSwitched" key, though it might just be outside the bounds of your screenshot.
Also the following won't work:
[[dict objectForKey:#"noteColor"] valueForKey:#"Purple"]
Whatever you get from [dict objectForKey:#"noteColor"] isn't going to be a dictionary, so calling valueForKey: on that isn't going to give you what you want.
simply you should do this with document directory
NSString *contentPath=[[NSBundle mainBundle] pathForResource:#"PLIST_FILE_NAME" ofType:#"plist"];
NSDictionary *dictionary=[NSDictionary dictionaryWithContentsOfFile:contentPath];
write your logic after this, wait a minute , its seems like you dont have a key "noteColor" also. check your plist
Here is some example code documented up the wazoo. Hopefully it will help you understand how these plists and dictionaries work. Everything will be based on your plist file (which could definitely be improved upon, but that's up to you as I don't know your specific situation).
Your question is "How do I find color based on user selection?" I will assume you get the user selection as an int. Something like "User selected 7".
//Load your plist dictionary
NSString *path = #"/var/mobile/Library/Preferences/NCNotes.plist";
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
//Get the array of validValues and the array of validTitles
NSArray *valuesArray = [dict objectForKey:#"validValues"];
NSArray *titlesArray = [dict objectForKey:#"validTitles"];
//Now get the user selected index from the validValues array
int arraySelection = -1;
for(int i = 0; i < [valuesArray count]; i++)
{
NSNumber *number = [valuesArray objectAtIndex:i];
if([number intValue] == userSelectedInput)
{
arraySelection = i;
break;
}
}
if(arraySelection == -1)
{
//Not found in array
return;
}
//Now with that index get the title of the object that the user selected
NSString *userSelectedTitle = [titlesArray objectAtIndex:arraySelection];
//Now do your checking on what the user selected based on that:
if([userSelectedTitle isEqualToString:#"Purple"])
...
You could boil this down quite a bit. Currently your validValues array is completely useless. If it were out of order or missing numbers then it would be needed, but straight counting can be achieved by the validTitles array.

getting value from the loop to store in array

I have this code from which i am checking songs that are present in my app and storing the number of songs in an NSMUTABLEArray but there is something wrong it always shows null
for (int i = 1; i < 100; ++i) {
// Load the plist
NSString *plist = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%d",i] ofType:#"mp3"];
if (nil == plist) {
NSLog(#"not found (%i)", i);
continue;
}
NSLog(#"Found Songs (%i)",i);
[MYNSMUTABLEArray addObject:[NSNumber numberWithInt:i]];
NSLog(#"%#",MYNSMUTABLEArray);
}
the i variable is working absolutely fine
You need to create your mutable array, so that an object exists to store the data. I presume that the code at the moment is sending the addObject: message to nil.
I'd strongly suggest a better variable name than MYNSMUTABLEArray, so here is my code snippit that should go before the loop.
NSMutableArray* myMutableArray = [NSMutableArray alloc] init];
Then where you are adding the object, you'd use:
[myMutableArray addObject:#(i)];
Bootnote: A little tip, you can use # literals to automatically box your primitive int value into an NSNumber object. This is used in the addObject: example, and is in the form #(int).

Resources