Getting mutable array of dictionaries then adding objects in Objective-C - ios

I'm translating two different apps from Swift to Objective - C and one thing I'm hung up on is a global property of an array of mutable dictionaries.
Here is the syntax and Swift and the code where the objects are added,
1st app (adding places to a dictionary for a map app)
declared at top of class
var places = [Dictionary<String, String>()]
objects added
places.append(["name":"Taj Mahal","lat":"27.175277","lon":"78.042128"])
2nd app (check to see if a userID on parse is following another user)
declared at top of class
var isFollowing = ["":false]
objects added
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
if objects.count > 0 {
self.isFollowing[user.objectId!] = true
} else {
self.isFollowing[user.objectId!] = false
}
}
To do this in Objective-C is it necessary to set up an NSObject subclass with an initWithDictionary method?
Any suggestions? Thanks in advance!

No you won't need to create a custom class to do this.
Create the class-level array by defining this in the SomeClass.m file:
static NSMutableArray *_places = nil;
Use a singleton-pattern to initialize and retrieve it:
+ (NSMutableArray *)places
{
if (!_places)
_places = [NSMutableArray new];
return _places;
}
Add to it like this:
NSMutableArray *places = [SomeClass places];
[places addObject: #{
#"name" : #"Taj Mahal",
#"lat" : #(27.175277), // Note: NSNumber, not NSString
#"lon" : #(78.042128) // ditto
}];
The second app doesn't need a global array by the look of it; simply a #property containing the user-id:
#interface SomeClass : NSObject
#property NSUInteger followingUserId;
#end
And then set/compare the user-id as necessary:
NSUInteger incomingUserId = ...;
if (incomingUserId != self.followingUserId) {
// Set new user-id?
self.followingUserId = incomingUserId;
}

Related

How to story multiple values for one object in NSMutableDictionary?

I would like to story many items in one object (the name of the user, their photo, and the time the photo was taken). I have tried to use three different arrays to do this and it somewhat works, however since I am loading this into a tableview its not the best way to do this. I am thinking about using a NSMutableDictionary but I can't seem to do it. Any help would be appreciated!
You can do like this.
NSDictionary *dic = [[NSDictionary alloc]initWithObjectsAndKeys:
#"Jone",#"UserName",
#"Jone.png",#"Photo",
[NSDate date],#"Time",nil];
NSMutableArray *userArray = [[NSMutableArray alloc] init];
[userArray addObject:dic];
//NSLog(#"%#",userArray);
for (int i=0; i<userArray.count; i++) {
NSDictionary *userDictionary = [userArray objectAtIndex:i];
NSLog(#"UserName:%#",[userDictionary valueForKey:#"UserName"]);
NSLog(#"Photo:%#",[userDictionary valueForKey:#"Photo"]);
NSLog(#"Time:%#",[userDictionary valueForKey:#"Time"]);
}
Note:I think you need to use "NSMutableArray" of NSDictionary. if you all user property (username,photo,time...) are unpredictable the only you need to use NSMutableDictionary.
What you want to do is create a User Entity (Swift/Objective-C Class) with username, photo and photoTime fields.
Create the users, put drop them in an array and then read from it while you are creating the table in cellForRowAtIndexPath.
#interface User : NSObject
#property (nonatomic, retain) NSString *userName;
#property (nonatomic, retain) NSString *photoURL;
#property (nonatomic, retain) NSString *photoTime;
#end
Then somewhere within your code where you create users...
NSMutableArray *usersArray= [[NSMutableArray alloc] init];
User *user= [[Useralloc] init];
user.userName=#"someUserName"
user.photoURL=#"http://somephotourl.com";
user.photoTime=#"1231232132121";
[usersArray addObject:user];
The data structure behind a UITableView is usually NSArray or a list of some kind. Each object in the array should represent a row in the table.
Following this pattern your structure should look like that:
var users = [User]()
struct User {
let name: String
let photo: UIImage
let date: NSDate
}
// This struct could also be a dictionary
var user = [String: AnyObject]()
user["name"] = ..
user["photo"] = ..
user["date"] = ...
Instantiate User objects and add them to the users array when appropriate.
Your UITableView should find the correct user and update the cell accordingly in the dataSource method:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = .. // Dequeue cell
let user = users[indexPath.row] // Get the user for this row
cell.textLabel.title = user.name // Update cell properties
// etc..
// OR using a dictionary
cell.textLabel.title = user["name"]
// etc..
return cell

Updating an NSMutableArray passed into a method

I'm very new to objective-c so be easy :-) I have a container object, "Data", who has a number of NSMutableArrays.
Data.h
#interface Data : NSObject{
NSMutableArray *one;
NSMutableArray *two;
}
#property (nonatomic, retain) NSMutableArray *one;
#end
and would like to pass it to a load method in which case it will update each corresponding array in the Data class.
Parser.h
+ (Parser *)load:(Data*) store;
Parser.m
+ (Parser *)load:(Data *) store {
...
[store.one addObject:name.stringValue];
}
But no matter what I do the string in "name.stringValue" doesn't get appended to the array. Is there something I'm missing when passing in the "Store" data object to the parse method? Let me know if I should provide more details but I feel this covers the issue.
Check in your implementation of Data that you are properly initializing the mutable arrays - here is a simple example below given your Data interface:
#import "Data.h"
#implementation Data
{
#pragma mark - Properties
- (NSMutableArray *)one
{
if (!_one) {
_one = [[NSMutableArray alloc] init];
}
return _one;
}
- (NSMutableArray *)two
{
if (!_two) {
_two = [[NSMutableArray alloc] init];
}
return _two;
}
}
Use one = [NSMutableArray array]; before you start using it. This will create an empty mutable array.

Parse: Using PFObject subclasses with PFCloud calls

I have a PFObject subclass called MediaFile. In order to get back instances of my subclass from calls to the server, I've seen how you can construct queries out of Parse subclasses like this:
PFQuery *query = [MediaFile query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) { ... }];
However, most of the server calls in my app are custom Cloud calls, which return PFObjects. As soon as I've retrieved these objects I want to treat them as MediaFile objects and call custom methods on them that are defined in the MediaFile class. Casting does not work, because it does not actually construct a MediaFile object. What I've done to overcome this problem is construct new MediaFiles for each PFObject I get back using [MediaFile object], and then copy all the data into each one using a method I wrote called loadFromObject::
[PFCloud callFunctionInBackground:#"func" withParameters:#{} block:^(id objects, NSError *error) {
for (PFObject *object in objects) {
MediaFile *mf = [[MediaFile object] loadFromObject:object];
[array addObject:mf];
}
}];
In MediaFile.m:
- (MediaFile *) loadFromObject:(PFObject *)object {
NSArray *keys = [object allKeys];
for (NSString *key in keys) {
id obj = [object objectForKey:key];
[self setObject:obj forKey:key];
}
return self;
}
This works, but is there a more elegant way to address this problem?
How does your custom class defined?
Because their subclassing mechanism is relying on Key-Value-Coding(KVC).
Here's KVC Programming Guide. Or you can check out the guide over here.
The following sample should work.
In the header:
#interface CustomObject : PFObject <PFSubclassing>
#property (nonatomic, strong) NSString *propertyName;
In the implementation:
#import <Parse/PFObject+Subclass.h>
#pragma mark - columns of Parse table
#dynamic propertyName;
#pragma mark - regiester parse subclass in runtime
+ (void)load {
#autoreleasepool {
[self registerSubclass];
}
}
#pragma mark - parse cloud table name
+ (NSString *)parseClassName
{
return #"CustomObject"; // table name
}
TL;DR:
Make sure your subclass of PFObject is registered with Parse by the time your Cloud code returns those objects from a net call.
If you've subclassed PFObject using the instructions Parse includes in it's documentation guide, you can just make the following call before the first net call that returns your custom subclass:
CustomSubclass.initialize()
A good place to add this code is in your app delegate's application(didFinishLaunchingWithOptions:) method, where it will be likely to run before any other code related to your subclass.
Long version with explanation
My situation was a little different from yours, but I believe the problem is largely the same. My problem was that I was fetching objects through a PFQuery and the objects returned in the query callback were just generic PFObjects.
Here's what my code looked like:
// Scene is a PFObject subclass
func getLatestScenes(completion: ((scenes: [Scene]) -> Void)?) {
var query = PFQuery(className: SceneClassName)
query.findObjectsInBackgroundWithBlock { (results: [AnyObject]?, error: NSError?) -> Void in
if let scenes = results as? [Scene] {
// This would never run, as the members of `results` were only PFObjects
completion?(scenes: scenes)
} else {
// Code would always skip to the empty result case
completion?(scenes: [])
}
}
}
Individual members of the results array would print out in the debugger with descriptions that clearly stated that they were supposed to be of the class Scene, as intended, but when inspected they were only PFObjects.
(lldb) po results![0]
<Scene: 0x7fc743a8f310, objectId: YyxYH9dtBp, localId: (null)> {
creator = "<PFUser: 0x7fc74144b320, objectId: FV3cmDI1PW>";
sceneDataFile = "<PFFile: 0x7fc743a96920>";
}
(lldb) p results![0]
(PFObject) $R6 = 0x00007fc743a8f310 {
NSObject = {
isa = 0x00007fc743a8f310
}
...
// Info on object...
...
}
The problem here was that the Scene object was not properly registered with Parse as a PFObject subclass. The initialize method was correctly overridden like so:
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
but the initalize function wasn't being called by the time the query was called. The issue is that a class's initialize method is called right before the first message is sent to that class, but there was no message being sent to Scene before the query was performed. Adding the following line to my AppDelegate's application(didFinishLaunchingWithOptions:) solved the issue
Scene.initialize()
After that, Parse was able to infer which class on the client the incoming data the objects should be, and constructed the results array with that class.
It's simple after all. What worked for me is a simple assignment of the returned PFObject to the PFObject's Objective-C subclass. So in your case,
[PFCloud callFunctionInBackground:#"func" withParameters:#{} block:^(id objects, NSError *error) {
for (PFObject *object in objects) {
MediaFile *mf = object; // simple as that
[array addObject:mf];
}
}];

How to omit null values in JSON Dictionary using Mantle?

I have MyModel inheriting from MTLModel (using the GitHub Mantle pod).
MyModel.h
#import <Mantle/Mantle.h>
#interface MyModel : MTLModel <MTLJSONSerializing>
#property (nonatomic, copy, readonly) NSString *UUID;
#property (nonatomic, copy) NSString *someProp;
#property (nonatomic, copy) NSString *anotherProp;
#end
MyModel.m
#import "MyModel.h"
#implementation MyModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"UUID": #"id",
#"someProp": #"some_prop",
#"anotherProp": #"another"
};
}
}
#end
Now I want to send the JSON to the backend using AFNetworking. Before that I convert the model instance to a JSON NSDictionary to use as parameters/body payload within my request.
NSDictionary *JSON = [MTLJSONAdapter JSONDictionaryFromModel:myModel];
But this JSON consists of strange "" Strings for properties of my model that are nil. What i instead want is Mantle to omit these key/value pairs and just spit out a JSON with only the properties that are not nil or NSNull.null, whatever.
This is a common issue with Mantle and it's called implicit JSON mapping.
MTLJSONAdapter reads all properties of a model to create a JSON string optionally replacing property names with ones given in +JSONKeyPathsByPropertyKey.
If you want some properties to be excluded from the JSON representation of your model, map them to NSNull.null in your +JSONKeyPathsByPropertyKey:
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"UUID": #"id",
#"someProp": #"some_prop",
#"anotherProp": #"another",
#"myInternalProperty": NSNull.null,
#"myAnotherInternalProperty": NSNull.null,
};
}
The implicit JSON mapping has lately become a noticeable problem, a solution for which is currently being discussed at Mantle's home repository at GitHub.
See issues #137, #138, #143 and the current discussion under #149.
EDIT: I clearly misunderstood the question, but now, when I suppose I understand it correctly, the answer is simple.
MTLJSONAdapter generates the JSON data using MTLModel's dictionaryValue property. If you wish to exclude a property from the JSON itself, you can overwrite that method in your MYModel:
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *originalDictionaryValue = [[super dictionaryValue] mutableCopy];
if (self.aPropertyThatShouldBeExcludedWhenNil == nil) {
[originalDictionaryValue removeObjectForKey:#"aPropertyThatShouldBeExcludedWhenNil"];
}
/* repeat the process for other "hidden" properties */
return originalDictionaryValue;
}
EDIT #2: Check out the code* for removing all values that are nil:
- (NSDictionary *)dictionaryValue {
NSMutableDictionary *modifiedDictionaryValue = [[super dictionaryValue] mutableCopy];
for (NSString *originalKey in [super dictionaryValue]) {
if ([self valueForKey:originalKey] == nil) {
[modifiedDictionaryValue removeObjectForKey:originalKey];
}
}
return [modifiedDictionaryValue copy];
}
* - code sample suggested by matths.
I remove nil valued keys by creating an MTLJSONAdapter subclass, and overriding -serializablePropertyKeys:forModel: method.
MTLJSONAdapterWithoutNil.h
/** A MTLJSONAdapter subclass that removes model dictionaryValue keys whose value is `[NSNull null]`. */
#interface MTLJSONAdapterWithoutNil : MTLJSONAdapter
#end
MTLJSONAdapterWithoutNil.m
#import "MTLJSONAdapterWithoutNil.h"
#implementation MTLJSONAdapterWithoutNil
- (NSSet *)serializablePropertyKeys:(NSSet *)propertyKeys forModel:(id<MTLJSONSerializing>)model {
NSMutableSet *ms = propertyKeys.mutableCopy;
NSDictionary *modelDictValue = [model dictionaryValue];
for (NSString *key in ms) {
id val = [modelDictValue valueForKey:key];
if ([[NSNull null] isEqual:val]) { // MTLModel -dictionaryValue nil value is represented by NSNull
[ms removeObject:key];
}
}
return [NSSet setWithSet:ms];
}
#end
And use this to create JSON dictionary instead. Like this:
NSDictionary *JSONDictionary = [MTLJSONAdapterWithoutNil JSONDictionaryFromModel:collection error:nil];
NOTE: if you are overriding NSValueTransformer methods for array or dictionary properties, you also have to change the MTLJSONAdapter class to your subclass as well. Like this:
+ (NSValueTransformer *)myDailyDataArrayJSONTransformer {
return [MTLJSONAdapterWithoutNil arrayTransformerWithModelClass:KBDailyData.class];
}
Overriding - dictionaryValues did not give me the expected behavior
So I created a method for MTL Base class
- (NSDictionary *)nonNullDictionaryWithAdditionalParams:(NSDictionary *)params error:(NSError *)error {
NSDictionary *allParams = [MTLJSONAdapter JSONDictionaryFromModel:self error: &error];
NSMutableDictionary *modifiedDictionaryValue = [allParams mutableCopy];
for (NSString *originalKey in allParams) {
if ([allParams objectForKey:originalKey] == NSNull.null) {
[modifiedDictionaryValue removeObjectForKey:originalKey];
}
}
[modifiedDictionaryValue addEntriesFromDictionary:params];
return [modifiedDictionaryValue copy];
}
The EDIT #2 used to work for me with the previous Mantle code base. Now I have to do the following to continue using EDIT #2:
In file MTLJSONAdapter.m, replace this line:
NSDictionary *dictionaryValue = [model.dictionaryValue dictionaryWithValuesForKeys:propertyKeysToSerialize.allObjects];
with
NSDictionary *dictionaryValue = model.dictionaryValue;
The above is my current workaround to get
{ }
instead of
{
"AddressLine2" : null,
"City" : null,
"ZipCode" : null,
"State" : null,
"AddressLine1" : null
}

How to replace an object in an NSMutableArray at a given index with a new object

I have an NSMutableArray object (retained, synthesized as all) that is initiated just fine and I can easily add objects to it using the addObject: method. But if I want to replace an object at a certain index with a new one in that NSMutableArray, it doesn't work.
For example:
ClassA.h:
#interface ClassA : NSObject {
NSMutableArray *list;
}
#property (nonatomic, copy, readwrite) NSMutableArray *list;
#end
ClassA.m:
#import "ClassA.h"
#implementation ClassA
#synthesize list;
- (id)init
{
[super init];
NSMutableArray *localList = [[NSMutableArray alloc] init];
self.list = localList;
[localList release];
//Add initial data
[list addObject:#"Hello "];
[list addObject:#"World"];
}
// Custom set accessor to ensure the new list is mutable
- (void)setList:(NSMutableArray *)newList
{
if (list != newList)
{
[list release];
list = [newList mutableCopy];
}
}
-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
int i = [theIndex intValue]-1;
[self.list replaceObjectAtIndex:i withObject:newTitle];
NSLog((NSString *)[self.list objectAtIndex:i]); // gives the correct output
}
However, the change remains true only inside the method. from any other method, the
NSLog((NSString *)[self.list objectAtIndex:i]);
gives the same old value.
How can I actually get the old object replaced with the new one at a specific index so that the change can be noticed from within any other method as well.
I even modified the method like this, but the result is the same:
-(void)updateTitle:(NSString *)newTitle:(NSString *)theIndex
{
int i = [theIndex intValue]-1;
NSMutableArray *localList = [[NSMutableArray alloc] init];
localList = [localList mutableCopy];
for(int j = 0; j < [list count]; j++)
{
if(j == i)
{
[localList addObject:newTitle];
NSLog(#"j == 1");
NSLog([NSString stringWithFormat:#"%d", j]);
}
else
{
[localList addObject:(NSString *)[self.list objectAtIndex:j]];
}
}
[self.list release];
//self.list = [localList mutableCopy];
[self setList:localList];
[localList release];
}
Please help out guys :)
This does the trick:
[myMutableArray replaceObjectAtIndex:index withObject:newObject];
OK, there are a few bits of confusion here.
You don't need to take a mutableCopy of a newly created NSMutableArray to make it mutable. It's already mutable -- the clue is in the name. You only need to do that in the setter if you want the property to have copy semantics (which you've set, and may have good reason for, of course). But you certainly wouldn't need to do it as shown in your updated updateTitle code, and doing so leaks localList.
Also, you're mixing together property access via self.list and direct use of list in the same method. This is not invalid, but it's bad practice, because it means whatever other stuff the accessor methods do is being randomly bypassed. It's common for properties like this to do everything through self except in the accessors themselves, or in dealloc, and possibly in init (opinions seem to differ on this), where you would access the ivar directly.
Also, never call [self.list release] -- the property accessor doesn't give its caller ownership. Doing this will end in tears, mark my words.
None of this answers the real question, which is why is your change disappearing. The original updateTitle code does not explain this as far as I can see -- it should work. So I suspect that somewhere else you are calling self.list = theOriginalList and hence undoing your change.
Update:
Just for the sake of argument, I'm going to post what I think the code you posted is probably meant to look like. I've preserved your use of a string to pass the index to updateTitle, but I'd like to point out that doing it this way is wrong. It's a number, you should pass it as such. Even if the number comes from a text field or something, that's the caller's concern; the class interface should specify a number. Similarly the apparent change from 1-based to 0-based indexing. Please do not do this sort of thing implicitly, it is a recipe for weeping and gnashing of teeth.
ClassA.h:
#import <Cocoa/Cocoa.h>
#interface ClassA : NSObject
{
NSMutableArray* list;
}
- (void) setList:(NSMutableArray*)newList;
- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex;
#property (nonatomic, copy, readwrite) NSMutableArray* list;
#end
ClassA.m:
#import "ClassA.h"
#implementation ClassA
#synthesize list;
- (id) init
{
if ( self = [super init] )
{
list = [[NSMutableArray alloc] init];
[list addObject:#"Hello "];
[list addObject:#"World"];
}
return self;
}
- (void) setList:(NSMutableArray*) newList
{
if ( list != newList )
{
[list release];
list = [newList mutableCopy];
}
}
- (void) updateTitle:(NSString*)newTitle forIndex:(NSString*)theIndex
{
int i = [theIndex intValue] - 1;
[self.list replaceObjectAtIndex:i withObject:newTitle];
}
- (void) dealloc
{
[list release];
[super dealloc];
}
#end
This cleans up various issues, but note that updateTitle is mostly the same. If you drop all this in and the change still doesn't survive, you are definitely resetting list somewhere.
A more straight answer would be:
self.list[i] = newTitle;
This just works like
[self.list replaceObjectAtIndex:i withObject:newTitle];
Look at this line:
#property (nonatomic, copy, readwrite) NSMutableArray *list;
The copy means that whenever you access self.list, you don't get the "_list" instance variable of your object, but a copy of that list. If you write [self.list replaceObjectAtIndex... ] you replace an object in that copy of your list; the original _list is unchanged. Just use
#property (nonatomic, strong, readwrite) NSMutableArray *list;
And to avoid confusion, remove the "list" instance variable and the #synthesize statement, then use _list to access the instance variable.
For Swift you could try:
//if you have indexPath
self.readArray.removeAtIndex((indexPath?.row)!)
self.readArray.insert(tempDict, atIndex: (indexPath?.row)!)
//tempDict is NSDictionary object.
Finally Got Some Perfect Code,
let DuplicateArray: NSArray = array
let DuplicateMutableArray: NSMutableArray = []
DuplicateMutableArray.addObjectsFromArray(DuplicateArray as [AnyObject])
var dic = (DuplicateMutableArray[0] as! [NSObject : AnyObject])
dic["is_married"] = "false"
DuplicateMutableArray[self.SelectedIndexPath] = dic
array = []
array = (DuplicateMutableArray.copy() as? NSArray)!
//Output Will Be Like
array = [
{
"name": "Kavin",
"Age": 25,
"is_married": "false"
},
{
"name": "Kumar",
"Age": 25,
"is_married": "false"
}
]

Resources