Objective C : Writing into a Plist - objective-c

Even though i know there are at least 2 or 3 topics with this name, i didnt find a proper answer so far to my problem :
I want to edit a Plist (which has been created by zwoptex (image/animations program)) in order to divide every number in it by 2.
So in my plist i do have some keys like "spriteOffset" with {{182, 160}, {58,75}} or {192, 165} as value. Those are NSStrings, and i just want to modify the numbers so i need to check if there's a "{" or a space or such, then casting the number.
The thing is i don't really know how to do it.....
Also, it seems that i'm missing something with my plist management. I've put some NSLogs for displaying every of those strings in my plist, but.... nothing gets displayed...
So here is my code :
-(void)DivideValues
{
for(NSString * plistName in plistSubpathsByName)
{
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:#"%#.plist",plistName]];
for(NSDictionary * sprite in [infoDict objectForKey:#"frames"])
{
for(NSString * string in [infoDict objectForKey:#"spriteColorRect"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteOffset"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteSize"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"spriteSourceSize"])
{
NSLog(#"%#",string);
}
for(NSString * string in [infoDict objectForKey:#"textureRect"])
{
NSLog(#"%#",string);
}
}
}
}
Thanks for any response, and i wish you all good luck for your career/passion

First of all, you should replace [infoDict objectForKey:#"spriteColorRect"] with [sprite objectForKey:#"spriteColorRect"], since the sprite is probably the dict containing further information.
You don't see any logs because -objectForKey: returns nil for a key that does not exist.
For changing the values, you might try to create a CGPoint or CGRect from the string, then changing it and finally converting it back to a string. (CGPointFromNSString() and NSStringFromCGPoint)
To save the modified version of your dictionary use NSDictionary's -writeToFile:atomically:.

The reason you example logs nothing is most likely because your inner for..in loops are probably looking in the wrong dictionary: the outer loop gets a dictionary sprite, so shouldn't the inner loops be looking at keys in that dictionary?
If you want to read a property list in, change some values in it, and write the same property list back out, you might find it useful to look at the NSPropertyListSerialization class -- it lets you quickly get a structure of mutable arrays/dictionaries from plist data, so you can iterate into them however you'd like to change values within, then serialize the whole thing back to data again. (If you use dictionaryWithContentsOfFile: you'll get a mutable dictionary, but all the containers within it will be immutable, so you'd have to do mutableCopy and swizzle contents all over the place during your iteration.)
No time to write up more detail at the moment, but I might edit the answer later if looking up the docs for NSPropertyListSerialization doesn't help you.

Ok I did succeed so if anyone is interested here is the code :
-(void)DivideValues
{
for(NSString * xflName in [xflSubpathsByName objectEnumerator]){
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:[sourceFolder stringByAppendingPathComponent:xflName]];
NSDictionary * dictionary = [infoDict objectForKey:#"frames"];
NSMutableDictionary * mutabledictionary = [[dictionary mutableCopy] autorelease];
for(NSString * pngFileName in dictionary) {
NSDictionary * sprite = [dictionary objectForKey:pngFileName];
NSLog(pngFileName);
NSMutableDictionary * mutablesprite = [[sprite mutableCopy] autorelease];
NSString * newstring = [self castSpriteRect:[sprite objectForKey:#"spriteColorRect"]];
[mutablesprite setObject:newstring forKey:#"spriteColorRect"];
newstring = [self castSprite:[sprite objectForKey:#"spriteOffset"]];
[mutablesprite setObject:newstring forKey:#"spriteOffset"];
newstring = [self castSprite:[sprite objectForKey:#"spriteSize"]];
[mutablesprite setObject:newstring forKey:#"spriteSize"];
newstring = [self castSprite:[sprite objectForKey:#"spriteSourceSize"]];
[mutablesprite setObject:newstring forKey:#"spriteSourceSize"];
newstring = [self castSpriteRect:[sprite objectForKey:#"textureRect"]];
[mutablesprite setObject:newstring forKey:#"textureRect"];
[mutabledictionary setObject:mutablesprite forKey:pngFileName];
}
[infoDict setObject:mutabledictionary forKey:#"frames"];
[infoDict writeToFile:[sourceFolder stringByAppendingPathComponent:xflName] atomically:NO];
}
if(!cancelling)
++digestStage;
else
digestStage = End;
}
-(NSString *)castSprite:(id)obj{
CGPoint point = NSPointFromString((NSString *)obj);
int i = (int)point.x%2 == 0 ?(int)point.x/2:1+(int)point.x/2;
int j = (int)point.y%2 == 0 ?(int)point.y/2:1+(int)point.y/2;
NSString * res = [NSString stringWithFormat:#"{%d, %d}",i,j];
return res;
}
-(NSString *)castSpriteRect:(id)obj{
CGRect point = NSRectFromString((NSString *)obj);
int i = (int)point.origin.x%2 == 0 ?(int)point.origin.x/2:1+(int)point.origin.x/2;
int j = (int)point.origin.y%2 == 0 ?(int)point.origin.y/2:1+(int)point.origin.y/2;
int y = (int)point.size.width%2 == 0 ?(int)point.size.width/2:1+(int)point.size.width/2;
int x = (int)point.size.height%2 == 0 ?(int)point.size.height/2:1+(int)point.size.height/2;
NSString * res = [NSString stringWithFormat:#"{{%d, %d}, {%d, %d}}",i,j,y,x];
return res;
}

Related

Array Issue in Objective C Xcode

I have a problem with my algorithm for calculating a sore. The user enters a word into the UITextField, and if the word matches a string in the array (#"The Word") the int 'score' will be added by 1.
Then the int score is set as a label as the user gets a word right. (DISPLAYING THE SCORE)
THE PROBLEM, a user can just keep on entering the same word over and over again and the score will keep going up by one. IS there a command for knowing if a word has already been entered, so you can only use the word once.
The Code
NSArray *scoreArray1 = [NSArray arrayWithObjects:
#"Word 1", #"Word 2", #"Word 3", nil];
NSString *inputtwo =_EnterNameText.text;
BOOL isItright = NO;
for(NSString *possible in scoreArray1) {
if([inputtwo isEqual:possible] ) {
isItright = YES;
break;
}
}
if(isItright) {
static int myInt = 0;
myInt++;
NSString *score = [NSString stringWithFormat:#"%d", myInt];
[_scorelabel setText:score];
}
UPDATE!!!!!!
NSArray *scoreArray1 = [NSArray arrayWithObjects:
#"Alan Shearer", #"Shearer", #"Andrew Cole", #"Andy Cole", #"Cole", #"Thierry Henry", #"Henry", #"Robbie Fowler", #"Fowler", #"Frank Lampard", #"Lampard", #"Michael Owen", #"Owen", nil];
NSSet *set2 = [NSSet setWithArray:scoreArray1];
NSString *inputtwo =_EnterNameText.text;
BOOL isItright = NO;
for(NSString *possible in set2) {
if([inputtwo isEqual:possible] ) {
isItright = YES;
break;
}
}
if(isItright) {
static int myInt = 0;
myInt++;
NSString *score = [NSString stringWithFormat:#"%d", myInt];
[_scorelabel setText:score];
}
HOWEVER NOW THE APP DOES NOT WORK, IT CRASHES, any suggestions?
Why don't you keep a second Array where you store the given (correct) answers.
Whit this you can just do a contains inside your if....problem solved.
a second option is not to put string in your array but "Answer" Objects, that have a field that you can flag as already used.
You could just create an NSMutableSet and put a copy of the word into there whenever one is entered. Then you just need to check if the word exists in the set before incrementing the score.
I'm suggesting a set because it uses hashed access, so lookups are fast. Also, if you add the same string more than once, the set will still only have one reference to the string.
Actually, if you have an array of "legal" words, the way to go is to simply remove each word as it's called out, until the array gets to be zero entries long.
NSMutableArray* scoreArrayCopy = [NSMutableArray arrayWithArray:scoreArray];
int originalCount = scoreArrayCopy.count;
...
while (scoreArrayCopy.count > 0) {
NSString* guess = <get next guess>;
[scoreArrayCopy removeObject:guess];
score = originalCount - scoreArrayCopy.count;
}
(If you have a lot of words things would be more efficient if you used an NSMutableSet instead of an NSMutableArray, but the logic would be the same.)

extracting data from an array of arrays

I have the following code:
NSString *movies = [[NSString alloc] initWithData:webData7 encoding:NSUTF8StringEncoding];
NSLog(#"movies: %#", movies);
array_webdata = [parsedata objectWithString:movies error:nil];
userMovies = [array_webdata valueForKey:#"movies"];
NSLog(#"userMovies: %#", userMovies);
NSMutableArray *arrayCodes = [userMovies valueForKey:#"rtid"];
NSLog(#"arrayCodes: %#", arrayCodes);
In first line I save in movies the following:
movies:
{"movies":[["rtid","770672122"],["rtid","771268706"],["rtid","771240265"],["rtid","9377"]]}
with: userMovies = [array_webdata valueForKey:#"movies"]; I store in userMovies:
userMovies: (
rtid,
770672122
),
(
rtid,
771268706
),
(
rtid,
771240265
),
(
rtid,
9377
)
From here I want to extract the numbers to create another array like :
codes: (770672122, 771268706, 771240265, 9377)
But with following line, the execution crashes:
NSMutableArray *arrayCodes = [userMovies valueForKey:#"rtid"];
The error is EXC_BAD_ACCESS. On Debug Area shows no errors
Do you know what is wrong?
Thanks!
I have edited the question to make it more understandable
The code you have is clearly not going to work because you are assigning a number (or int, or some type) to a mutable array. You can't do that. You need to ADD them to the array since they will be contained IN THE ARRAY, those numbers ARE NOT arrays...
How are you getting it from JSON? Your array is probably an array of NSDictionary's so you'll want to do something like:
for (NSDictionary *aDictionary in userMovies)
{
[arrayCodes addObject: [aDictionary valueForKey:#"rtid"]];
}
You need to start by finding out what's in your array. Do:
NSLog(#"My array contains %#'s", [[userMovies valueForKey:#"rtid"] class]);
From there, if you get NSDictionary use the code above, and if you get an array, you'll have to loop through the array like:
for (NSArray *anArray in userMovies)
{
[arrayCodes addObject: [anArray objectAtIndex:1]];
}
SOLVED!
I have deleted my last two lines of code:
NSMutableArray *arrayCodes = [userMovies valueForKey:#"rtid"];
NSLog(#"arrayCodes: %#", arrayCodes);
and added the following
NSMutableArray *moviesCodes = [[NSMutableArray alloc] init];
for (int counter = 0; counter<[userMovies count]; counter++){
[moviesCodes addObject:[[userMovies objectAtIndex:counter] objectAtIndex:1]];
}
With this I have stored all the ids in the array called moviesCodes. The problem was that userMovies is an array of arrays

iOS asserting NSMutableArray contains 2 objects, regardless of index

I have the following test case in my iOS application :
-(void) testTwoDefaultUsersExist
{
NSString * expected;
NSString * actual;
expected = #"John Smith";
actual = [[[userService getAllUsers]objectAtIndex:0] fullName];
STAssertEqualObjects(expected, actual, #"Not equal");
expected = #"Dave Brown";
actual = [[[userService getAllUsers]objectAtIndex:1] fullName];
STAssertEqualObjects(expected, actual, #"Not equal");
}
The above just checks that my call to [userService getAllUsers] returns 2 User objects, one with a name of John Smith, the other with Dave Brown. This appears to work fine for this scenario, but I have other cases where that ordering may change, so John may be placed in index 1 rather than 0
Question : How can I assert that the NSMutableArray, being returned from the call to [userService getAllUsers] contains those 2 objects, regardless of ordering?
Can you not simply use the NSArray method -containsObject:? An NSMutableArray is still an NSArray, so you can do:
NSArray * expected = [NSArray arrayWithObjects:#"John Smith", #"Dave Brown", nil];
NSArray * actual = [[userService getAllUsers] valueForKey:#"fullName"];
for(NSString * name in expected) {
STAssertTrue([actual containsObject:name], #"Missing name");
}
Note the (ab)use of -valueForKey: to transform an array of user objects into an array of NSString objects, making the -containsObject: call simpler. This will only work if your user object is key-value coding compliant for the fullName property.
NSMutableArray always contains the elements as you insert them to the array
You can iterate over the elements that you insert and test if they're at the NSArray using:
- (NSUInteger)indexOfObject:(id)anObject
If the object is not found it returns NSNotFound, that can be used with the Unit Test Framework that you choice
Greetings
Assert on equality like this
NSAssert1([userService getAllUsers].count == 2, #"SomeDescription", nil);
If you want to search an array for the existence of some strings, use the following function
- (BOOL) containsAllNames:(NSArray*)arrToSearch namesToSearch:(NSArray*)arr
{
BOOL containsAll = YES;
for (NSString *name in arr) {
BOOL containsCurrent = NO;
for (NSString *nameToSearch in arrToSearch) {
if ([name isEqualToString:nameToSearch]) {
containsCurrent = YES;
break;
}
}
if (!containsCurrent) {
containsAll = NO;
}
}
return containsAll;
}
Call it like
NSArray *toSearch = [NSArray arrayWithObjects:#"John Smith", #"Dave Brown", nil];
[self containsAllNames:YourArray namesToSearch:toSearch];

Getting the size of an array

i have some code that requires the use of a for loop to read variables from an array.
int size=sizeof names;
NSLog(#"thelast one is %d",size);
NSString *usersName=userName.text;
NSString *usersPass=passWord.text;
for (i=0; i<=size;i++){
NSString *namesArray=[names objectAtIndex:i];
NSString *passArray=[pass objectAtIndex:i];
NSLog(#"namesArray %#",namesArray);
NSLog(#"passArray %#",passArray);
if([namesArray isEqualToString:usersName]){
userValid=1;
NSLog(#"The content of arry4 is %#",namesArray);
}
if([passArray isEqualToString:usersPass]){
passValid=1;
NSLog(#"The content of arry4 is %#",passArray);
}
else {
userValid=0;
passValid=0;
}
}
I've been having some problems because every time this function is called from within the program, it's almost as if the 'sizeof names' is wrong, therefore not all values in the array are checked.
I'm generally a Java programmer so i'm used to names.length, and i was told sizeof names is essentially the same thing... any help?
Cheers.
Don't use sizeof. Use [names count].
You want to use [names count] not sizeof names. Sizeof is going to give you the size of the actual names object pointer itself and not the number of elements, since it's dynamic memory type.
To get the number of elements stored in an NSAarray you should use the instance method count, which returns an NSUInteger.
Alternatevely, you can iterate over these elements using the for in loop, which is available also in Java, if I recall correctly.
for (MyClass *element in myArray) {
NSLog(#"%#", element);
}
Note that sizeof is a C operator that returns the size in bytes of its operand, so it doesn't tell you how many elements are stored in that NSArray, but the size in bytes of one NSArray instance.
I know your question has already been answered - but here is a more Cocoa way of writing it
NSString *userName = userName.text;
NSString *userPass = passWord.text;
// Use a block enumerator
NSUInteger nameIdx = [names indexOfObjectPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return ([obj isEqualToString:userName]);
}];
// Is the name in the array
if (nameIdx == NSNotFound) {
// Name not in array - so set to zero
userValid = 0;
passValid = 0;
} else {
userValid = 1;
// See if the corresponding password is correct
NSString password = [pass objectAtIndex:nameIdx];
if (![password isEqualToString:userPass]) {
passValid = 0;
} else {
passValid = 1;
}
One can also use Fast Enumeration, in some cases it can be more clear to a reader:
NSString *userName = userName.text;
NSString *userPass = passWord.text;
BOOL userValid = NO;
BOOL passValid = NO;
int index = 0;
for (NSString *eachName in namesArray) {
if ([eachName isEqualToString:userName) {
userValid = YES:
if ([[passArray objextAtIndex:index] isEqualToString:passWord) {
passValid = YES;
}
break;
}
index += 1;
}

Works with number of string contained in NSArray

I need to pick string valur from an NSMutableArray then save it into a plist. I've builded an NSMutableArray to display infos in table View. Maximum allowed index is 8. (paste just two in example)
The problem if the String doesn't exist, I get the following error:
sDict is a dictionary for saving datas to a property list file.
the code:
- (IBAction)save:(id)sender {
(...)
NSString *One;
NSString *Two;
...etc
if ([self.smOne objectAtIndex:0])
One = [self.smOne objectAtIndex:0];
if ([self.smOne objectAtIndex:1])
Two = [self.smOne objectAtIndex:1];
...etc
if (One)
[sDict setObject:[self.smTwo objectAtIndex:0]
forKey:[UserM stringByAppendingString:One]];
[sDict setObject:[self.smThree objectAtIndex:0]
forKey:[UserS stringByAppendingString:One]];
[sDict setObject:[self.smFour objectAtIndex:0]
forKey:[UserP stringByAppendingString:One]];
if (Two)
[sDict setObject:[self.smTwo objectAtIndex:1]
forKey:[UserM stringByAppendingString:Two]];
[sDict setObject:[self.smThree objectAtIndex:1]
forKey:[UserS stringByAppendingString:Two]];
[sDict setObject:[self.smFour objectAtIndex:1]
forKey:[UserParM stringByAppendingString:Two]];
...etc
}
This code works if all objects are present, but fails if it miss one of the object at index.
I really don't know how to check properly if the object is present or not, cause code above seem's to don't works well.
I've tried with [self.smOne count] but as problem to pass as a Int or String to make conditions with.
Thanks for answer.
it looks like you're explicitly checking smOne from indices 1 through 8. But you also mentioned that the array can have up to 8. So if it's missing, say, 6, 7 and 8, you'd still be calling [smOne objectAtIndex:6], which would result in an NSRangeException being raised as 6 is out of bounds for the array.
try this instead:
int i = 0;
for ( NSString *aString in self.smOne )
{
[sDict setObject:[self.smTwo objectAtIndex:i]
forKey:[UserM stringByAppendingSting:aString]];
[sDict setObject:[self.smThree objectAtIndex:i]
forKey:[UserS stringByAppendingString:aString]];
[sDict setObject:[self.smFour objectAtIndex:i]
forKey:[UserP stringByAppendingString:aString]];
i++;
}
it'll go through each object in the smOne array and add the object into sDict regardless of how many items you have in smOne.
also, be careful with how you're generating your keys. there's the possibility that [UserM stringByAppendingSting:aString] won't always be unique.
Sorry to ask again but i have dificulties to find how to rebuild arrays from the key/string couple saved with the loop.
i've tried this:
int i = 0;
for (NSString *SMServ in [temp objectForKey:
[UserMen stringByAppendingFormat:#"%d",i]]){
NSString *SMMem[i];
SMMem[i] = [temp objectForKey:[UserMen stringByAppendingFormat:#"%d",i]];
NSArray *theArray = [ NSArray arrayWithObjects:SMMem count:i];
i++;
}
But nothing happens.
if i try with for (i = 0; i < 9; i ++) and (i + 1) instead of [i], i get same bounds errors in the first example.
thanks again for help.
Well, finally it ,works. Thanks for link to the documentation, i read too fast last time.
I'm sure this is not the cleanest way, but it works with this loop:
for (key in sDict) {
if ([sDict valueForKey:[UserMen stringByAppendingFormat:#"%d",i]]) {
tempMe = [sDict valueForKey:[UserMen stringByAppendingFormat:#"%d",i]];
if (tempMe) {
[manArray insertObject:tempMe atIndex:(0 + a)];
[bankArray insertObject:[NSString stringWithFormat:#"%d",i] atIndex:(0 + a)];
a++;
}
}
if ([sDict valueForKey:[UserSerial stringByAppendingFormat:#"%d",i]]) {
SMSer = [sDict valueForKey:[UserSerial stringByAppendingFormat:#"%d",i]];
if (SMSer) {
[serArray insertObject:SMSer atIndex:(0 + b)];
b++;
}
}
if ([sDict valueForKey:[UserPart stringByAppendingFormat:#"%d",i]]) {
SMPart = [sDict valueForKey:[UserPart stringByAppendingFormat:#"%d",i]];
if (SMPart) {
[partArray insertObject:SMPart atIndex:(0 + c)];
c++;
}
}
i++;
}