I have a problem with parsing with my server, specially the variable that I add. it doesn't let me to add it. the error message is "Bad receiver type "Bool"(aka "bool")"
here is my code :
#interface MessagingKeyServerResponse : NSObject <NSCopying>
#property (nonatomic, readonly) NSData *key;
#property (nonatomic, readonly) NSString *keyId;
#property (nonatomic, readonly) NSDate *validityStart;
#property (nonatomic, readonly) NSDate *validityEnd;
#property (nonatomic, readonly) BOOL support_long_messages;
#end
#interface MessagingKeyServerResponse ()
// added support_long_messages for parsing
-(instancetype)initWithKey:(NSData *)key keyId:(NSString *)keyId validityStart:(NSDate *)validityStart validityEnd:(NSDate *)validityEnd support_long_messages:(BOOL)support_long_messages;
#end
NS_ASSUME_NONNULL_END
#implementation MessagingKeyServerResponse
// steve note: added message long characters
-(instancetype)initWithKey:(NSData *)key keyId:(NSString *)keyId validityStart:(NSDate *)validityStart validityEnd:(NSDate *)validityEnd support_long_messages:(BOOL)support_long_messages
{
if (!key) {
[NSException raise:NSInvalidArgumentException format:#"No key"];
return nil;
}
if (!keyId) {
[NSException raise:NSInvalidArgumentException format:#"No key id"];
return nil;
}
if (!validityStart) {
[NSException raise:NSInvalidArgumentException format:#"No validity start"];
return nil;
}
if (!validityEnd) {
[NSException raise:NSInvalidArgumentException format:#"No validity end"];
return nil;
}
if (!support_long_messages) {
[NSException raise:NSInvalidArgumentException format:#"there is no support long Characters"];
return nil;
}
if (!([validityStart compare:validityEnd] == NSOrderedAscending)) {
[NSException raise:NSInvalidArgumentException format:#"Invalid validity range"];
return nil;
}
self = [super init];
if (self) {
_key = [key copy];
_keyId = [keyId copy];
_validityStart = [validityStart copy];
_validityEnd = [validityEnd copy];
_support_long_messages = [support_long_messages copy] ;
if (!_key || !_keyId || !_validityStart || !_validityEnd || !_support_long_messages) {
return nil;
}
}
return self;
}
so the error that I receive from _support_long_messages when I want to assign :
_support_long_messages = [support_long_messages copy] ;
any help appreciate.
Simply
_support_long_messages = support_long_messages;
BOOL is a value type, assignment already creates a copy.
Explicit copy is necessary only for reference types (objects).
I am receiving multiple keys for Latitude and Longitude in the JSON body for a request.
{
...
latitude: "28.4949762000",
longitude: "77.0895421000"
}
I would like to combine them into a single CLLocation property while converting them into my JSON model:
#import <Mantle/Mantle.h>
#import CoreLocation;
#interface Location : MTLModel <MTLJSONSerializing>
#property (nonatomic, readonly) float latitude;
#property (nonatomic, readonly) float longitude; //These are the current keys
#property (nonatomic, readonly) CLLocation* location; //This is desired
#end
How do I go about achieving the same?
Finally found the answer here. It's quite an ingenious way, and I was surprised to see it not being mentioned in the docs explicitly.
The way to combine multiple keys into a single object is by mapping the target property to multiple keys using an array in the +JSONKeyPathsByPropertyKey method. When you do so, Mantle will make the multiple keys available in their own NSDictionary instance.
+(NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
...
#"location": #[#"latitude", #"longitude"]
};
}
If the target property is an NSDictionary, you're set. Otherwise, you will need specify the conversion in either the +JSONTransformerForKey or the +propertyJSONTransformer method.
+(NSValueTransformer*)locationJSONTransformer
{
return [MTLValueTransformer transformerUsingForwardBlock:^CLLocation*(NSDictionary* value, BOOL *success, NSError *__autoreleasing *error) {
NSString *latitude = value[#"latitude"];
NSString *longitude = value[#"longitude"];
if ([latitude isKindOfClass:[NSString class]] && [longitude isKindOfClass:[NSString class]])
{
return [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
}
else
{
return nil;
}
} reverseBlock:^NSDictionary*(CLLocation* value, BOOL *success, NSError *__autoreleasing *error) {
return #{#"latitude": value ? [NSString stringWithFormat:#"%f", value.coordinate.latitude] : [NSNull null],
#"longitude": value ? [NSString stringWithFormat:#"%f", value.coordinate.longitude]: [NSNull null]};
}];
}
I'm trying to map the responses from the Sickbeard API to my objects with Mantle, but I can't figure out how, since the response is key-value based using TVDB id's as key, like this:
"data": {
"71663": {
"air_by_date": 0,
"cache": {
"banner": 1,
"poster": 1
},
"language": "en",
"network": "FOX",
"next_ep_airdate": "2014-09-28",
"paused": 0,
"quality": "Any",
"show_name": "The Simpsons",
"status": "Continuing",
"tvdbid": 71663,
"tvrage_id": 6190,
"tvrage_name": "The Simpsons"
},
"72227": {
"air_by_date": 0,
"cache": {
"banner": 1,
"poster": 1
},
"language": "en",
"network": "CBS",
"next_ep_airdate": "",
"paused": 0,
"quality": "Any",
"show_name": "Two and a Half Men",
"status": "Continuing",
"tvdbid": 72227,
"tvrage_id": 6454,
"tvrage_name": "Two and a Half Men"
}
}
Since the data object does not simply contain an array of objects like this [{"key": value},{"key": value}] but instead objects keyed by some unique id, I'm not sure how I should map it into my SBShow classes, defined like:
#import <Foundation/Foundation.h>
#import <Mantle.h>
#interface SBShow : MTLModel <MTLJSONSerializing>
#property (nonatomic, strong) NSNumber *tvdbid;
#property (nonatomic, strong) NSString *showName;
#property (nonatomic, strong) NSString *network;
#property (nonatomic, strong) NSString *status;
#end
#implementation SBShow
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{#"showName": #"show_name"};
}
+ (NSValueTransformer *)dateJSONTransformer {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd"];
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *str) {
return [formatter dateFromString:str];
} reverseBlock:^(NSDate *date) {
return [formatter stringFromDate:date];
}];
}
#end
Any help would be greatly appreciated.
You can do it like this, by adding the key to the rest of the 'JSON dictionary':
NSMutableArray *shows = [NSMutableArray array];
// data is an NSDictionary, representing the 'data' key in the JSON
[data enumerateKeysAndObjectsUsingBlock:^(NSString *tvdbID, NSDictionary *showData, BOOL *stop) {
NSMutableDictionary *modelDictionary = [showData mutableCopy];
modelDictionary[#"tvdbid"] = tvdbID;
NSError *error = nil;
SBShow *show = [MTLJSONAdapter modelOfClass:SBShow.class
fromJSONDictionary:modelDictionary
error:&error];
[shows addObject:show];
}];
NSLog(#"Show models are %#", shows);
You can write your own transformer to encapsulate this logic and apply it to the data key if appropriate.
I found that my CouchModel (TSCAssetUploadDO) saves the attachment but does not save the property.
TSCAssetUploadDO *assetDO = [[TSCAssetUploadDO alloc] initWithNewDocumentInDatabase: _localAssetUploadDatabase];
NSData *data = UIImageJPEGRepresentation(contact.avatar, 1.0); // may need to resize.
NSString *attachmentName = [docID stringByAppendingString:#".jpg"];
[assetDO createAttachmentWithName:attachmentName type:#"image/jpeg" body:data];
assetDO.relatedDocID = docID;
assetDO.docType = #"contact";
RESTOperation *op2 = [assetDO save];
//[op2 wait]; originally thought that making it sync may work
[op2 onCompletion:^{
if (op2.error) NSLog(#"ERROR [TOUCHDB] %#", op2.error);
else
{
NSLog(#"YES! it saved");
}
if (completionBlock)
{
completionBlock(op2.error, contact);
}
//[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationDataUpdated object:nil];
}];
This is resulting to a document like this ( with saved attachment, no property like docType and relatedDocID though )
{
"_id": "D50ED630-34ED-4A02-A9C8-204E79A0648B",
"_rev": "1-b0ce9eaa1fb2f86dc9ae619e27ffe1ea",
"_attachments": {
"QSPNGC665.jpg": {
"content_type": "image/jpeg",
"revpos": 1,
"digest": "md5-u9V0rgoSRN5cUW2T3xh0hw==",
"length": 117500,
"stub": true
}
}
}
Below is the CouchModel that I just used.
#interface TSCAssetUploadDO: CouchModel
#property(nonatomic,retain) NSString *relatedDocID;
#property(nonatomic,retain) NSString *docType;//entity or contact
#property bool *toProcess;
#property (retain) NSDate* created_at;
#end
#implementation TSCAssetUploadDO
- (NSDictionary*) propertiesToSave {
// Initialize created_at the first time the document is saved:
if (self.created_at == nil)
self.created_at = [NSDate date];
return [super propertiesToSave];
}
#end
Is there anything that I did wrong?
Solved my issue. need to add #dynamic
#implementation TSCAssetUploadDO
#dynamic relatedDocID, docType, toProcess, created_at;
<... rest of the code >
#end
Hello I a class of type NSObject:
ProductDetails *details = [[ProductDetails alloc] init];
details.name = #"Soap1";
details.color = #"Red";
details.quantity = 4;
I want to pass the "details" object to a dictionary.
I did,
NSDictionary *dict = [NSDictionary dictionaryWithObject:details forKey:#"details"];
I am passing this dict to another method which performs a check on JSONSerialization:
if(![NSJSONSerialization isValidJSONObject:dict])
And I am getting a crash on this check. Am I doing anything wrong here? I know that the details I am getting is a JSON object and I am assigning it to the properties in my ProductDetails class.
Please help me. I am a noob in Objective-C.
I now tried:
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:(NSData*)details options:kNilOptions error:&error];
All I need here is an easy way to convert details to NSData.
I noticed that I have an array inside my object may be thats why all the ways I tried is throwing an exception. However since this question is becoming to big, I have started an another question thread for it where I have displayed the data I am getting inside the object - https://stackoverflow.com/questions/19081104/convert-nsobject-to-nsdictionary
This may well be the easiest way to achieve it. Do import #import <objc/runtime.h> in your class file.
#import <objc/runtime.h>
ProductDetails *details = [[ProductDetails alloc] init];
details.name = #"Soap1";
details.color = #"Red";
details.quantity = 4;
NSDictionary *dict = [self dictionaryWithPropertiesOfObject: details];
NSLog(#"%#", dict);
//Add this utility method in your class.
- (NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
[dict setObject:[obj valueForKey:key] forKey:key];
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
NSDictionary *details = {#"name":product.name,#"color":product.color,#"quantity":#(product.quantity)};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:details
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&error];
if (! jsonData) {
NSLog(#"Got an error: %#", error);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
Second part's source: Generate JSON string from NSDictionary in iOS
As mmackh said, you want to define a custom method for your ProductDetails object that will return a simple NSDictionary of values, e.g.:
#implementation ProductDetails
- (id)jsonObject
{
return #{#"name" : self.name,
#"color" : self.color,
#"quantity" : #(self.quantity)};
}
...
Let's assume that we added manufacturer property to our ProductDetails, which referenced a ManufacturerDetails class. We'd just write a jsonObject for that class, too:
#implementation ManufacturerDetails
- (id)jsonObject
{
return #{#"name" : self.name,
#"address1" : self.address1,
#"address2" : self.address2,
#"city" : self.city,
...
#"phone" : self.phone};
}
...
And then change the jsonObject for ProductDetails to employ that, e.g.:
#implementation ProductDetails
- (id)jsonObject
{
return #{#"name" : self.name,
#"color" : self.color,
#"quantity" : #(self.quantity),
#"manufacturer" : [self.manufacturer jsonObject]};
}
...
If you have potentially nested collection objects (arrays and/or dictionaries) with custom objects that you want to encode, you could write a jsonObject method for each of those, too:
#interface NSDictionary (JsonObject)
- (id)jsonObject;
#end
#implementation NSDictionary (JsonObject)
- (id)jsonObject
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj respondsToSelector:#selector(jsonObject)])
[dictionary setObject:[obj jsonObject] forKey:key];
else
[dictionary setObject:obj forKey:key];
}];
return [NSDictionary dictionaryWithDictionary:dictionary];
}
#end
#interface NSArray (JsonObject)
- (id)jsonObject;
#end
#implementation NSArray (JsonObject)
- (id)jsonObject
{
NSMutableArray *array = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj respondsToSelector:#selector(jsonObject)])
[array addObject:[obj jsonObject]];
else
[array addObject:obj];
}];
return [NSArray arrayWithArray:array];
}
#end
If you do something like that, you can now convert arrays or dictionaries of your custom objects object into something that can be used for generating JSON:
NSArray *products = #[[[Product alloc] initWithName:#"Prius" color:#"Green" quantity:3],
[[Product alloc] initWithName:#"Accord" color:#"Black" quantity:1],
[[Product alloc] initWithName:#"Civic" color:#"Blue" quantity:2]];
id productsJsonObject = [products jsonObject];
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:productsJsonObject options:0 error:&error];
If you're simply trying to save these objects in a file, I'd suggest NSKeyedArchiver and NSKeyedUnarchiver. But if you need to generate JSON objects for your own private classes, you can do something like the above might work.
In .h File
#import <Foundation/Foundation.h>
#interface ContactDetail : NSObject
#property (nonatomic) NSString *firstName;
#property (nonatomic) NSString *lastName;
#property (nonatomic) NSString *fullName;
#property (nonatomic) NSMutableArray *mobileNumbers;
#property (nonatomic) NSMutableArray *Emails;
#property (assign) bool Isopen;
#property (assign) bool IsChecked;
-(NSDictionary *)dictionary;
#end
in .m file
#import "ContactDetail.h"
#import <objc/runtime.h>
#implementation ContactDetail
#synthesize firstName;
#synthesize lastName;
#synthesize fullName;
#synthesize mobileNumbers;
#synthesize Emails;
#synthesize IsChecked,Isopen;
//-(NSDictionary *)dictionary {
// return [NSDictionary dictionaryWithObjectsAndKeys:self.fullName,#"fullname",self.mobileNumbers,#"mobileNumbers",self.Emails,#"emails", nil];
//}
- (NSDictionary *)dictionary {
unsigned int count = 0;
NSMutableDictionary *dictionary = [NSMutableDictionary new];
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
id value = [self valueForKey:key];
if (value == nil) {
// nothing todo
}
else if ([value isKindOfClass:[NSNumber class]]
|| [value isKindOfClass:[NSString class]]
|| [value isKindOfClass:[NSDictionary class]] || [value isKindOfClass:[NSMutableArray class]]) {
// TODO: extend to other types
[dictionary setObject:value forKey:key];
}
else if ([value isKindOfClass:[NSObject class]]) {
[dictionary setObject:[value dictionary] forKey:key];
}
else {
NSLog(#"Invalid type for %# (%#)", NSStringFromClass([self class]), key);
}
}
free(properties);
return dictionary;
}
#end
if any crash ,You check the property (NSMutableArray,NSString,etc ) in else if condition inside of for.
In Your Controller, in any func...
-(void)addItemViewController:(ConatctViewController *)controller didFinishEnteringItem:(NSMutableArray *)SelectedContact
{
NSLog(#"%#",SelectedContact);
NSMutableArray *myData = [[NSMutableArray alloc] init];
for (ContactDetail *cont in SelectedContact) {
[myData addObject:[cont dictionary]];
}
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:myData options:NSJSONWritingPrettyPrinted error:&error];
if ([jsonData length] > 0 &&
error == nil){
// NSLog(#"Successfully serialized the dictionary into data = %#", jsonData);
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSLog(#"JSON String = %#", jsonString);
}
else if ([jsonData length] == 0 &&
error == nil){
NSLog(#"No data was returned after serialization.");
}
else if (error != nil){
NSLog(#"An error happened = %#", error);
}
}
Try this:
#import <objc/runtime.h>
+ (NSDictionary *)dictionaryWithPropertiesOfObject:(id)obj {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
[dict setObject:[obj valueForKey:key] ? [obj valueForKey:key] : #"" forKey:key];
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
The perfect way to do this is by using a library for serialization/deserialization
many libraries are available but one i like is
JagPropertyConverter
https://github.com/jagill/JAGPropertyConverter
it can convert your Custom object into NSDictionary and vice versa
even it support to convert dictionary or array or any custom object within your object (i.e Composition)
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc]init];
converter.classesToConvert = [NSSet setWithObjects:[ProductDetails class], nil];
//For Object to Dictionary
NSDictionary *dictDetail = [converter convertToDictionary:detail];
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:dictDetail options:NSJSONWritingPrettyPrinted error:&error];
You can convert object (say modelObject) to dictionary at runtime with the help of objc/runtime.h class but that has certain limitations and is not recommended.
Considering MVC, mapping logic should be implemented in Model class.
#interface ModelObject : NSObject
#property (nonatomic) NSString *p1;
#property (nonatomic) NSString *p2;
-(NSDictionary *)dictionary;
#end
#import "ModelObject.h"
#implementation ModelObject
-(NSDictionary *)dictionary
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:self.p1 forKey:#"p1"];// you can give different key name here if you want
[dict setValue:self.p2 forKey:#"p2" ];
return dict;
}
#end
Uses:
NSDictionary *modelObjDict = [modelObj dictionary];
Try using
NSDictionary *dict = [details valuesForAttributes:#[#"name", #"color"]];
And compare what the dictionary contains. Then try to convert it to JSON. And look at the JSON spec - what data types can go into a JSON encoded file?
You also can use the NSObject+APObjectMapping category which is available on GitHub: https://github.com/aperechnev/APObjectMapping
It's a quit easy. Just describe the mapping rules in your class:
#import <Foundation/Foundation.h>
#import "NSObject+APObjectMapping.h"
#interface MyCustomClass : NSObject
#property (nonatomic, strong) NSNumber * someNumber;
#property (nonatomic, strong) NSString * someString;
#end
#implementation MyCustomClass
+ (NSMutableDictionary *)objectMapping {
NSMutableDictionary * mapping = [super objectMapping];
if (mapping) {
NSDictionary * objectMapping = #{ #"someNumber": #"some_number",
#"someString": #"some_string" };
}
return mapping
}
#end
And then you can easily map your object to dictionary:
MyCustomClass * myObj = [[MyCustomClass alloc] init];
myObj.someNumber = #1;
myObj.someString = #"some string";
NSDictionary * myDict = [myObj mapToDictionary];
Also you can parse your object from dictionary:
NSDictionary * myDict = #{ #"some_number": #123,
#"some_string": #"some string" };
MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];
Swift
Now the swift is very popular and most of the SDK's are written in Objective C, we need to convert NSObject to NSDictionary, With the Help of #thatzprem Answer, I wrote an extension for Swift which will convert our NSObject into NSDictionary, then we can use that NSDictionary to simple Dictionary or JSON Object or other purpose. I hope so this will help out the Swift User.
extension NSObject {
func convertNSObjectToNSDictionary() -> [AnyHashable : Any]? {
var dict: [AnyHashable : Any] = [:]
var count: UInt32 = 0
let properties = class_copyPropertyList(type(of: self), UnsafeMutablePointer<UInt32>(mutating: &count)) //as? objc_property_t
for i in 0..<Int(count) {
var key: String? = nil
if let property = properties?[i] as? objc_property_t {
key = String(utf8String: property_getName(property))
}
//dict[key] = (obj as? NSObject)?.value(forKey: key ?? "")
dict[key] = (self).value(forKey: key ?? "")
}
free(properties)
return dict
}
}