I was stuck on writing NSDictionary into Object process, I am sure that problem is simple as I imagine but would be great to get assistant. Here is my code:
my custom object:
#interface User : NSObject
#property (nonatomic, retain) NSString *cId;
#property (nonatomic, retain) NSString *firstName;
#property (nonatomic, retain) NSString *lastName;
....
-(instancetype) initWithParameters:(NSDictionary*) parameters;
#end
#import "User.h"
#implementation User
-(instancetype) initWithParameters:(NSDictionary*) parameters
{
self = [super init];
if (self) {
[self setParameters:parameters];
}
return self;
}
- (void) setParameters:(NSDictionary*) parameters{
_cId = parameters[#"cId"];
_firstName = parameters[#"first_name"];
_lastName = parameters[#"last_name"];
....
}
and writing process:
id userObjects = [resultData objectForKey:#"data"];
NSMutableArray* mUsers = [[NSMutableArray alloc] init];
for (NSDictionary* userParameters in userObjects) {
User *user = [[User alloc] initWithParameters:userParameters];
[mUsers addObject:user];
}
userObjects - NSArray got from JSON object from server data.
The problem is : nothing happening and user object still empty after initialization, then I have tried - setValuesForKeysWithDictionary after I called variables same as keys in dictionary and nothing changed.
after adding in mUsers:
Could anybody tell me what I am doing wrong? Thank you!
I believe you think those objects are uninitialized because you are seeing 0 key/value pairs next to each User object.
Your code looks good and I think things will change once you implement [NSObject description] (or [NSObject debugDescription]) like this:
- (NSString *)description
{
return [NSString stringWithFormat:#"cId=%#, firstName=%#, lastName=%#",
_cId, _firstName, _lastName];
}
Related
I am aware of how normal NSArray concatenation works in Objective-C. This is not that question.
I have data that is being incrementally updated from a web service. My object has the following class definition (with a lot removed):
// NoteTemplate.h
#import <UIKit/UIKit.h>
#interface NoteTemplate
#property (copy, nonatomic) NSString *objectId;
I am caching a list of these on-device and checking at launch to see if there are any new or updated NoteTemplate objects in my database to load. So, I end up with two arrays:
NSArray <NoteTemplate *> *oldArray
NSArray <NoteTemplate *> *newArray
If there are no updates, then all I need to do is simply concatenate the two arrays together and that's that.
If there are updates, however, I want to combine the two arrays, but whenever there is a common objectId, the item in newArray should take precedence over the item in oldArray.
Thus far, I am brute-forcing it like this:
- (void)updateNoteTemplatesWithArray:(NSArray *)newTemplates {
NSArray *oldTemplates = [self getNoteTemplates];
NSMutableArray *combined = [NSMutableArray arrayWithArray:newTemplates];
for (NoteTemplate *noteTemplate in oldTemplates) {
NSArray *matches = [combined filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id blockTemplate, NSDictionary<NSString *,id> *bindings) {
return [((NoteTemplate *)blockTemplate).objectId isEqualToString:noteTemplate.objectId];
}]];
if (matches.count == 0) {
[combined addObject:noteTemplate];
}
}
[self setNoteTemplates:[combined copy]];
}
Is there a more optimized way to do this? I can't see that this will affect performance at all, so perhaps an optimization is unnecessary. Still, this approach feels hacky and way over-engineered.
To extend #Larme's suggestion with Set usage you can try the following approach:
#interface NoteTemplate: NSObject
#property (copy, nonatomic) NSString *objectId;
#property (copy, nonatomic) NSString *text;
- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text;
#end
#implementation NoteTemplate
- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text {
self = [super init];
if (self != nil) {
_objectId = objectId;
_text = text;
}
return self;
}
- (BOOL)isEqual:(id)object {
return [self.objectId isEqualToString:[object objectId]];
}
#end
And the usage code:
NoteTemplate *nt1 = [[NoteTemplate alloc] initWithObjectId:#"1" text:#"old set"];
NoteTemplate *nt2 = [[NoteTemplate alloc] initWithObjectId:#"2" text:#"old set"];
NoteTemplate *nt3 = [[NoteTemplate alloc] initWithObjectId:#"1" text:#"new set"];
NoteTemplate *nt4 = [[NoteTemplate alloc] initWithObjectId:#"3" text:#"new set"];
NSSet <NoteTemplate *> *oldSet = [NSSet setWithObjects:nt1, nt2, nil];
NSSet <NoteTemplate *> *newSet = [NSSet setWithObjects:nt3, nt4, nil];
NSMutableSet <NoteTemplate *> *mergedSet = [newSet mutableCopy];
[mergedSet unionSet:oldSet];
for (NoteTemplate *note in mergedSet) {
NSLog(#"Set item %# %#", note.objectId, note.text);
}
After executing this code you'll see in the log:
Set item 3 new set
Set item 1 new set
Set item 2 old set
I assume that's what you were looking for.
I don't know if I'd call this elegant but it's a less brutish approach. Instead of filtering combined at every pass through the loop, get all the new IDs in advance and check the ID list in the loop.
NSMutableArray <NoteTemplate *> *combined = [NSMutableArray arrayWithArray:newTemplates];
NSArray <NSString *> *newTemplateIds = [newTemplates valueForKey:#"objectId"];
for (NoteTemplate *oldTemplate in oldTemplates) {
if (![newTemplateIds containsObject:oldTemplate.objectId]) {
[combined addObject:oldTemplate];
}
}
I create the custom class name with FileModel.
FileModel.h
#import <Foundation/Foundation.h>
#interface FileModel : NSObject
#property (nonatomic, copy) NSString *fileName;
#property (nonatomic, copy) NSString *fileType;
#property (nonatomic, strong) NSDate *editDate;
#property (nonatomic, assign) NSInteger fileSize;
#end
I want to compare the particular string with the fileName.
I create the sample like below .m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *fileSampleName = [[NSArray alloc] initWithObjects:#"apple.png",#"banana.png",#"cherry.png",#"durian.png",#"grape.png",#"avocado.png", nil];
NSMutableArray *fileData = [NSMutableArray new];
FileModel *fileModel = nil;
for( NSInteger i = 0 ; i < fileSampleName.count ; i++){
fileModel = [FileModel new];
fileModel.fileName = [fileSampleName objectAtIndex:i];
fileModel.fileType = #"photo";
fileModel.fileSize = 0;
fileModel.editDate = [NSDate new];
[fileData addObject:fileModel];
}
// fileData's fileName containsObject #"grape" or not?
}
NSArray has containsObject method.
But How can I check the #"grape" is containsObject using fileData at the custom class property filename?
I known using for loop compare one by one.
Did they have other method to check like containsObject?
--- edit---
I try to using indexOfObjectPassingTest method , But the result always is 1.
BOOL result = [fileData indexOfObjectPassingTest:^ BOOL (id tr,NSUInteger index, BOOL *te){
FileModel *fileModel = (FileModel*)tr;
if([#"orange" isEqualToString: fileModel.fileName]){
*te = YES;
return YES;
}else{
return NO;
}}];
NSLog(#"result:%#",#(result)); // it always return 1
Why? thank you very much.
Take a look that NSArray class reference in Xcode. One method you could use is indexOfObjectPassingTest. There are number of related methods depending on your needs. All take a block that's used to test objects to see if they meet whatever criteria you want. In your case you'd test the fileName string.
So you'd pass in a closure that compared the fileName property of each object to your desired filename.
For my iOS application that uses Parse, I need to store an array of custom objects into a PFObject. I tried doing this, and I am getting the error: 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (JSQMessage)'
Is there any possible way to store an array of custom objects in Parse? I can't seem to find a good answer for this.
For your reference, I am using the JSQMessages view controller library at https://github.com/jessesquires/JSQMessagesViewController
The array that I am trying to add to the PFObject is initialized with the code:
[[NSMutableArray alloc] initWithObjects:
[[JSQMessage alloc] initWithText:initialtext sender:[[PFUser currentUser] objectId] date:[NSDate date]],
nil];
If your custom objects conform to the NSCoding protocol then yes, you can save them most definitely.
Here's an example for a Magic: The Gathering set
#interface MTGSet : NSObject <NSCoding>
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *code;
#property (nonatomic, strong) NSDate *releaseDate;
#end
Implementation
#import "MTGSet.h"
#implementation MTGSet
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:#"name"];
[aCoder encodeObject:_code forKey:#"code"];
[aCoder encodeObject:_releaseDate forKey:#"releaseDate"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
return [self initWithJSONDictionary:
#{#"name" : [aDecoder decodeObjectForKey:#"name"],
#"code" : [aDecoder decodeObjectForKey:#"code"],
#"releaseDate" : [aDecoder decodeObjectForKey:#"releaseDate"]}];
}
#end
Usage
MTGSet *set = [MTGSet new];
set.name = #"Magic 2015";
set.code = #"M15";
set.releaseDate = [NSDate date];
NSData *dataFromSet = [NSKeyedArchiver archivedDataWithRootObject:set];
PFObject *object = [PFObject objectWithClassName:#"MyObject"];
object[#"set"] = dataFromSet;
[object save];
MTGSet *unarchivedSet = [NSKeyedUnarchiver unarchiveObjectWithData:object[#"set"]];
NSLog(#"Here's the set: %#", unarchivedSet);
There are libraries out there to make this easier using the objective-c runtime which I recommend.
https://github.com/eladb/Parse-NSCoding
I have a custom object: Vendor that extends NSObject. I am initiating it like so:
NSDictionary *vendorObj = [vendors objectAtIndex:i];
Vendor *vendor = [[Vendor alloc] initWithVendorInfo:vendorObj];
NSLog(#"VendorObj: %#", vendorObj);
NSLog(#"Vendor: %#", vendor);
Here is what the class looks like:
#interface Vendor : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *description;
- (id)initWithVendorInfo:(NSDictionary *)vendorDetails;
#end
#implementation Vendor
- (id)initWithVendorInfo:(NSDictionary *)vendorDetails
{
self = [super init];
if(self)
{
_name = [vendorDetails[#"company_name"] copy];
_description = [vendorDetails[#"description"] copy];
}
return self;
}
If I NSLog vendorObj all the details are there. Once I initiate the Vendor object and NSLog it, the log shows:
2013-11-21 22:22:44.769 [48202:a07] Vendor:
I cannot seem to figure out why my object is nothing, no memory address, not even a null. What am I doing wrong here?
The problem is your description property. The NSObject class defines a description method. This method is called when you use a %# format specifier with an object.
Your description property is overriding that method.
Rename your description property to something else.
I'm new to iphone app development and I'm stuck on this problem I'm having with the app I'm trying to develop.
I have a datacontroller for populating a tableview. I created it using this tutorial:
About Creating Your Second iOS App
I'm trying to pass an array from one of my viewcontrollers that was created from a JSON response.
Here is some code from my viewcontroller.h that needs to pass the array:
#interface ViewController : UIViewController
#property (nonatomic, retain) DataController *Data;
#property (nonatomic, retain) NSMutableArray *array;
#end
viewcontroller.m:
#import "DataController.h"
[Data setMasterList: self.array];
DataController.h:
#interface DataController : NSObject
#property (nonatomic, strong) NSMutableArray *masterList;
- (void)setMasterList:(NSMutableArray *)newList;
#end
DataController.m
#import "LoginViewController.h"
- (void)setMasterList:(NSMutableArray *)newList {
if (_masterList != newList) {
_masterList = [newList mutableCopy];
NSLog("List: %#", newList);
}
}
The NSLog message never shows up in the console and the array is nil.
Any help is appreciated.
Thanks.
EDIT:
Here's the updated viewcontroller.m:
Data = [[DataController alloc] init];
[Data setMasterList: self.array];
The datacontroller.m:
- (void)setMasterList:(NSMutableArray *)newList {
if (_masterList != newList) {
_masterList = [newList mutableCopy];
NSLog("List: %#", self.masterList);
}
}
- (NSUInteger)countOfList {
NSLog("List: %#", self.masterList);
return [self.masterList count];
}
The first nslog inside setMasterList returns the correct array values, but the second nslog inside countOfList returns null. The list always returns null anywhere outside of setMasterList. Is it because I'm creating a new instance of the DataController? If so, how else could I pass the array to the datacontroller.
As in first comment Till have suggested, Data must be initialized before calling setMasterList. Such As:
Data = [[DataController alloc] init];
[Data setMasterList: self.array];