How to work with cloudkIt fetchOperation.fetchRecordsCompletionBlock - objective-c

How do I access each property in cloudKit Users. I have an array of recordsIDs that I use to fetch. I get back the records (2 in this example) in one dictionary.
NSMutableArray *recordIDs;
for (NSString *recordName in recordNames) {
CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName:recordName];
[recordIDs addObject:recordID];
}
CKFetchRecordsOperation *fetchOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:recordIDs];
fetchOperation.fetchRecordsCompletionBlock = ^(NSDictionary /* CKRecordID * -> CKRecord */ *recordsByRecordID, NSError *operationError) {
NSLog(#"recordsByRecordID %#", recordsByRecordID);
};
Console logs the following:
2015-04-11 11:04:15.829 projectName[10154:555807] recordsByRecordID {
"<CKRecordID: 0x7fea3bd86f80; _c835c6d554eafe23xxxxb65a1a9dd94d:(_defaultZone:__defaultOwner__)>" = "<CKRecord: 0x7fea44596fc0; recordType=Users, recordID=_c835c6d554eafe23xxxxb65a1a9dd94d:(_defaultZone:__defaultOwner__), recordChangeTag=i7rxe5ce, values={\n identifier = \"3E7837F6-C23C-44B2-89E8-5D9DF9AB061E\";\n points = 50;\n username = \"_c835c6d554eafe23xxxxb65a1a9dd94d\";\n}>";
"<CKRecordID: 0x7fea3e0614d0; _cd5f8486186028eaxxx6b6597489619:(_defaultZone:__defaultOwner__)>" = "<CKRecord: 0x7fea3e2567a0; recordType=Users, recordID=_cd5f8486186028eaxxx6b6597489619:(_defaultZone:__defaultOwner__), recordChangeTag=i7s7706j, values={\n identifier = \"4B3B374E-42B0-47B7-8638-A8967C23FA21\";\n points = 250;\n username = \"_cd5f8486186028eaxxx6b6597489619\";\n}>";
}
I want to retrieve the username and points and add it to a local dictionary.

I got it. Looks like I need to pass in the recordID for each record then the key to retrieve.
for (CKRecordID *recordID in recordsByRecordID) {
NSString *pointsString = recordsByRecordID[recordID][PointsKey];
NSLog(#"string %#", pointsString);
}

Related

Magical Record - duplicate records appearing in objective-c

in my ios application, i am using magical record.. first of all i am retrieving json data from server. Then json data should be stored into magical record. The problem is when i fetch the data from magical record i got data repeatedly. i want to display magical record data in tableview. if i run the application every time the menu item count incremented.
json response:
App Menu List Item response:
{
Status = Success;
data =
(
{
Text = "Emergency Summary";
},
{
Text = "Problems New";
},
{
Text = "Family History New";
}
my magical record response is:
list items (entity: AppMenuList; id: 0x7b10fb80 ; data:
{
menuItemName = "Emergency Summary";
list items (entity: AppMenuList; id: 0x7b10fb90
menuItemName = "Emergency Summary";
list items (entity: AppMenuList; id: 0x7b10fba0
menuItemName=“Problems New”
i am fetching data from magical record like this:
(void)parseGetAppMenuListResponse:(NSDictionary *)mediaResponse {
NSArray *dbMenuListArray = [AppMenuList MR_findAll];
NSArray *menuListDetailsArray = [MenuListDetails MR_findAll];
DLog(#"AppMenuList: %#", [AppMenuList MR_findAll]);
DLog(#"MenuListDetails: %#", [MenuListDetails MR_findAll]);
DLog(#"MenuResponse: %#", mediaResponse);
for (int iCount = 0; iCount < [[mediaResponse objectForKey:#"data"] count]; iCount++) {
NSDictionary *menuData = [[mediaResponse objectForKey:#"data"] objectAtIndex:iCount];
//Checking for the existence fo records in database and then remove the records
for (AppMenuList *list in dbMenuListArray) {
if ([list.userID isEqualToString:[[UserDefaults defaultsInstance] getUserId]]) {
[list MR_deleteEntity];
}
NSLog(#"list items %#",list);
}
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
// Do your work to be saved here, against the `localContext` instance
// Everything you do in this block will occur on a background thread
}
If what you do is saveWithBlock without wait, then multiple request results in multiple saving at almost the same time, so you get duplicate record.
Okay, I tried to do:
[[NSManagedObjectContext MR_rootSavingContext] performBlockAndWait:^{
//Get your data.
}];
I am fetching data using [MyEntity MR_findAllWithPredicate:myPredicate].
Hope it will be useful.

lua_touserdata is returning null

I am badly stuck trying to get my userInfo reference. One of my method is returning instance of the object. Everytime createUserInfo is called, it will return the userInfoObject to the lua.
However, when I call a method for userInfo object from Lua, I am not able to get the reference of the userInfo object (lua_touserdata(L,1))
static int getUserName (lua_State *L){
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,1);
// The following is throwing null! Need help.
// Not able to access the userInfo object.
NSLog(#"UserInfo Object: %#", *userInfo);
}
static const luaL_reg userInstance_methods[] = {
{"getUserName", getUserName},
{NULL, NULL}
}
int createUserInfo(lua_State *L){
UserInfo *userInfo = [[UserInfo alloc] init];
UserInfoData **userInfoData = (UserInfoData **)lua_newuserdata(L, sizeof(userInfo*));
*userInfoData = userInfo;
luaL_openlib(L, "userInstance", userInstance_methods, 0);
luaL_getmetatable(L, "userInfoMeta");
lua_setmetatable(L, -2);
return 1;
}
// I have binded newUserInfo to the createUserInfo method.
// I have also created the metatable for this userInfo Object in the init method.
// luaL_newmetatable(L, "userInfoMeta");
// lua_pushstring(L, "__index");
// lua_pushvalue(L, -2);
// lua_settable(L, -3);
// luaL_register(L, NULL, userInstance_methods);
Please let me know if I am missing anything!
My LuaCode snippet:
local library = require('plugin.user')
local userInfo = library.newUserInfo()
print(userInfo.getUserName())
Update
I got rid of null, after using lua_upvalueindex(1) This is giving reference back to the user info instance.
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,lua_upvalueindex( 1 ));
Hope it helps others too!
I think it may be the way your dealing with the userdata's metatable. Specifically, I think what you're returning from createUserInfo() is a table not a userdata. What I suggest is that you create the metatable once e.g. in luaopen, and then just set that on the new userdata. Something like this...
int createUserInfo(lua_State *L) {
UserInfo *userInfo = [[UserInfo alloc] init];
UserInfoData **userInfoData = (UserInfoData **)lua_newuserdata(L, sizeof(userInfo));
*userInfoData = userInfo;
luaL_getmetatable(L, "userInfoMeta");
lua_setmetatable(L, -2);
return 1;
}
LUALIB_API int luaopen_XXX(lua_State *L)
{
luaL_newmetatable(L,"userInfoMeta");
luaL_openlib(L, NULL, userInstance_methods, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
...
lua_upvalueindex(1) has fixed the nil error.
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,lua_upvalueindex( 1 ));
I want to explain in brief on what is actually happening. function(s) in C will get stack of params passed to the method. Lua online document has example of an Array, where all of its methods take first parameter of array instance. so, lua_touserdata(L,1) worked fine as the first parameter is the array instance.
Example from lua.org shows
a = array.new(10) --size 10
array.insert(a, 1, 1) --array.insert(instance, index, value).
lua_touserdata(L,1) works as the first param is the array instance.
In my case, I was invoking the method over instance without any params. So, the lua stack was empty in my C function and lua_touserdata(L,1) was throwing null.
Example:
a = array.new(10)
a.showValues() --the method is not over array. it is called on instance.
So, inorder to get access to the instance in showValues, I need to call lua_touserdata(L, lua_upvalueindex(1)). This will give the array instance object.

How to (easily) update an ABPerson object with a vCard and keep its unique ID?

The AddressBook Framework offers a great method for initializing an ABPerson with a vCard, by using the initWithVCardRepresentation: method.
What I want to do is update a contact with a certain vCard. I can't use initWithVCardRepresentation: because this will give me a new ABPerson object with a new uniqueId and I want to keep the uniqueId in between these changes.
What's an easy way to go about doing something like this?
Thanks!
initWithVCardRepresentation is still the slickest way to turn your vCard into an ABPerson.
Just use the results of it to find the matching person in your Address Book and then iterate over the vCard properties, placing them into the existing record. The save at the end will harden your changes.
The following example assumes that the unique "keys" will be last-name, first-name. You can modify the search element if you want to include companies where no name is listed or whatever, or you could change the iteration scheme by getting [AddressBook people], and then iterating over the people and using only those records where the key-value pairs match to your satisfaction.
- (void)initOrUpdateVCardData:(NSData*)newVCardData {
ABPerson* newVCard = [[ABPerson alloc] initWithVCardRepresentation:newVCardData];
ABSearchEleemnt* lastNameSearchElement
= [ABPerson searchElementForProperty:kABLastNameProperty
label:nil
key:nil
value:[newVCard valueForProperty:kABLastNameProperty]
comparison:kABEqualCaseInsensitive];
ABSearchEleemnt* firstNameSearchElement
= [ABPerson searchElementForProperty:kABFirstNameProperty
label:nil
key:nil
value:[newVCard valueForProperty:kABFirstNameProperty]
comparison:kABEqualCaseInsensitive];
NSArray* searchElements
= [NSArray arrayWithObjects:lastNameSearchElement, firstNameSearchElement, nil];
ABSearchElement* searchCriteria
= [ABSearchElement searchElementForConjunction:kABSearchAnd children:searchElements];
AddressBook* myAddressBook = [AddressBook sharedAddressBook];
NSArray* matchingPersons = [myAddressBook recordsMatchingSearchElement:searchCriteria];
if (matchingPersons.count == 0)
{
[myAddressBook addRecord:newVCard];
}
else if (matchingPersons.count > 1)
{
// decide how to handle error yourself here: return, or resolve conflict, or whatever
}
else
{
ABRecord* existingPerson = matchingPersons.lastObject;
for (NSString* property in [ABPerson properties]) // i.e. *all* potential properties
{
// if the property doesn't exist in the address book, value will be nil
id value = [newVCard valueForProperty:property];
if (value)
{
NSError* error;
if (![existingPerson setValue:value forProperty:property error:&error] || error)
// handle error
}
}
// newVCard with it's new unique-id will now be thrown away
}
[myAddressBook save];
}

Initializing objects and building CoreData relationships

I'm trying to build the relationships for an object. But I guess I'm not understanding everything properly. When I set them, it looks like they all get set properly (that is, the relationships are found). But when I loop over them later, some of them (not all) don't exist.
I have an Report, which has many LineItems. Each inspection item one Rating. When I create an inspection, I'm copying data from another object, ReportMaster. ReportMaster also has MasterLineItems which also have Ratings. But as of right now I can't guarantee that the relationship between MasterLineItems and Ratings is correct. Therefore when I copy the ReportMaster's attributes for the Report I also do a fetch for the Rating.
I have a method within Report to check to see if all the LineItems have Ratings. But I can never get it to return true.
My question is, how should I properly be setting these attributes, or what am I doing wrong? Thanks. (The relationship building as at the end of the code block)
# Creating Report
Report *report = [Report object];
[report setWithMasterReport:masterReport];
# Report.m
- (void)setWithMasterReport:(MasterReport *)masterReport {
self.name = [masterReport.name copy];
[self addLineItems:[LineItems copyItemsFromMasterReportItems:masterReport.masterLineItems]];
NSLog(#"items have ratings: %i",[self lineItemsHaveRatings]);
}
- (BOOL)lineItemsHaveRatings {
NSArray *lineItems = [[self lineItems] allObjects];
BOOL t = TRUE;
for (LineItem *lineItem in lineItems){
if (lineItem.rating)
t = t && TRUE;
else
t = FALSE;
}
return t;
}
# LineItem.m
+ (NSSet*)copyItemsFromMasterReportItems:(NSSet *)masterLineitems {
NSMutableArray *lineItems = [NSMutableArray arrayWithArray:[masterLineitems allObjects]];
// go through one by one and replace inspectionFormItem with the inspectionItem copy
for (NSUInteger i; i < [lineItems count]; i++){
LineItem *lineItem = [InspectionItem object];
[lineItem setWithMasterLineItem:[lineItems objectAtIndex:i]];
[lineItems replaceObjectAtIndex:i withObject:lineItem];
}
return [NSSet setWithArray:lineItems];
}
- (void)setWithMasterLineItem:(MasterLineItem *)masterLineItem {
self.name = masterLineItem.name;
self.ratingID = masterLineItem.ratingID;
Rating *rating = [Rating findFirstByAttribute:#"myID" withValue:self.ratingID];
if (rating)
[self setRating:rating];
}
When I run that I get
items have ratings: 0
Another part of this question is, in these "init" methods (setWith...:) should I be copying the attribute or not? If so, should I be copying the relationship too? I just want a pointer to the object, right?
Side note: these are CoreData objects and I'm using RestKit which is why I get the findFirstByAttribute:withValue: helper method.
Thanks for any help.
Update
- (BOOL)LineItemsHaveRatings {
NSArray *lineItems = [[self LineItems] allObjects];
BOOL t = TRUE;
for (LineItem *lineItem in lineItems){
if (lineItem.rating)
t = t && TRUE;
else {
t = FALSE;
Rating *rating = [Rating findFirstByAttribute:#"myID" withValue:LineItem.ratingID];
NSLog(#"[No Rating Found] %#",lineItem);
NSLog(#"[Rating to find] %#",rating);
}
}
return t;
}
[No Rating Found] <LineItem: 0x8200e40> (entity: LineItem; id: 0x8200d00 <x-coredata:///LineItem/t05319326-640E-4DA5-B619-EB20621E3D533> ; data: {
rating = nil;
ratingID = 13903;
})
[Rating to find] <Rating: 0xec356a0> (entity: Rating; id: 0xec31520 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/Rating/p15553> ; data: {
myID = 13903;
name = "APPA (Northwestern Memorial Hospital)";
rangeItemRatings = (
"0xec3a2d0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RangeItemRating/p2952>",
"0xec3a2b0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RangeItemRating/p2950>",
"0xec3a2e0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RangeItemRating/p2953>",
"0xec3a2f0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RangeItemRating/p2954>",
"0xec3a2c0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RangeItemRating/p2951>"
);
ratingType = "0xec368b0 <x-coredata://4EE6AD5A-CC34-460A-A97A-0909454126A4/RatingType/p112>";
ratingTypeID = 1;
})
It is strange to me that the other relationships are built and stayed connected. But this one doesn't. ... oh. I didn't have the relationship set to be a to-many relationship. Oops.
I just needed to changed the relationship type for the Rating.

Reorganizate NSDictionary keys based on numbers

I have a NSDictionary, which looks like:
0 = {
content = Test;
date = "2012-02-02 18:36:46 +0000";
};
1 = {
content = "#### da ####";
date = "2012-02-02 18:36:46 +0000";
};
2 = {
content = dfdffdfddfdffddf;
date = "2012-02-03 20:30:31 +0000";
};
But if I delete the second key (number 1 in the example) the dictionary ends up like:
0 = {
content = Test;
date = "2012-02-02 18:36:46 +0000";
};
2 = {
content = dfdffdfddfdffddf;
date = "2012-02-03 20:30:31 +0000";
};
How could I reorganizate them so it maintains the order (like 0, 1, 2, 3) when I need to?
NSDictionaries are unordered, meaning that you cannot control the order of keys and they may change randomly or in undocumented ways between iOS versions.
You have a few options:
Maintain your own, separate NSArray of keys, and use that to index your dictionary.
Use this OrderedDictionary class that I wrote, which basically does that but wraps it all up in an NSDictionary subclass so you don't have to do any extra work:
http://charcoaldesign.co.uk/source/cocoa#ordered-dictionary
If your dictionary keys are numbers (or if they are alphabetical, or something else that's easy to sort automatically), you could just sort the keys whenever you need to display them in order using:
NSArray *keys = [dict allKeys];
keys = [keys sortedArrayUsingSelector:#selector(compare:)];
//now loop over keys array instead of directly over dictionary