I pass a NSDictionary, but the function get nil - objective-c

This is weird. I have a NSDictionary. I pass it to a method. The NSDictionary is filled, but the reciever get nil. What could be?
[self.manager POST:url parameters:params success:^(NSURLSessionDataTask *task, NSDictionary *responseObject)
{
NSString *code = responseObject[kCODE];
if ([code isEqualToString:rAPI_SUCCESS]) {
fulfiller([[ResponseDataModel alloc] initWithDictionary:responseObject]);
I put a breakpoint in the fullfiler line, then this is the value:
Printing description of responseObject:
{
code = "API_SUCCESS";
data = {
cliente = {
celular = ******;
email = "test#****.com";
nombre = test;
"user_token" = ACDF*****4;
"ver_celular" = 1;
};
};
message = "The operation was successful";
success = 1;
}
Now in the receiver:
- (id)initWithDictionary:(NSDictionary *)dictionary {
if (!nsDictionaryClass) nsDictionaryClass = [NSDictionary class];
Printing description of dictionary:
(NSDictionary *) dictionary = <variable not available>
(Self is a valid reference)
This is with Xcode Version 5.1.1 (5B1008) in debug mode.

Related

How to differentiate the returned value of a function using completion block in Objective C?

I have a function that gives 2 different String values that are returned :
-(NSString*)load:(NSDictionary *)dict
{
NSDictionary *dataDict = [self objectForId:#"data" fromDict:dict withDefault:nil];
if (dataDict) {
NSDictionary *success = [self objectForId:#"success" fromDict:dataDict withDefault:nil];
NSString *str = [NSString stringWithFormat:#"%#", success];
if ([str isEqualToString: #"1"])
{
NSDictionary *idDict = [self objectForId:#"id" fromDict:dataDict withDefault:nil];
if (idDict) {
NSString *idString = [NSString stringWithFormat:#"%#", idDict];
return idString;
}
} else {
NSDictionary *messages = [self objectForId:#"messages" fromDict:dataDict withDefault:nil];
if (messages) {
NSDictionary *messageDict = (NSDictionary *)messages;
NSArray *type = messageDict[#"type"];
if (type.count > 0) {
NSString *messageString = type[0][#"message"];
return messageString;
}
}
}
}
return nil;
}
And accessing the stringValue like this :
NSString *string = [className load:dict];
Now I want to write if else statements for "idString" and "messageString" return values. How do I differentiate the 2 return values?
While returning a NSDictionary (see #Yihui Yang solution), or a custom Class (see #Sulthan's solution) for it are valid solutions, it maybe be too much.
You need to remember the keys of the dictionary returned, or maybe creating a custom class just for that is too much.
Here are two other possibilities:
I'll have has sample dict to test:
NSDictionary *dictToTest1 = #{#"id": #"idString",
#"noiseKey": #"noiseValue"
};
NSDictionary *dictToTest2 = #{#"messages": #"messagesString",
#"noiseKey": #"noiseValue"
};
I'll simplify your test to check only if there is a key/value for key id or for messages.
Using Double pointers:
-(void)loadDict:(NSDictionary *)dict withRetKey:(NSString **)key andRetValue:(NSString **)value
{
NSString *retKey = nil;
NSString *retValue = nil;
if (dict[#"id"])
{
retKey = #"id";
retValue = dict[#"id"];
}
else if (dict[#"messages"])
{
retKey = #"messages";
retValue = dict[#"messages"];
}
if (key)
{
*key = retKey;
}
if (value)
{
*value = retValue;
}
}
Sample test:
NSString *key1 = nil;
NSString *value1 = nil;
[self loadDict:dictToTest1 withRetKey:&key1 andRetValue:&value1];
NSLog(#"Key1: %#\t value1: %#", key1, value1);
NSString *key2 = nil;
NSString *value2 = nil;
[self loadDict:dictToTest2 withRetKey:&key2 andRetValue:&value2];
NSLog(#"Key2: %#\t value2: %#", key2, value2);
Output:
$> Key1: id value1: idString
$> Key2: messages value2: messagesString
Where did you see the & for objects ?
Almost all the times in managing a NSError. (linked question)
For primitive? For sample if you want to retrieve the red/blue/green/alpha of a UIColor (linked question)
With blocks:
-(void)blockLoadDict:(NSDictionary *)dict withBlock:(void(^) (NSString *key, NSString *value))block
{
NSString *retKey = #"";
NSString *retValue = #"";
if (dict[#"id"])
{
retKey = #"id";
retValue = dict[#"id"];
}
else if (dict[#"messages"])
{
retKey = #"messages";
retValue = dict[#"messages"];
}
if (block)
{
block(retKey, retValue);
}
}
Sample:
__block NSString *key3 = nil;
__block NSString *value3 = nil;
[self blockLoadDict:dictToTest1 withBlock:^(NSString *key, NSString *value) {
key3 = key;
value3 = value;
}];
NSLog(#"Block Key3: %#\t value3: %#", key3, value3);
__block NSString *key4 = nil;
__block NSString *value4 = nil;
[self blockLoadDict:dictToTest2 withBlock:^(NSString *key, NSString *value) {
key4 = key;
value4 = value;
}];
NSLog(#"Block Key4: %#\t value4: %#", key4, value4);
Output:
$> Block Key3: id value3: idString
$> Block Key4: messages value4: messagesString
What I understand is that you want to know if load method returns an idString or messageString.
So what I recommend is using a tricky method.
Instead of return a string, you can return a dict which is like
return #{
#"type":#"idString",
#"content":idString
}
And using
NSDictionary * returnDict = [className load:dict]
if ([returnDict[#"type"] isEqualToString:#"idString"]) {
//code here
}
else{
//code here
}
Finally, I know this is not a best solution but it will work fine.
I'd make 2 separate methods. First would only return the id string, the second one would return a message.
That way you can make something like this:
NSDictionary *dict = /* some code here */;
NSString *message = nil;
NSString *idString = [foo loadId:dict];
if (idString.length == 0) {
message = [foo loadMessage:dict];
}
Instead of returning a simple string, create an object that will be returned:
#interface Result: NSObject
#property (nonatomic) NSString *id;
#property (nonatomic) NSString *message;
#end
Ideally, you could create -initWithDictionary: initializer that would handle the parsing.
You can use NSException. Instead of returning idString you throw an NSException
#throw [NSException exceptionWithName:idString reason:nil userInfo:nil];
Then you can call your method like this:
#try{
NSString *messageString = [className load:dict];
NSLog(#"Message String: %#", messageString);
}#catch (NSException *e){
NSString * idString = e.name;
NSLog(#"ID String: %#",idString);
}

How to access data "deep" inside a NSDictionary?

I am getting a JSON response from a web service as follows into a NSDictionary
NSDictionary *fetchAllCollectionsJSONResponse = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
If I dump the output of the NSDictionary it looks correct like this
2017-10-06 10:11:46.097698+0800 NWMobileTill[396:33294] +[ShopifyWebServices fetchAllCollections]_block_invoke, {
data = {
shop = {
collections = {
edges = (
{
cursor = "eyJsYXN0X2lkIjo0NTI4NTY3MTcsImxhc3RfdmFsdWUiOiI0NTI4NTY3MTcifQ==";
node = {
description = "";
id = "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzQ1Mjg1NjcxNw==";
};
},
{
cursor = "eyJsYXN0X2lkIjo0NTI4NTkwODUsImxhc3RfdmFsdWUiOiI0NTI4NTkwODUifQ==";
node = {
description = "Test Collection 1";
id = "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzQ1Mjg1OTA4NQ==";
};
},
{
cursor = "eyJsYXN0X2lkIjo0NTU0OTMwMDUsImxhc3RfdmFsdWUiOiI0NTU0OTMwMDUifQ==";
node = {
description = Sovrum;
id = "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzQ1NTQ5MzAwNQ==";
};
},
{
cursor = "eyJsYXN0X2lkIjo0NTU0OTMzODksImxhc3RfdmFsdWUiOiI0NTU0OTMzODkifQ==";
node = {
description = Badrum;
id = "Z2lkOi8vc2hvcGlmeS9Db2xsZWN0aW9uLzQ1NTQ5MzM4OQ==";
};
}
);
pageInfo = {
hasNextPage = 0;
};
};
};
};
}
I need to access the "description" attribute deep inside this structure and I cannot figure out how to do it.
I tried the following but it crashes
for (NSDictionary *dictionary in fetchAllCollectionsJSONResponse) {
NSLog(#"jongel %#", [dictionary objectForKey:#"data"]);
}
#Bilal's answer is right. This might be a bit easier to read:
NSArray *edges = fetchAllCollectionsJSONResponse[#"data"][#"shop"][#"collections"][#"edges"];
for (NSDictionary *edge in edges) {
NSString *description = edge[#"node"][#"description"];
NSLog(#"description = %#", description);
}
fetchAllCollectionsJSONResponse is a Dictionary not an Array. Try this.
NSDictionary *fetchAllCollectionsJSONResponse = nil;
NSDictionary *data = fetchAllCollectionsJSONResponse[#"data"];
NSDictionary *shop = fetchAllCollectionsJSONResponse[#"shop"];
NSDictionary *collections = fetchAllCollectionsJSONResponse[#"collections"];
NSArray *edges = fetchAllCollectionsJSONResponse[#"edges"];
// Or a shorter version
// NSArray *edges = fetchAllCollectionsJSONResponse[#"data"][#"shop"][#"collections"][#"edges"];
for (NSDictionary *edge in edges) {
NSString *cursor = edge[#"cursor"];
NSDictionary *node = edge[#"node"];
}

chatDidReceiveMessage method not called QuickBlox

I am using QuickBlox-iOS SDK for chatting. Login/Signup is working perfectly. Also I am able to send message but the delegate method
- (void)chatDidReceiveMessage:(QBChatMessage *)message;
is not getting called. Here's the code I am using to setup chat. Adding the following code in appDelegate :
// connect to Chat
[[QBChat instance] addDelegate:self];
QBUUser *currentUser = [QBUUser user];
currentUser.ID = [Global sharedInstance].currentUser.ID;
currentUser.password = #"password";
[[QBChat instance] connectWithUser:currentUser completion:^(NSError * _Nullable error) {
NSLog(#"connect to chat error %#",error);
}];
And the below code I am using to send message :
QBChatMessage *message = [QBChatMessage message];
message.recipientID=[Global sharedInstance].QBUserID;
message.senderID=[Global sharedInstance].currentUser.ID;
[message setText:messageTextView.text];
message.dateSent = [NSDate date];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[#"save_to_history"] = #YES;
[message setCustomParameters:params];
[QBRequest createMessage:message successBlock:^(QBResponse *response, QBChatMessage *createdMessage) {
NSLog(#"success: %#", createdMessage);
} errorBlock:^(QBResponse *response) {
NSLog(#"ERROR: %#", response.error);
}]
I checked on QuickBlox dashboard. It shows all the sent/received messages. But the delegate is not getting called when I send message to another user. I am not using any additional services classes (QMServices) like they are using in their Example Project. Any help would be appreciated. Thanks
I don't understand why you're using the [QBRequest createMessage:successBlock:errorBlock:] method to send messages to another user.
For me what always worked was to create a chatDialog with the user you're trying to message, like so:
QBChatDialog *dialog = [[QBChatDialog alloc] initWithDialogID:nil
type: QBChatDialogTypePrivate];
dialog.occupantIDs = #[#([Global instance].QBUserID),
#([Global instance].currentUser.user.ID)];
Afterwards, you can call Quickblox method to create the dialog on the servers:
if (dialog.ID == nil) {
[QBRequest createDialog:dialog successBlock:^(QBResponse *response, QBChatDialog *createdDialog) {
[self sendMessageToDialog: dialog withText:#"Hello friend!"];
} errorBlock:^(QBResponse *response) {
NSLog(#"dialog creation err: %#", response);
}];
}
Create the message:
- (QBChatMessage *) createMessageWithText: (NSString *)text andDialog: (QBChatDialog*)dialog {
QBChatMessage *message = [QBChatMessage message];
message.text = text;
message.senderID = [Global instance].currentUser.ID;
message.markable = YES;
message.deliveredIDs = #[#([Global instance].currentUser.ID)];
message.readIDs = #[#([Global instance].currentUser.ID)];
message.dialogID = dialog.ID;
message.dateSent = [NSDate date];
message.recipientID = dialog.recipientID;
message.customParameters = [NSMutableDictionary dictionary];
message.customParameters[kQMCustomParameterDialogID] = dialog.ID;
message.customParameters[kQMCustomParameterDialogType] = [NSString stringWithFormat:#"%lu",(unsigned long)dialog.type];
message.customParameters[#"application_id"] = #"<your-application-id>";
message.customParameters[#"save_to_history"] = #"1";
if (dialog.lastMessageDate != nil){
NSNumber *lastMessageDate = #((NSUInteger)[dialog.lastMessageDate timeIntervalSince1970]);
message.customParameters[kQMCustomParameterDialogRoomLastMessageDate] = [lastMessageDate stringValue];
}
if (dialog.updatedAt != nil) {
NSNumber *updatedAt = #((NSUInteger)[dialog.updatedAt timeIntervalSince1970]);
message.customParameters[kQMCustomParameterDialogRoomUpdatedDate] = [updatedAt stringValue];
}
return message;
}
And then send the message to the dialog:
- (void) sendMessageToDialog: (QBChatDialog *)dialog withText: (NSString *)text {
QBChatMessage *message = [[ChatService shared] createMessageWithText:text andDialog:self.dialog];
[dialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
if (error != nil) {
NSLog(#"error creating message %#", error);
} else {
NSLog(#"message sent!");
}
}];
}
I think following this flux you'll be able to receive the callback through the delegate.
EDIT - I forgot to mention the consts I used in the code above are:
NSString const *kQMCustomParameterDialogID = #"dialog_id";
NSString const *kQMCustomParameterDialogRoomName = #"room_name";
NSString const *kQMCustomParameterDialogRoomPhoto = #"room_photo";
NSString const *kQMCustomParameterDialogRoomLastMessageDate = #"room_last_message_date";
NSString const *kQMCustomParameterDialogUpdatedDate = #"dialog_updated_date";
NSString const *kQMCustomParameterDialogType = #"type";
NSString const *kQMCustomParameterDialogRoomUpdatedDate = #"room_updated_date";
Have you added <QBChatDelegate> into your .h file.

Alternate Syntax fo assigning json data objectForKey to attributes of class

Can i write last three lines of code in a single line:
NSArray* latestLoans = [self.JsonData objectForKey:#"loans"];
for (id object in latestLoans) {
NSDictionary* loan = object;
newModelClass.name = [loan objectForKey:#"name"];
newModelClass.sector = [loan objectForKey:#"sector"];
newModelClass.activity = [loan objectForKey:#"activity"];
my complete code is here and if there is any other best practice, please suggest me.
-(void)CopyOnlineData:(NSDictionary*)OnlineData{
self.JsonData = OnlineData;
NSArray* latestLoans = [self.JsonData objectForKey:#"loans"];
for (id object in latestLoans) {
NewModelClass *newModelClass = [[NewModelClass alloc] init];
NSDictionary* loan = object;
newModelClass.name = [loan objectForKey:#"name"];
newModelClass.sector = [loan objectForKey:#"sector"];
newModelClass.activity = [loan objectForKey:#"activity"];
NSDictionary *loactionDictionary = loan[#"location"];
newModelClass.country = loactionDictionary[#"country_code"];
newModelClass.town = loactionDictionary[#"town"];
NSDictionary *imageid = loan[#"image"];
newModelClass.ImageId = imageid[#"id"];
NSLog(#"name: %# \n town: %#\n sector: %#\n country: %#\n activity: %#\n image id: %#", newModelClass.name,newModelClass.town,newModelClass.sector,newModelClass.country,newModelClass.activity,newModelClass.ImageId);
[self.tableData addObject:newModelClass];
}
[[self KivaTableView]reloadData];
}
Thank you,
Heres what I would do: create a custom init for NewModelClass in order to remove it from this part of code. That way, if you have to create a new object of NewModelClass, you won't have to rewrite all the lines.
In NewModelClass.h:
-(id)initWithJSONDict:(NSDictionary *)dict;
In NewModelClass.m:
-(id)initWithJSONDict:(NSDictionary *)dict
{
self = [super init];
if (self)
{
self.name = [dict objectForKey:#"name"];
self.sector = [dict objectForKey:#"sector"];
self.activity = [dict objectForKey:#"activity"];
NSDictionary *loactionDictionary = dict[#"location"];
self.country = loactionDictionary[#"country_code"]; //or dict[#"location][#"country_code"];
self.town = loactionDictionary[#"town"];//or dict[#"location][#"town"];
NSDictionary *imageid = dict[#"image"];
self.ImageId = imageid[#"id"];//or dict[#"image][#"id"];
}
return self;
}
I'd override description too:
-(NSString *)description
{
return [NSString stringWithFormat:#"<%# %p>: name: %# \n town: %#\n sector: %#\n country: %#\n activity: %#\n image id:", [self class], self, self.name, self.town, self.sector, self.country, self.activity, self.ImageId ];
}
Then in your code:
-(void)addOnlineData:(NSDictionary*)onlineData
{
self.JsonData = onlineData;
NSArray* latestLoans = [self.JsonData objectForKey:#"loans"];
for (NSDictionary *aLoan in latestLoans)
{
NewModelClass *newModelClass = [[NewModelClass alloc] initWithJSONDict:aLoan];
NSLog(#"Loan: %#", loan);
[self.tableData addObject:newModelClass];
}
[[self KivaTableView]reloadData];
}
Modification of the for loop, since you already know it's a NSDictionary, so no use of for id, then id cast to NSDictionary.
Modification of the method name:
Start method with a lower case.
Avoid naming starting with "copy" if you don't do a copy.
Note: This code is not tested, it may not compile due to a light syntax error, but you should get the main idea.

Unable to parse the result returned by NSAppleScript method executeAndReturnError

This is the code which i am using:
NSDictionary *errorInfo=nil;
NSString *source=#"tell application \"Mail\"\nget name of mailbox of every account\nend tell";
NSAppleScript *run = [[NSAppleScript alloc] initWithSource:source];
NSAppleEventDescriptor *aDescriptor=[[NSAppleEventDescriptor alloc]init];
aDescriptor=[run executeAndReturnError:&errorInfo];
[aDescriptor coerceToDescriptorType:'utxt'];
NSLog(#"result:%#",[aDescriptor stringValue]);
Output which i got:
result:(null)
Please help me anyone on this.Thanks in advance:)
IIRC that will return a list descriptor filled with list descriptors. You need to iterate over them and pull out the info you want. You're also initializing a descriptor and then immediately overwriting its pointer. Do something like (untested):
NSDictionary *errorInfo = nil;
NSString *source = #"tell application \"Mail\"\nget name of mailbox of every account\nend tell";
NSAppleScript *run = [[NSAppleScript alloc] initWithSource:source];
NSAppleEventDescriptor *aDescriptor = [run executeAndReturnError:&errorInfo];
NSInteger num = [aDescriptor numberOfItems];
// Apple event descriptor list indexes are one-based!
for (NSInteger idx = 1; idx <= num; ++idx) {
NSAppleEventDescriptor *innerListDescriptor = [aDescriptor descriptorAtIndex:idx];
NSInteger innerNum = [innerListDescriptor numberOfItems];
for (NSInteger innerIdx = 1; innerIdx <= innerNum; ++innerIdx) {
NSString *str = [[innerListDescriptor descriptorAtIndex:innerIdx] stringValue];
// Do something with str here
}
}
Swift version, tested in 2022:
func run(appleScript: String) {
var error: NSDictionary? = nil
if let scriptObject = NSAppleScript(source: appleScript) {
let output = scriptObject.executeAndReturnError(&error)
// Print All Values
let numberOfItems = output.numberOfItems
print("numberOfItems: \(numberOfItems)")
for i in 0..<numberOfItems {
let innerDescriptor = output.atIndex(i)
print("\(i): " + (innerDescriptor?.stringValue ?? "nil"))
}
// Catch Error
if let error = error {
print("Error: '\(error)'")
}
} else {
print("Error: Unable to init NSAppleScript")
}
}