I am not figuring out how to perform a query in Azure. I did finally figure out inserts, but now I am trying to query from Azure. Two parts here, how do I return the result from Azure and how do I read the results in objective-C?
Thus far, I have this
-(double)GetValidAppVersion
{
// Create a proxy client for sending requests to the Azure platform.
MSClient *client = [MSClient clientWithApplicationURLString : #""
withApplicationKey : #"];
MSTable *appSettingsTable = [client getTable:#"AppSettings"];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"Key == AppVersion"];
NSArray *queryResults = [[NSArray alloc] init];
[appSettingsTable readWhere:predicate completion:^(NSArray *results, NSInteger totalCount, NSError *error)
{
self.items = [results mutableCopy];
}];
return 1.0;
}
I have not figured out the Azure side either. How can I query and return a result based on the input parameter?
My table is simple with
ID int
Key varchar
Value varchar
Any help with getting this going is greatly appreciated.
Edit:
I added this to my controller
-(bool) IsAppVersionValid
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
double validAppVersion = [delegate.appVersion doubleValue];
double serverAppVersion;
NSDictionary *item = #{ #"complete" : #(NO) };
[self.Service SelectAppVersion:item completion:^(NSUInteger index)
{
}];
return true;//clientVersion >= validAppVersion;
}
And this to my service (this is sloppy as it should be a simple completion block -- I would like to pass NSString * with the AppSettings key value and use that in the predicate as well. Any thoughts on the syntax for that?
typedef void (^CompletionWithAppVersionBlock)(NSUInteger index);
- (void) SelectAppVersion:(NSDictionary *) item
completion:() completion;
All of the read table read methods that are part of the iOS SDK for Mobile Services are asynchronous which means that you have to pass a completion block into them (as you're doing above where you're setting self.items = [results mutableCopy];) in order to then do something with the results they are fetching.
This means that in order to get the value you're looking for, you'll want to pass in a completion block into your GetValidAppVersion method. You can then pass the app version you're getting back to that block. So something like this:
-(void) GetValidAppVersion:(NSDictionary *)item completion:(CompletionWithVersion)completion
{
MSTable *appSettingsTable = [client getTable:#"AppSettings"];
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"Key == AppVersion"];
NSArray *queryResults = [[NSArray alloc] init];
[appSettingsTable readWhere:predicate completion:^(NSArray *results, NSInteger totalCount, NSError *error)
{
completion([results objectAtIndex:0]);
}];
}
You would need to define the CompletionWithVersion as being a block with a parameter returned (the AppVersion). Take a look at the iOS quickstart application to see how the completion blocks are defined.
Related
I'm trying to check if NSString 'testing' (47) exists inside of my NSMutableArray 'self.checkfriendData'. I'm using the code below, though after logging my if statement it appears as though it's never executed (even though the statement is true - see console data below, uid = 47, and thus hiding my object should fire?) Any idea as to why this isn't working? Help is much appreciated!
ViewController.m
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.checkfriendData = (NSMutableArray *)responseObject;
NSString *testing = #"47";
NSArray *friendorNo = self.checkfriendData;
if ([friendorNo containsObject:testing]) // YES
{
self.addFriend.hidden = YES;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
Here's what's inside self.checkfriendData:
2017-05-18 19:36:07.266529-0700 This is the friend data check (
{
body = "My name is Britt";
friendphoto = "/sites/default/files/stored/x.jpg";
"node_title" = "Britt";
uid = 47;
}
)
It appears that your NSArray contains NSDictionarys and you are asking if the array contains an NSString. The answer will always be no as the array doesn't directly contain any NSStrings.
If you want to search for the uid of 47 you will have to iterate over the array and check the uid key of each NSDictionary for the value 47.
The code for this would look something like:
for (NSDictionary *dict in friendorNo) {
if ([dict[#"uid"] isEqualToString:testing]) {
self.addFriend.hidden = YES;
}
}
I have an entity named Geometry which is related to a Plot entity. In our piece of code, given the Plot and some downloaded data stored in a NSDictionary, we must get the Geometry and set some NSString properties but, after doing that, I find that the relationship between entities is lost.
NSError * saveError = nil;
NSFetchRequest * request = [[NSFetchRequest alloc] initWithEntityName:[Geometry entityName]];
request.predicate = [NSPredicate predicateWithFormat:#"plot == %#", plot];
NSError * error = nil;
NSArray * results = [context executeFetchRequest:request error:&error];
Geometry * __block geometry = nil;
if ([results count] > 0) {
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
Geometry * geometryObject = obj;
if ([geometryObject.gid isEqualToString:[NSString stringWithFormat:#"%#", [data valueForKey:#"gid"]]]) {
geometry = geometryObject;
stop = YES;
}
}];
}
if (geometry != nil) {
[geometry setPolygon:[NSString stringWithFormat:#"%#", [data valueForKey:#"polygon"]]];
}
if (![context save:&saveError]) {
NSLog(#"%#", saveError);
}
The first time I run this code results have one object, but the next time I run this there is no results.
Assume everything outside the scope of this piece of code is working right. Any hint or clue about why this happen? How can I solve this?
EDIT: The problem has been solved outside the scope of the code posted and outside the scope of this question. I should have properly reviewed the code further.
There is nothing in your code that breaks the relationship. The error must be elsewhere.
You have a Plot object, so you can get the geometries with plot.geometries without a fetch request, and filter them without a loop:
Geometry *geometry = [plot.geometries
filteredArrayUsingPredicate: [NSPredicate predicateWithFormat:#"gid = %#", gidString]]
.firstObject
where geometries is the name of the inverse relationship for plot.
You can now set the polygon property and save. Check your setPolygon method if you are not removing the relationship.
Hi I need to download over 2000 records from azure , the maximum you can download is 1000 at the time , so I need to use a completion handler to download 200 at the time.
They posted this code as an example but I don't know how to use.
If I copy this to Xcode there is an error
(bool)loadResults() - Error " Expect Method Body "
Returning data in pages
Mobile Services limits the amount of records that are returned in a single response. To control the number of records displayed to your users you must implement a paging system. Paging is performed by using the following three properties of the MSQuery object:
BOOL includeTotalCount
NSInteger fetchLimit
NSInteger fetchOffset
In the following example, a simple function requests 20 records from the server and then appends them to the local collection of previously loaded records:
- (bool) loadResults() {
MSQuery *query = [self.table query];
query.includeTotalCount = YES;
query.fetchLimit = 20;
query.fetchOffset = self.loadedItems.count;
[query readWithCompletion:(NSArray *items, NSInteger totalCount, NSError *error) {
if(!error) {
//add the items to our local copy
[self.loadedItems addObjectsFromArray:items];
//set a flag to keep track if there are any additional records we need to load
self.moreResults = (self.loadedItems.count < totalCount);
}
}];
}
thanks for your help.
If you are getting Error " Expect Method Body " then you copied it into your code incorrectly and there is a formatting issue.
If you want to load data with paging in a single call, I would do something like this:
in your .h file declare
typedef void (^CompletionBlock) ();
#property (nonatomic, strong) NSMutableArray *results;
in your .m file
- (void)loadData
{
self.results = [[NSMutableArray alloc] init];
MSClient *client = [MSClient clientWithApplicationURLString:#"YOUR_URL" applicationKey:#"YOUR_KEY"]
MSTable *table = [client tableWithName:#"YOUR_TABLE"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"YOUR_SELECT_FILTER"];
MSQuery *query = [[MSQuery alloc] initWithTable:table predicate:predicate];
//note the predicate is optional. If you want all rows skip the predicate
[self loadDataRecursiveForQuery:query withCompletion:^{
//do whatever you need to do once the data load is complete
}];
}
- (void)loadDataRecursiveForQuery:(MSQuery *)query withCompletion:(CompletionBlock)completion
{
query.includeTotalCount = YES;
query.fetchLimit = 1000; //note: you can adjust this to whatever amount is optimum
query.fetchOffset = self.results.count;
[query readWithCompletion:(NSArray *items, NSInteger totalCount, NSError *error) {
if(!error) {
//add the items to our local copy
[self.results addObjectsFromArray:items];
if (totalCount > [results count]) {
[self loadDataRecursiveForQuery:query withCompletion:completion];
} else {
completion();
}
}
}];
}
Note: I haven't tested this code, but it should work more or less.
This is a SyncEngine from an RW tutorial. I need help understanding how only UPDATED records from the web are fetched and processed into Core Data.
- (void)processJSONDataRecordsIntoCoreData {
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
// Iterate over all registered classes --- CHECK!
for (NSString *className in self.registeredClassesToSync) {
if (![self initialSyncComplete]) {
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:#"results"];
for (NSDictionary *record in records) {
[self newManagedObjectWithClassName:className forRecord:record];
}
} else {
NSArray *downloadedRecords = [self JSONDataRecordsForClass:className sortedByKey:#"objectId"];
if ([downloadedRecords lastObject]) {
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
int currentIndex = 0;
//if dl count is < current index, there is an updated object dl from the web
for (NSDictionary *record in downloadedRecords) {
NSManagedObject *storedManagedObject = nil;
//Quick check to see if they indeed match, if they do, update the stored object with remote service objects
if ([storedRecords count] > currentIndex) {
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
}
//Othwerwise its a new object and you need to create a new NSManagedObject to represent it in CDdb
if ([[storedManagedObject valueForKey:#"objectId"] isEqualToString:[record valueForKey:#"objectId"]]) {
[self updateManagedObject:[storedRecords objectAtIndex:currentIndex] withRecord:record];
} else {
[self newManagedObjectWithClassName:className forRecord:record];
}
currentIndex++;
}
}
}
// After all NSMO are created in your context, save it!
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"Unable to save context for class %#", className);
}
}];
// Cleanup time
[self deleteJSONDataRecordsForClassWithName:className];
[self executeSyncCompletedOperations];
}
[self downloadDataForRegisteredObjects:NO];
}
From what I understand, on the first or initial sync, it fetches JSONDictionaryForClassWithName which reads the downloaded data from disk and creates a newManagedObjectWithClassName.
My confusion is in the update else block. downloadedRecords is populated from JSONDataRecordsForClass which simply calls JSONDictionaryForClassWithName. Then it checks to see if there is at least 1 object in that array. If there is it does this:
NSArray *storedRecords = [self managedObjectsForClass:className sortedByKey:#"objectId" usingArrayOfIds:[downloadedRecords valueForKey:#"objectId"] inArrayOfIds:YES];
This fetches all managedObjectsForClass:sortedByKey which is below:
- (NSArray *)managedObjectsForClass:(NSString *)className sortedByKey:(NSString *)key usingArrayOfIds:(NSArray *)idArray inArrayOfIds:(BOOL)inIds {
__block NSArray *results = nil;
NSManagedObjectContext *managedObjectContext = [[SDCoreDataController sharedInstance] backgroundManagedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:className];
NSPredicate *predicate;
if (inIds) {
predicate = [NSPredicate predicateWithFormat:#"objectId IN %#", idArray];
} else {
predicate = [NSPredicate predicateWithFormat:#"NOT (objectId IN %#)", idArray];
}
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:#"objectId" ascending:YES]]];
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
}];
return results;
}
The next bit which compares the [storedRecords count] > currentIndex is confusing. Can someone please explain this? I think my confusion lies in what the managedObjectsForClass method does with the usingArraysOfIds & inArrayOfIds.
I would expect that at some point it gets the the updatedAt field from the downloaded records and compares it to the updatedAt field of the CoreData fetched records.
This function is processing the stored JSON. The actual remote fetching and updateAt checking happens in downloadDataForRegisteredObjects and mostRecentUpdatedAtDateForEntityWithName.
[storedRecords count] > currentIndex is a bit crazy. Although in defense of the original programmer, writing any decent syncengine will quickly make you go googoo. Basically he needs to work out which records are existing and which ones are new and update the local data store accordingly, that's all.
I had another look and this code is actually horribly broken. It will only works if either you have the same records both locally and remotely. Or if the new objects have an objectId that sort-wise comes after the last object the local store has. Which is not the case with Parse objectId's.
If you are testing with just one device this works because new objects will be inserted locally before being pushed to the server. Therefor you will always have the same amount of records. If additional records get inserted any other way, this code will do weird things.
So I am using restkit for asynchronous calls and such, and felt the one of the best ways to handle possible connection issues was to have a requestManager Which holds a single an array of requests and it will keep looping through it and remove whenever they are successful, this way the payload for the request can be created and added to the manager and the rest of the app can continue without having to worry about returns or not unless it is a GET... Now my issue is that some of the requests are using closures or code Blocks as you will.
And i am having trouble wrapping my head around the syntax and usage of it properly. Especially when it comes to blocks.
This is how I plan to implement it...
so I first Make this Call
GameInfo *game = [[GameInfo alloc] init];
game.myWTFID = [playerInfo _wtfID];
game.opponentWTFID = [gameInfoObj GetOpponentWTFID];
game.gameType = [NSNumber numberWithInt:[gameInfoObj gameType]];
game.turnNumber = [gameInfoObj GetTurnNumber];
game.lastMove = [gameInfoObj GetGameID];
[[RequestPayload sharedRequestPayload] addPutRequestForObject : game withSerializationMapping : serializedGameMap forClass : [Game class] withBlock :^(RKObjectLoader *loader)
{
[loader setObjectMapping:gameMap];
loader.delegate = self;
loader.onDidLoadResponse = ^(RKResponse *response)
{
[game release];
};
loader.targetObject = nil;
loader.backgroundPolicy = RKRequestBackgroundPolicyRequeue;
}
];
here Is the Implementation
- ( void ) addPutRequestForObject : (id) object withSerializationMapping : (RKObjectMapping*) serialMapping forClass : (Class) class withBlock : ( void(^)(RKObjectLoader*) ) block
{
NSMutableDictionary *dict = [NSMutableDictionary new];
NSNumber *postType = [[NSNumber alloc]initWithInt:1];
[dict setObject:postType forKey:#"request"];
[dict setObject:object forKey:#"data"];
[dict setObject:serialMapping forKey:#"serialMapping"];
[dict setObject:class forKey:#"class"];
void (^ copyBlockLoader)(id,RKObjectMapping*) = Block_copy(block);
[dict setObject:copyBlockLoader forKey:#"block"];
Block_release(copyBlockLoader);
[postType release];
postType = nil;
[_RequestsToInvoke addObject:dict];
}
and then within a for loop after going though each object in the array which will be a dictionary containing the needed information to do something like this.(apologies if this does not make sense it is highly contrived as the actual method is alot longer but the vital part i thought was here.)
[[RKObjectManager sharedManager].mappingProvider setSerializationMapping:[dict objectForKey:#"serialMapping"] forClass:[dict objectForKey:#"class"]];
void(^block)(RKObjectLoader *) = [dict objectForKey:#"block"];
[[RKObjectManager sharedManager] putObject:object usingBlock:block];
So my questions are
in the first snippet where i ask it to release the game object...Will that work? or will it result in a leak the game object is declared within the same scope as that call so the only pointer i have left is the one in the code block.
am i saving the block into the dictionary correctly?
and last does anyone have a better alternative? or spot something else with my implementation that needs correcting/modification?