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
Related
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];
}
It's a custom class:
#import <Foundation/Foundation.h>
#interface timeTable : NSObject
#property (nonatomic) int ID;
#property (nonatomic) NSString * type;
#property (nonatomic) NSString * time;
#property (nonatomic) NSString * busno;
#property (nonatomic) NSString * stops;
// nothing is done in it's .m file not even synthesise
// thats an other class
#import <Foundation/Foundation.h>
#import "timeTable.h"
#interface refreshDatabase : NSObject
#property (strong, nonatomic) NSMutableArray * arrayTimeTable;
#property (strong, nonatomic) timeTable * objectTimeTable;
// in it's .m file i am downloading a JSON formatted array using a
service then i am saving it to NsMutbaleArray
// downloading a json array which contains a rows of data
NSError * error;
NSArray * jsonArray = [NSJSONSerialization JSONObjectWithData:
[safeString dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments error:&error];
NSLog(#"json Array %#", jsonArray);
// for getting an instance of array
NSDictionary * jsonElement;
for (int i=0; i<jsonArray.count ; i++)
{ // each row will be saved in an object of timetable class then that
// object will be saved to nsmutablearray
jsonElement = [jsonArray objectAtIndex:i];
objectTimeTable = [[timeTable alloc]init];
objectTimeTable.ID = [[jsonElement objectForKey:#"id"]intValue];
objectTimeTable.type = [jsonElement objectForKey:#"type"];
objectTimeTable.time = [jsonElement objectForKey:#"time"];
objectTimeTable.busno = [jsonElement objectForKey:#"busno"];
objectTimeTable.stops = [jsonElement objectForKey:#"stops"];
// adding an instance from JSON Array to our NSmutablearray
[arrayTimeTable addObject:objectTimeTable];
}//end of json Array FOR loop
// our array containnig all the objects will be saved using
//NSUserDefualts
// userDefaults is an object of NSUserDefaults
if(userDefaults)
{ // its not saving it to userdefaults
[userDefaults setObject:arrayToStore forKey:#"ArrayOfTimeTables"];
[userDefaults synchronize];
}
// retrieving the saved array from NSUSerDefaults and printing it
// using slog
timeTable *objPrint = [[timeTable alloc]init];
NSMutableArray *arrayLoader = [userDefaults arrayForKey:#"ArrayOfTimeTables"];
for (int i=0; i<arrayLoader.count ; i++)
{
objPrint = [arrayLoader objectAtIndex:i];
NSLog(#"outSide Printing For LOOP After Loading of tim # %d times havind id =%d type = %# time = %# busno = %# stops = %#",i,objPrint.ID,objPrint.type,objPrint.time,objPrint.busno,objPrint.stops);
}
Thanx a lot in helping me in advance.
Please tell me how to save that array which contains object of timetable class into nsUseDefaults and then how to load it back.
Please help me. I read a lot of similar question and answers, but don't know how to make them work for me.
Use NScoding to encode each of your custom object then add that custom object into an array then encode other and then add it to the array then save that array into NSUserDefaults
encoding and decoding of upper given question
is
the custom class .h file
#import <Foundation/Foundation.h>
#interface timeTable : NSObject<NSCoding>
#property (nonatomic) NSString * ID;
#property (nonatomic) NSString * type;
#property (nonatomic) NSString * time;
#property (nonatomic) NSString * busno;
#property (nonatomic) NSString * stops;
the custom class .m file
#import "timeTable.h"
#implementation timeTable
#synthesize ID;
#synthesize type;
#synthesize time;
#synthesize busno;
#synthesize stops;
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.ID forKey:#"ID"];
[aCoder encodeObject:self.type forKey:#"type"];
[aCoder encodeObject:self.time forKey:#"time"];
[aCoder encodeObject:self.busno forKey:#"busno"];
[aCoder encodeObject:self.stops forKey:#"stops"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if((self = [super init])) {
//decode properties, other class vars
self.ID = [aDecoder decodeObjectForKey:#"ID"];
self.type = [aDecoder decodeObjectForKey:#"type"];
self.time = [aDecoder decodeObjectForKey:#"time"];
self.busno = [aDecoder decodeObjectForKey:#"busno"];
self.stops = [aDecoder decodeObjectForKey:#"stops"];
}
return self;
}
#end
where you encode each custom object one by one and adding it to the array then save that NSMutableArray or NSArray
into NSUserDefaults
encoding a custom object then adding it to array and saving it into user defaults
// encoding a custom object before saving it to array
NSData *encodeTimeTableObj = [NSKeyedArchiver
archivedDataWithRootObject:objectTimeTable];
addObject:encodeTimeTableObj];
//saving it to user Defaults
if(userDefaults)
{
[userDefaults setObject:arrayTimeTable
forKey:#"ArrayOfTimeTables"];
[userDefaults synchronize];
NSLog(#"saving to usedefaults");
}
retriving an array either mutable or non mutable then decoding each of its object
NSMutableArray *arrayLoader = [userDefaults
objectForKey:#"ArrayOfTimeTables"];
NSData * decode = [arrayLoader objectAtIndex:0];
// in case of upper given custom class Time Table
timeTable *objPrint = [NSKeyedUnarchiver unarchiveObjectWithData:decode];
Use NSArray to get array from NSUSerDefaults as NSUSerDefaults return immuttable array.
If you need NSMutableArray, then convert this NSArray to NSMutableArray.
// retrieving the saved array from NSUSerDefaults and printing it
// using slog
timeTable *objPrint = [[timeTable alloc]init];
NSArray *arrayLoader = [userDefaults arrayForKey:#"ArrayOfTimeTables"];
for (int i=0; i
I have a Product model with the header:
#interface Product : RLMObject <NSCopying,NSCoding>
{
}
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *thumbnailURL;
#property (nonatomic, strong) UIImage *thumbnail;
-(id)initWithInfo:(NSDictionary*)dictionary;
-(UIImage*)getThumbnail;
and implementation:
#implementation Product
-(id)initWithInfo:(NSDictionary*)dictionary
{
self = [self init];
if (self) {
_title = dictionary[#"title"];
_thumbnailURL = dictionary[#"thumbnailURL"];
_thumbnail = [self getThumbnail];
}
return self;
}
-(UIImage*)getThumbnail
{
if (_thumbnail) {
return _thumbnail;
}
//load image from cache
return [self loadImageFromCache];
}
Now, when I try to create a Product object and insert it into Realm, I always get the exception
[RLMStandalone_Product getThumbnail]: unrecognized selector sent to instance 0xcd848f0'
Now, I remove _thumbnail = [self getThumbnail]; and it works fine. But then I get another exception
[RLMStandalone_Product title]: unrecognized selector sent to instance 0xd06d5f0'
when I reload my view. I have created my Product object in the main thread, so it should be fine to using its property and method, isn't it?
Any advice will be appreciated!
Because Realm object properties are backed by the database rather than in-memory ivars, accessing those properties' ivars is not supported. We're currently clarifying our docs to convey this:
Please note that you can only use an object on the thread from which is was created or obtained, ivars shouldn't be accessed directly for any persisted properties, and that getters and setters for persisted properties cannot be overridden.
So to work with Realm, your model should look like this:
#interface Product : RLMObject
#property NSString *title;
#property NSString *thumbnailURL;
#property (nonatomic, strong) UIImage *thumbnail;
#end
#implementation Product
-(UIImage*)thumbnail
{
if (!_thumbnail) {
_thumbnail = [self loadImageFromCache];
}
return _thumbnail;
}
-(UIImage*)loadImageFromCache
{
// Load image from cache
return nil;
}
+(NSArray*)ignoredProperties
{
// Must ignore thumbnail because Realm can't persist UIImage properties
return #[#"thumbnail"];
}
#end
And usage of this model could look like this:
[[RLMRealm defaultRealm] transactionWithBlock:^{
// createInDefaultRealmWithObject: will populate object keypaths from NSDictionary keys and values
// i.e. title and thumbnailURL
[Product createInDefaultRealmWithObject:#{#"title": #"a", #"thumbnailURL": #"http://example.com/image.jpg"}];
}];
NSLog(#"first product's image: %#", [(Product *)[[Product allObjects] firstObject] thumbnail]);
Notice how initWithInfo isn't necessary because RLMObject already has initWithObject: and createInDefaultRealmWithObject: already do this.
I'm trying to store a NSMutableArray consisting of VOs (NameVO) with Core Data but getting the following exception thrown:
'NSInvalidArgumentException', reason: '-[NameVO encodeWithCoder:]: unrecognized selector sent to instance 0x1096400a0'
My NameVO is just a simple value object, extending NSObject and contains two fields, a string and a NSMutableArray that itself contains strings. It also contains a compare function.
I'm trying to prepare this to be stored as a CD transformable attribute type with ('names' is my NameVO array):
NSData *tempNamesData = [NSKeyedArchiver archivedDataWithRootObject:names];
My question is: what do I need to do to make NameVO be accepted by NSKeyedArchiver to convert it successfully to a NSData?
I don't want NameVO to extend NSManagedObject because then I cannot instantiate and init it directly.
Now that we're in 2017, it's best to use the safer NSSecureCoding protocol instead of the older, less safe NSCoding protocol. The implementation changes are minimal:
1) ensure that your class declares its conformation to NSSecureCoding
#interface MyClass : NSObject<NSSecureCoding>
#property (nonatomic, strong) NSNumber *numberProperty;
#property (nonatomic, strong) NSString *stringProperty;
#end
2) NSSecureCoding protocol houses the same two instance methods methods in the NSCoding protocol, plus an additional class method, +supportsSecureCoding. You'll need to add that method, as well as slightly modify your -initWithCoder: method.
#implementation MyClass
// New Method for NSSecureCoding. Return YES.
+ (BOOL)supportsSecureCoding {
return YES;
}
// Your Encode Method Can Stay The Same, though I would use NSStringFromSelector whenever possible to get the keys to ensure you're always getting what you're looking for.
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.numberProperty forKey:NSStringFromSelector(#selector(numberProperty))];
[aCoder encodeObject:self.stringProperty forKey:NSStringFromSelector(#selector(stringProperty))];
}
// Slightly updated decode option
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.numberProperty = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(#selector(numberProperty))];
self.stringProperty = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(#selector(stringProperty))];
}
}
#end
Notice that NSCoder's -decodeObjectOfClass:withKey: requires you to specify the class that you're expecting to receive. This is a much safer way to do things.
Then, to store this decodable object in CoreData, simply create a Managed object that contains an NSData attribute and some identifying information (a string, a date, an id, or a number or something)
#interface MyClassMO : NSManagedObject
#property (nonatomic, strong) NSString *identifier;
#property (nonatomic, strong) NSData *data;
#end
#implementation MyClassMO
#dynamic identifier;
#dynamic data;
#end
In practice, it would look something like this:
- (void)storeObject:(MyClass *)object withIdentifier:(NSString *)identifier {
NSData *objectData = [NSKeyedArchived archivedDataWithRootObject:object];
NSManagedObjectContext *moc = ... // retrieve your context
// this implementation relies on new NSManagedObject initializers in the iOS 10 SDK, but you can create it any way you typically create managed objects
MyClassMO *managedObject = [[MyClassMO alloc] initWithContext:moc];
managedObject.data = objectData;
managedObject.identifier = identifier;
NSError *error;
[moc save:&error];
}
- (MyClass *)retrieveObjectWithIdentifier(NSString *)identifier {
NSManagedObject *moc = ... // retrieve your context
// This also relies on iOS 10 SDK's new factory methods available on NSManagedObject. You can create your request any way you typically do;
NSFetchRequest *request = [MyClassMO fetchRequest];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"identifier = %#", identifier];
request.predicate = predicate;
NSError *error;
NSArray<MyClassMO *> *results = [moc executeFetchRequest:request withError:&error];
// if you're only storing one object per identifier, this array should only be 1 object long. if not, you'll need to decide which object you're looking for. you also might want to implement an overwrite policy or a delete before store type thing.
MyClassMO *managedObject = results.firstObject;
NSData *objectData = managedObject.data;
MyClass *object = [NSKeyedUnarchiver unarchiveObject:objectData];
return object;
}
This solution is obviously a bit of an oversimplification, how and when you store stuff in the db is up to your needs, but the main idea is that, you'll need to make sure your custom class conforms to NSSecureCoding, and that you'll need to make a separate Managed Object class to store and retrieve your data.
As your exception says you need to implement NSCoding protocol to your class and you have to override two methods:
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
It should sorted your issue.
// EXTENDED
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_formId = [aDecoder decodeObjectForKey:#"FormID"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.formId forKey:#"FormID"];
}
Use this initWithCoder and encodeWithCode method.I hope it will work for you. it works for me in same issue as u have...Use this sample code
-(id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super init]){
storePlaylist=[aDecoder decodeObjectForKey:#"storePlaylist"];
playlistName=[aDecoder decodeObjectForKey:#"playlistName"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:storePlaylist forKey:#"storePlaylist"];
[encoder encodeObject:playlistName forKey:#"playlistName"];
}
I know that subtracting an NSArray from NSArray if it is a single basic object found here
But what i have is an object like this
#interface Set : NSObject
#property (nonatomic, strong) NSString *ItemId;
#property (nonatomic, strong) NSString *time;
#property (nonatomic, strong) NSString *Category_id;
#property (nonatomic, strong) NSString *List_id;
#property (nonatomic, strong) NSString *name;
#end
How can i delete an array having the set object from another array with the same?
It can be done by iterations that i knw.Is there some other way ?
EDIT: For clarity
I have Array A with 5 Set objects and I have 4 Set Objects in Array B
array A and Array B contain 3 set objects with common values..[Note : memory may be different] common
All I need is an Array C =Array A - Array B that has 2 objects in resulting array C
Thank You :)
You need to implement the - (NSUInteger)hash and - (BOOL)isEqual:(id)object method in Set class.
For eg:-
- (NSUInteger)hash {
return [self.ItemId hash];
}
- (BOOL)isEqual:(id)object
{
return ([object isKindOfClass:[self class]] &&
[[object ItemId] isEqual:_ItemId])
}
After that try this:
NSMutableSet *set1 = [NSMutableSet setWithArray:array1];
NSMutableSet *set2 = [NSMutableSet setWithArray:array2];
[set1 intersectSet:set2]; //this will give you only the obejcts that are in both sets
NSArray *commonItems = [set1 allObjects];
[mutableArray1 removeObjectsInArray:commonItems];//mutableArray1 is the mutable copy of array1
mutableArray1 will have all objects in the same order as earlier after removing common objects.
By using NSSet and NSPredicate we can meet your requirement.
Assessors *ass1 = [[Assessors alloc] init];
ass1.AssessorID = #"3";
Assessors *ass2 = [[Assessors alloc] init];
ass2.AssessorID = #"2";
Assessors *ass3 = [[Assessors alloc] init];
ass3.AssessorID = #"1";
Assessors *ass4 = [[Assessors alloc] init];
ass4.AssessorID = #"2";
NSSet *nsset1 = [NSSet setWithObjects:ass1, ass2, nil];
NSSet *nsset2 = [NSSet setWithObjects:ass3, ass4, nil];
// retrieve the IDs of the objects in nsset2
NSSet *nsset2_ids = [nsset2 valueForKey:#"AssessorID"];
// only keep the objects of nsset1 whose 'id' are not in nsset2_ids
NSSet *nsset1_minus_nsset2 = [nsset1 filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"NOT AssessorID IN %#",nsset2_ids]];
for(Assessors *a in nsset1_minus_nsset2)
NSLog(#"Unique ID : %#",a.AssessorID);
Here Assessors is my NSObject Class (Set in your case) and AssessorID is one property of that class.
Hope this can help.