Objective C: Replace array with "valueForKey" - ios

I have currenty something (in my opinion) complex.
I have a project from someone else and I have to do some little changes.
Now to my problem:
I have an array that looks like this:
(
"class:BPPlaceRate,0x1bcc0330, GUID:319F45BDF11AAE313159998513BF0B2C \nproperty: \n{\n identifier = \"barrierefreier-opnv\";\n rating = 5;\n}"
)
This is the result of NSLogging the array I want to change.
I nerver saw an array like this one.
So what I want to change is the value for "rating".
I tried this with:
[[self.contentArray valueForKey:#"rating"] replaceObjectAtIndex:i withObject:ratingArray[0][i]];
In "ratingArray" are the new values.
With this code I get an Error:
-[__NSArrayI replaceObjectAtIndex:withObject:]: unrecognized selector sent to instance
I know that it won't work. I tried to put the [self.contentArray valueForKey:#"rating"] in a tempArray. In this array I could change the values, but how can I transfer the values from the tempArray to the rating of self.contentArray?
Hope you can help me.

This array
(
"class:BPPlaceRate,0x1bcc0330, GUID:319F45BDF11AAE313159998513BF0B2C \nproperty: \n{\n identifier = \"barrierefreier-opnv\";\n rating = 5;\n}"
)
contains only one object. You see the output of this object's description method. I'm not sure whether the rating property is writeable by KVC - you can check it if you have source for BPPlaceRate class.
You can try to change the property this way:
id object = [self.contentArray firstObject];
[object setValue:ratingArray[0][i] forKey:#"rating"];

Related

how to parse a dictionary inside an array, which is in a dictionary

In my JSON response i receive a dictionary, which has a array, inside that array is a dictionary with few key-values, to make it simple to understand here is the JSON response:
{
weather = (
{
description = haze;
icon = 50n;
id = 721;
main = Haze;
}
);
}
I want to know how can i retrieve the elements description, icon, id and main. Suppose i have a NSString called str, how can i add the value of description to this string. I have tried many different things, but none seem to work. The one i thought should work, but didn't is:
str = [[[dir valueForKey:#"weather"] objectAtIndex:0] valueForKey:#"description"];
Can someone let me know how can i access those 4 values. I don't want to create a new array of those values and then retrieve them. Regards.
Use NSJSONSerialization to convert your JSON data to NSObjects.
Then you need to index into your objects based on their structure.
If the structure is fixed, and guaranteed, you can do it with fixed indexes/keys:
//data in theJSONDict.
NSArray *outerArray = theJSONDict[#"weather"];
NSDictionary *firstWeatherItem = outerArray[0];
NSString *firstItemDesc = firstWeatherItem[#"description"];
You could also do it all in 1 line:
firstItemDesc = theJSONDict[#"weather"][0][#"description"];
But that is much harder to debug.
If this is JSON data coming from a server you need to code more defensively than above, and write code that tries to fetch keys from dictionaries, tests for nil, and either loops through the array(s) using the count of objects, or tests the length of the array before blindly indexing into it. Otherwise you'll likely crash if your input data's format doesn't match your assumptions.
(BTW, do not use valueForKey for this. That is KVO code, and while it usually works, it is not the same as using NSArray and NSDictionary methods to fetch the data, and sometimes does not work correctly.)

Error : replaceObjectAtIndex:withObject:]: mutating method sent to immutable object

My applicaiton crashes with the exception mentioned in the title. Please find below my explanation :
tried to store array values using NSUserDefaults in my TableViewController as under :
func didDismissContentViewController(controller:ContentViewController,pageIndex:Int) { //You're passed in the contentViewController here, use relevant data to update your model. NSLog("Inside delegate method ..\n") var currentIdx = pageIndex
NSLog("Value of index edited \(pageIndex)")
NSLog("Current edited value is : \(controller.myTextView.text)")
**pageContent.replaceObjectAtIndex(pageIndex, withObject: controller.myTextView.text)**
**//pageCotent is defined as an NSMutableArray only...**
NSUserDefaults.standardUserDefaults().setObject(pageContent as NSMutableArray, forKey: "PageValues")
NSUserDefaults.standardUserDefaults().synchronize()
}
Retrieval is done as under : if var storedPageContent = NSUserDefaults.standardUserDefaults().objectForKey("PageValues") as? NSMutableArray{ pageContent = storedPageContent
While I am able to store, retrieve and display the updated page values in their respective pages, subsequent transition back and forth between the tableviewcontroller and content view controller throws the below error:
015-12-21 00:51:46.592 PageApp[3943:203948] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent to immutable object' * First throw call stack:
I tried commenting out the store to NSUserDefault, and the applicaiton does not crash, whereas using the NSUserDefault to store values, causes this to happen, So the error appears when I store it in NSUserDefaults and then retrieve it and assign it to array.
any help on this will be appreciated.Exchange data between uitah
The arrays returned from NSUserDefaults are immutable, so you will need to make a mutable copy of the array and then re-set the array in NSUserDefaults, once you've modified it.
I don't know Swift well enough to give you a code example, but this is how it would be done in Objective-C:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefauts];
NSMutableArray *pageContent = [[userDefaults objectForKey:#"PageValues"] mutableCopy];
[pageContent replaceObjectAtIndex:pageIndex withObject:controller.myTextView.text];
[userDefaults setObject:pageContent forKey:#"PageValues"];
I ran into exactly the same problem yesterday. Apparently if you get an array object using NSUserDefaults it acts like an NSArray even if you specify it to be NSMutableArray.
So the way I solved this problem was using this way (Objective-C). Once I had passed it to another NSMutableArray I was able to add / remove objects from it.
NSArray *favArrayTemp = [[NSUserDefaults standardUserDefaults] objectForKey:#"MyFavServers"];
NSMutableArray favArray = [[NSMutableArray alloc] initWithArray:favArrayTemp];
I think the issue is even though you are assigning values retrieved from NSUserDefaults to a NSMutableArray, it is not actually creating a NSMutableArray as you expected. Can you try initialising a new NSMutableArray with the objects fetched from NSUserDefaults?
In Objective-C, it will be like
NSMutableArray *pageContent = [[NSMutableArray alloc] initWithObjects:object1,nil];
where object1 is the value fetched from NSUserDefaults. Sorry I did not know Swift
Use Swift native types.
Cast the type retrieved from NSUserDefaults to a native Swift collection type as var then it's mutable.
var pageContent = [String]()
...
let userDefaults = NSUserDefaults.standardUserDefauts()
pageContent = userDefaults["PageValues"] as! [String]
Replacing is pretty easy, too.
pageContent[pageIndex] = controller.myTextView.text
Side note: The forced unwrapping while reading the value from NSUserDefaults assumes that the key/value pair has been registered properly. Then the unwrapping is safe.
Thank you for the response. I made changes as suggested by you and it worked. Let me share it below:
// As mentioned by you, since NSUserDefault always expects an NS Array, I did the storing as under :
NSLog("I am inside the bactktomonths ..\n")
//Defined an NS Array to copy the Mutable array to Immutable array
var storedArray = NSArray()
storedArray = pageArray!
NSUserDefaults.standardUserDefaults().setObject(storedArray , forKey: "PageValues")
NSUserDefaults.standardUserDefaults().synchronize()
}
// Retrieval.
if (NSUserDefaults.standardUserDefaults().objectForKey("PageValues") != nil){
pageContent = NSUserDefaults.standardUserDefaults().objectForKey("PageValues")?.mutableCopy() as NSMutableArray
// I am not sure If I need to execute the below code, but I have left it as suggested and it works currently !
NSUserDefaults.standardUserDefaults().setObject(pageContent, forKey: "PageValues")
Thanks

Extracting NSManagedObject attribute values from NSOrderedSet

I have an initializer that takes an array of Strings as a parameter. Rather than re-write the class and risk breaking things, I'd prefer to feed the initializer what it wants.
I'm trying to extract NSStrings from NSManagedObjects stored in an NSOrderedSet.
Here's what I've tried:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// this prints out the object in console
print("\(element)")
// this, doesn't give me accessors for the sound object
// print("\(element.fileName)")
}
I'm missing something basic, but I'm not sure what. How would I enumerate the objects in an NSOrderedSet and extract the value of the attributes for the entities contained within the set?
I would suggest reading the documentation on KVC (Key Value Coding) as you can write this as one line of code:
let filenameArray = soundsToPlay.valueForKey("fileName").array()
The call to valueForKey will return an NSOrderedSet of the string and then you can convert that to an array with a call to array()
I figured it out. I was missing a step:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// I have to tell the compiler what type of thing my thing is
let whatIWantToAccess = element as! MyObjectINeedAccessorsFor
print("\(whatIWantToAccess.fileName)")
}
Thats probably because compiler thinks that "element" is instance of NSManagedObject and it does not have fileName , try explicit type-casting , something like
for element: YourClass in soundsToPlay

How to change one value in NSMutableDictionary

I have a NSMutableDictionary with one key and it has the following values.
How can I change the value in item2 from 1 to 0? That's the only change I need.
The only way I have found is to recreate this whole key from scratch, change that one value and then delete that key and replace it with a new one. Is there any easier solution than that?
I will assume you get dict as an instance of NSMutableDictionary somehow:
NSMutableArray *tmp = [dict[#"07:00 AM.infoKey"] mutableCopy];
tmp[2] = #"0";
dict[#"07:00 AM.infoKey"] = tmp;
Unfortunately with nested arrays in dictionaries, the arrays aren't automatically mutable so there isn't any better method to doing so, like you observed.

Objective-C NSArray can't be changed when passed to method in different class

I have an NSArray and I need to change the order of the items within it. I have written a method that will determine the new order:
+(NSArray*)sortProxyForms:(NSArray*)arrayOfForms
{
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
arrayOfForms = [sortedForms copy]; // DOES NOT WORK
return sortedOfForms; // WORKS IF ASSIGNED IN THE CALLER
}
So, I can pass the NSArray to be sorted into the method. I call the method like this:
[HPSModelUtilities sortProxyForms:_formProxies];
If I actually try setting arrayOfForms (a reference to _formProxies) within the method then once I have returned from the method then the array is unchanged.
However, if I return the sorted array from the method and assign it to the NSArray in the calling method then the assignment works:
_formProxies = [HPSModelUtilities sortProxyForms:_formProxies]; // _formProxies NSArray is changed
_formProxies is declared in the calling class, and "HPSModelUtilities" is a different class.
How come the NSArray can be changed in the caller, but not changed in the called method, even though it is passed by reference?
When you pass a value into a method it is copied. This is called "pass by value". The arrayOfForms you are passing in is a pointer to an NSArray. This means that the pointer is copied when passed in. Redirecting this pointer to another instance of an NSArray does not change where the original pointer is pointing.
I would rename your method to (NSArray*)sortedArrayFromProxyForms:(NSArray*)proxyForms
If you really want to change where your NSArray reference is pointing in the method. Do it like this.
+ (void)sortProxyForms:(NSArray**)proxyForms {
*proxyForms = sortedForms;
}
You are passing a copy of the array reference (subtly different than passing by reference), but then you are changing where that reference points with this line:
arrayOfForms = [sortedForms copy];
arrayOfForms no longer points to the array instance you passed, but to a different array. You could pass a pointer of pointer, and change where the caller's pointer is pointing, but for what you are doing, I think the reassignment is fine.
If you'd really like here's what your function would look like with pointer of pointer:
+(void)sortProxyForms:(NSArray**)arrayOfForms {
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
*arrayOfForms = [sortedForms copy];
}
but I'll add the caveat that this isn't a pattern you see often in objective-c, so I'd avoid it when there are other alternatives available.
Also note when calling this function you need to add the & to get the extra level of indirection:
[HPSModelUtilities sortProxyForms:&_formProxies];

Resources