Combine Multiple Network Requests with ReactiveCocoa - afnetworking

I'm exploring ReactiveCocoa and trying to see whats possible. The issues i'm having is chaining a few network requests together.
I have 2 calls, the first gets a list of identifiers, then for each identifier I make a call to get data corresponding to that id and create a model object and return an array of the objects.
I'm using RACExtensions for AFNetworking to make the requests. Code looks something like this:
- (RACSignal *) identifersInfo
{
return [[[[self identifiersSignal] flattenMap:^RACStream *(RACTuple *tuple) {
RACTupleUnpack(AFHTTPRequestOperation *operation, id responseObject) = tuple;
NSArray *identifiers = responseObject[#"Identifiers"];
NSArray *requests = [self httpRequestsWithIdentifiers: identifiers];
return [self.client rac_enqueueBatchOfHTTPRequestOperationsWithRequests: requests];
}] collect] map:^id(RACTuple *tuple) {
RACTupleUnpack(AFHTTPRequestOperation *operation, id responseObject) = tuple;
Model *model = [[Model alloc] init];
model.title = responseObject[#"Title"];
model.description = responseObject[#"Description"];
model.lastUpdated = responseObject[#"LastUpdated"];
return model;
}];
}
identifiersSignal method looks like this:
- (RACSignal *) identifiersSignal
{
return [self.client rac_getPath: #"identifiers" parameters: nil];
}
This returns json dictionary which looks like:
{
"Identifiers": [
3,
4,
21
]
}
I'm actually mocking these calls at the moment and I know they work independently, I'm just trying to piece them together using ReacticeCocoa.
I can't figure out or find any decent samples on how this could be achieved using ReactiveCocoa, although I'm pretty confident it could be.

I had to look into the implementation of the AFNetworking extensions but I think you are misusing a some return values. I haven't tested but it seems like your code should look like this:
- (RACSignal *) identifersInfo {
return [[[self identifiersSignal] map:^RACSignal *(RACTuple *tuple) {
RACTupleUnpack(AFHTTPRequestOperation *operation, id responseObject) = tuple;
NSArray *identifiers = responseObject[#"Identifiers"];
NSArray *requests = [self httpRequestsWithIdentifiers: identifiers];
return [self.client rac_enqueueBatchOfHTTPRequestOperationsWithRequests: requests];
}] map:^Model*(id *reponseObject) {
Model *model = [[Model alloc] init];
// same as above
return model;
}];
}
It is a little misleading that rac_getPath returns a RACTuple as rac_enqueueBatchOfHTTPRequestOperationsWithRequests returns only a RACStream of the return objects. AFURLConnectionOperation+RACSupport.m The documentation in the header files should be improved with type information to get this straightened out.
Nice code, though.

Related

Obj-C: Check if object exists in NSMutableArray?

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;
}
}

Safe Subscripting of NSDictionary

With literals syntax one can use NSDictionary *dictionary like this to get the objectForKey
NSDictionary * dictionary;
id object = dictionary[key];
But if the type of dictionary is of type id and you try to write
id dictionary;
id object = dictionary[key];
This will work until if your dictionary was really a dictionary otherwise it would crash.
The solution for that would be to have a method
-(id)safeObject:(id)object forKey:(id)aKey {
if (![object isKindOfClass:[NSDictionary class]]) {
return nil;
}
return [object objectForKeyedSubscript:aKey];
}
So now when I call it like this
id dictionary;
id object = [self safeObject:dictionary forKey:key];
This won't crash. But the problem with this one is that If I have to go deeper in the nested dictionary for example
id object = dictionary[key1][subKey1][subsubKey1];
It is really convenient to write with the literal syntax with old syntax it would be something like
id mainObject = [self safeObject:dictionary forKey:key1];
id subObject = [self safeObject:mainObject forKey:subKey1];
id object = [self safeObject:subObject forKey:subsubKey1];
So not that much readable. I want to have this solution with new literal syntax is this possible?
You could use valueForKeyPath, e.g.
id dictionary = #{#"key":#{#"subkey" : #{ #"subsubkey" : #"value"}}};
id object = [self safeObject:dictionary];
id value = [object valueForKeyPath:#"key.subkey.subsubkey"];
Also slightly change safeObject only to check if it's a dictionary,
- (id)safeObject:(id)object {
if (![object isKindOfClass:[NSDictionary class]]) {
return nil;
}
return object;
}
Hope this helps, is this what you're looking for?

Parsing Nested NSDictionary and NSArray Data in xCode

I have a complex JSON file that I need to parse into a CoreData table. Currently, I capture the data into an NSArray with this format and the following 6 elements:
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[0]: #SchoolID
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[1]: #LastName
2013-08-29 10:54:04.930 iTrackTest[1542:c07] athleteRecords[2]: #Gender
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[3]: SchType
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[4]: #FirstName
2013-08-29 10:54:04.931 iTrackTest[1542:c07] athleteRecords[5]: #IDAthlete
First question, it appears that SchType is a k-dimensional NSArray of NSDictionaries. Is that true?
I have been capturing simpler, single-tiered JSON files using code from Paul Hegarty of Stanford:
dispatch_async(fetchQ, ^{
NSArray *athleteRecords;
athleteRecords = [AthleticNetDataFetcher retrieveDataForAthleteWithID:athleteID];
NSLog(#"In %#: athleteRecords has %d records",NSStringFromClass([self class]), [athleteRecords count]);
NSLog(#"NSArray with athleteRecords: %#", athleteRecords);
[document.managedObjectContext performBlock:^{
int iCount=0;
for (NSDictionary *athleteInfo in athleteRecords) {
[self resultsWithAthleteInfoForAthleteWithID:athleteInfo inManagedObjectContext:document.managedObjectContext];
NSLog(#"athleteRecords[%d]: %#", iCount, athleteInfo);
iCount++;
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
I need data elements from each node for every record in my CoreData table. For example, SchoolName from School node, IDSeason from Season node, and all elements from Results node would be written to a single CoreData table row (record).
Do I need to resort to dot notation and abandon the iteration through the NSArray or do I need to capture multiple NSArrays each with data further down the nodes? Having a hard time getting my head around this.
Thanks!
Not sure why I had such a difficult time getting my head around this, but Hot Licks got me on the right track.
Here is what I learned that might be helpful to others:
If you have multiple NSDictionaries embedded within an array, it is much simpler to parse these sub-dictionaries in other methods.
+(void)parseDivisionBranches:(NSDictionary *)schTypeDictionary usingStudentInfoFrom:(NSDictionary *)myAthleteInfo
intoManagedDoc: (UIManagedDocument *)document
{
NSArray* schoolDivisions = [self wrapDictionaryInArrayIfNecessary:[schTypeDictionary valueForKeyPath:#"School.SchoolDivision"]];
for (NSDictionary* schoolDivision in schoolDivisions) {
[MarksFromMeets parseDictionaryWithXcMarksForAthlete:(NSString*) [myAthleteInfo objectForKey:#"athlete_ID"]
fromDictionary:(NSDictionary *)schoolDivision
intoThisManagedDoc:(UIManagedDocument *)document];
}
}
In instances where only a single NSDictionary is passed at a particular level of the tree, it is simpler to embed that NSDictionary inside an NSArray so that you can use the same code to extract data; therefore, I always check to see if have an NSArray or NSDict.
+ (NSArray*) wrapDictionaryInArrayIfNecessary:(NSObject*)dictionaryMasquaradingAsAnArray
{
NSMutableArray* newArray = [[NSMutableArray alloc] init];
if([dictionaryMasquaradingAsAnArray isKindOfClass:[NSArray class]]) {
newArray = [dictionaryMasquaradingAsAnArray copy];
}else if([dictionaryMasquaradingAsAnArray isKindOfClass:[NSDictionary class]]) {
[newArray addObject:dictionaryMasquaradingAsAnArray];
}else {
NSString *className = NSStringFromClass([dictionaryMasquaradingAsAnArray class]);
NSLog(#"ERROR - dictionaryMasquaradingAsAnArray of %# class", className);
newArray = nil;
}
return newArray;
}
Then parse each sub-dictionary in turn by calling the method associated with the branch of the data tree, in this case:
+ (void)parseDictionaryWithXcMarksForAthlete:(NSString*)withAthleteID
fromDictionary:(NSDictionary *)dictionary
intoThisManagedDoc:(UIManagedDocument *)document
{
NSArray* seasons = [self wrapDictionaryInArrayIfNecessary:[dictionary valueForKeyPath:#"Season"]];
BOOL* parsedSeasonData;
for (NSDictionary* season in seasons) {
parsedSeasonData = [self parseDictionaryWithSeasonsListings:(NSString*)withAthleteID
fromDictionary:(NSDictionary *)season
intoThisManagedDoc:(UIManagedDocument *)document];
}
}
At some nodes, I had to capture data and pass it along down the chain for use later when I would ultimately write a record to CoreData. Again, thanks to Hot Licks and hope this helps others.

Query Azure from iOS

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.

IOS: Storing,retrieving & calling Code blocks

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?

Resources