How to send some data from my Objective-C code to direct MongoDB - objective-c

- (IBAction)outputPowerEditingDidEnd:(id)sender
{
int value = [self outputPowerFromSliderValue:self.outputPowerSlider.value];
self.resultsTextView.text = [self.resultsTextView.text stringByAppendingFormat:#"Output level changed to: %2d\n\n", value];
[self updateReaderInventoryConfiguration];
}
I want send the value of self.outputPowerSlider.value to MongoDB

Related

Update CoreData entity obj

I have a complex CoreData entity: MY_ENTITY
I receive a Object of type MY_ENTITY from my webService.
In some cases, I need to edit my local CoreData obj (MY_ENTITY) with received obj.
So:
I have OBJ_1 in CoreData
I receive OBJ_2 from WebService.
I need to update OBJ_1 from OBJ_2.
Have I to set all field or can I assign OBJ_1 ObjectID to OBJ_2 and save the context (same Context)?
Since they are two separate instances, you need to move what you want from O2 to O1. You can use a routine such as this to do the move attribute by attribute assuming both objects are of the same Entity class:
// use entity description to get entity attributes and use as keys to get value
// scan attributes
NSDictionary *attributes = [[sourceEntity entity] attributesByName];
for (NSString *attribute in attributes) {
id value = [sourceEntity objectForKey:attribute];
if (value == nil) {
continue;
}
NSAttributeType attributeType = [[attributes objectForKey:attribute] attributeType];
switch (attributeType) {
case NSStringAttributeType:
// value = [value stringValue];
break;
case NSInteger16AttributeType:
case NSInteger32AttributeType:
case NSInteger64AttributeType:
case NSBooleanAttributeType:
value = [NSNumber numberWithInteger:[value integerValue]];
break;
case NSFloatAttributeType:
case NSDecimalAttributeType:
value = [NSNumber numberWithDouble:[value doubleValue]];
break;
case NSDateAttributeType:
if (dateFormatter != nil)
value = [dateFormatter stringFromDate:value];
break;
default:
value = #"";
break;
}
[targetEntity setValue:value forKey:attribute];
}
Note, this is just an example and would need to be cleaned up and have error handling added if you intend to use. Also, if you are getting O2 via a webservice as JSON or XML, then you can use this to simply push the JSON payload into the targetEntity. This assumes your payload attrs align with your entity attrs. In this case you would replace sourceEntity with the JSON payload using a block or equivalent for example:
NSArray *seedData = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
options:kNilOptions
error:&err];
[seedData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
...
id value = [obj objectForKey:attribute];
...
}
In which format are you receiving OBJ_2 from the Web Service?
Either way, assigning OBJ_2 to OBJ_1 would not work since you would only replace the reference your local variable is pointing to.
To synchronize your local CoreData Entity which the data from the server you will need to modify the attributes of your existing entitiy. Depending on your data model and the format you are receiving OBJ_2 in there are different ways to achieve this.

Reactive Cocoa - Convert certain signal values into error or completed

Perhaps I'm still struggling on the reactive learning curve but I am having a hard time figuring out how to bridge a non reactive class with the rest of my reactive code. I am using a category to extend the non-reactive class.
The property is just an Enum representing the current state of a network action, states like New, Submitted, Processing and Completed. Right now I have written the following method in my category:
#implementation JRequestBase (RACExtensions)
- (RACSignal*) rac_RequestStateSignal
{
return RACAble(self, state);
}
#end
However, when state transitions from Processing -> Completed or from any state to Errored I want this signal to send Completed or Error instead of Next Value. How can I accomplish this in a category? I want to do something like:
#implementation JRequestBase (RACExtensions)
- (RACSignal*) rac_RequestStateSignal
{
return [RACAble(self, state) map:^(NSNumber *state){
if ([state intValue] == iRequestStateComplete)
{
# SEND COMPLETE
}
else if ([state intValue] == iRequestStateErrored)
{
# SEND ERROR
}
else
{
return state;
}
}];
}
#end
edit: I took a look at the GHAPIDemo and have come up with the following:
- (RACSignal*) rac_RequestSignal
{
RACSubject *subject = [[RACReplaySubject alloc] init];
[[RACAble(self, state) subscribeNext:^(NSNumber* s){
if ( [s intValue] == JRequestStateCompleted)
{
[subject sendNext:self];
[subject sendCompleted];
}
else if ([s intValue] == JRequestStateErrored)
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
// .. Set up dict with necessary values.
NSError *error = [NSError errorWithDomain:#"blah" code:1 userInfo:dict];
[subject sendError:error];
}
}];
return subject;
}
I'm not 100% sure this is the right way but it seems to be working.
Whenever you want to map values → signal events, instead of values → values, you should use -flattenMap: to return a signal corresponding to each input value. Then, as the "flatten" in the name implies, they'll be combined into one resulting signal.
However, this case is a little different, because you want to terminate the signal as soon as you get the Complete value. We'll use -takeUntilBlock: to represent that part.
The resulting code looks something like this:
- (RACSignal*) rac_RequestStateSignal
{
return [[RACObserve(self, state)
takeUntilBlock:^ BOOL (NSNumber *state){
return [state intValue] == iRequestStateComplete;
}]
flattenMap:^(NSNumber *state){
if ([state intValue] == iRequestStateErrored)
{
// Create a meaningful NSError here if you can.
return [RACSignal error:nil];
}
else
{
return [RACSignal return:state];
}
}];
}
(I used RACObserve because ReactiveCocoa 2.0 is now the only supported version, but you can use RACAble until you're ready to upgrade.)
As a general rule, you should avoid using subjects when possible, since they make code more stateful and reduce laziness.

Method to determine id of sender

I have code that works but I want to make sure I am doing things properly and cleanly.
I have four versions of the same collection of views displayed on a screen. Each collection which will be used to control the volume and rate of four different sounds. The collections are linked with IBOutletCollection to 4 different NSArrays (soundView0, soundView1, soundView2, soundView3).
I used the following code to determine which volume slider is being adjusted:
-(IBAction)whichVolume:(UISlider *)sender
{
if ([soundView0 containsObject:sender]) {
soundIndex = 0;
}
else if (([soundView1 containsObject:sender]))
{
soundIndex = 1;
}
else if ([soundView2 containsObject:sender])
{
soundIndex = 2;
}
else if ([soundView3 containsObject:sender])
{
soundIndex = 3;
}
//send a message to set volume of sound at index soundIndex
NSLog(#"The soundIndex is %d", soundIndex);
NSLog(#"The volume is %f", [sender value]);
}
Did I get this right or is there a better way to accomplish this?
You can use tag property to set an numeric index on a control, and then simply use sender.tag in event callback.

RestKit: connecting up a relationship which isn't in the response

Suppose that in my data model, I have Pages, which have-many Comments.
I want to connect up a relationship in the model which goes from the Comment back to the Page it belongs to, but the Page object isn't nested in the response, nor is any primary key which could identify the parent Page present in the response.
At the time that I call loadObjectsAtResourcePath, all the Comments which are loaded should belong to a fixed, known Page object. One way I could hook up the relationship would be to do:
loader.onDidLoadObjects = ^(NSArray* objs) {
for (Comment* comment in objs) comment.page = self.page;
...
}
but I'm hoping there's a better way. Note that I can't use the connectRelationship family of methods, because there's no primary key in the response which could let me hook each Comment up to a Page.
You can use the delegate method - (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout id *)mappableData to inject extra parameters before the mapping step. It ensures the objects and relationships will be correctly saved by RestKit if you are using core data (note the solution you gave above does not save the relationship).
Alternatively, look at this answer where I showed how to override RKObjectLoader to retrieve the page information from the URL itself.
EDIT: Here is the category I mentioned in the comment:
.h
#import <RestKit/RestKit.h>
typedef void(^RKObjectLoaderWillMapDataBlock)(id* mappableData);
#interface RKObjectLoader (Extended)
#property (nonatomic, copy) RKObjectLoaderWillMapDataBlock onWillMapData;
#end
and the .m:
#import <objc/runtime.h>
NSString* kOnWillMapDataKey = #"onWillMapData";
#implementation RKObjectLoader (Extended)
- (RKObjectLoaderWillMapDataBlock) onWillMapData {
return objc_getAssociatedObject(self, &kOnWillMapDataKey);
}
- (void) setOnWillMapData:(RKObjectLoaderWillMapDataBlock) block {
objc_setAssociatedObject(self, &kOnWillMapDataKey, block, OBJC_ASSOCIATION_COPY);
}
- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject inContext:(RKObjectMappingProviderContext)context error:(NSError**)error {
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:self.response.MIMEType];
NSAssert1(parser, #"Cannot perform object load without a parser for MIME Type '%#'", self.response.MIMEType);
// Check that there is actually content in the response body for mapping. It is possible to get back a 200 response
// with the appropriate MIME Type with no content (such as for a successful PUT or DELETE). Make sure we don't generate an error
// in these cases
id bodyAsString = [self.response bodyAsString];
RKLogTrace(#"bodyAsString: %#", bodyAsString);
if (bodyAsString == nil || [[bodyAsString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) {
RKLogDebug(#"Mapping attempted on empty response body...");
if (self.targetObject) {
return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionaryWithObject:self.targetObject forKey:#""]];
}
return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionary]];
}
id parsedData = [parser objectFromString:bodyAsString error:error];
if (parsedData == nil && error) {
return nil;
}
// Allow the delegate to manipulate the data
if ([self.delegate respondsToSelector:#selector(objectLoader:willMapData:)]) {
parsedData = [parsedData mutableCopy];
[(NSObject<RKObjectLoaderDelegate>*)self.delegate objectLoader:self willMapData:&parsedData];
}
if( self.onWillMapData ) {
parsedData = [parsedData mutableCopy];
self.onWillMapData(&parsedData);
}
RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider];
mapper.targetObject = targetObject;
mapper.delegate = (id<RKObjectMapperDelegate>)self;
mapper.context = context;
RKObjectMappingResult* result = [mapper performMapping];
// Log any mapping errors
if (mapper.errorCount > 0) {
RKLogError(#"Encountered errors during mapping: %#", [[mapper.errors valueForKey:#"localizedDescription"] componentsJoinedByString:#", "]);
}
// The object mapper will return a nil result if mapping failed
if (nil == result) {
// TODO: Construct a composite error that wraps up all the other errors. Should probably make it performMapping:&error when we have this?
if (error) *error = [mapper.errors lastObject];
return nil;
}
return result;
}
#end

best way to load/save disparate UITableView data for cellForRowAtIndexPath?

I have a multi-sectioin UITableView with different kinds of controls throughout various rows (multi-select checkboxes, single-select checkboxes, text inputs, text areas etc.). Each row could have a different data type (string, integer, date etc) and the number of rows and location are dynamic so you can't always depend on section X row Y being a certain control.
My question is what is the best way to save the data input into these fields for use in the view, grabbing the right data to show what was entered into that field when calling cellForRowAtIndexPath.
Note that I am NOT asking how to save this data persistently, I'm using CoreData for that, the question is just how to temporarily save the data while interacting with the view, so that you have it in an NSMutableArray or NSMutableDictionary ready to be saved with CoreData when the user touches the "Save" button, or completely discarded if they press "Cancel".
Currently I'm trying to implement a dictionary but it seems somewhat kludgy and I often get one row's data showing up in another row.
Here is my current method for saving the form data. It's using a name from the arguments along with a counter variable used for the view as a whole. The counter variable is also used as the tag integer for the control.
-(id)documentField:(UIView *)view withKey:(NSString *)key andValue:(id)value{
NSInteger foundTag = -1;
NSLog(#"searching dictionary for key: %#", key);
for(NSString *existingKey in fieldValues){
NSArray *keyParts = [existingKey componentsSeparatedByString:#"~"];
if( [[keyParts objectAtIndex:0] isEqualToString:key] )
{
foundTag = [[keyParts objectAtIndex:1] intValue];
NSLog(#"found key: %#, it's tag is: %d", [keyParts objectAtIndex:0], foundTag);
break;
}//end if
else{
//NSLog(#"no match: %# != %#", (NSString *)[keyParts objectAtIndex:0], key);
}
}//end for
//if we haven't tagged this element yet
//set the tag
if (foundTag == -1) {
view.tag = fieldValueCounter;
foundTag = fieldValueCounter;
fieldValueCounter++;
}//end if
NSString *fieldKey = [NSString stringWithFormat:#"%#~%d", key, foundTag];
if( ! [fieldValues objectForKey:fieldKey] ){
[fieldValues setObject:((value)? value : #"") forKey:fieldKey];
}
NSLog(#"returning fieldValue: %# = %#", fieldKey, [fieldValues objectForKey:fieldKey]);
return [fieldValues objectForKey:fieldKey];
}//end documentField:withKey:andValue:
And here is how it is being used.
((UTVCellTextField *)cell).textLabel.text = #"Door Location:";
((UTVCellTextField *)cell).textField.text = [self documentField:((UTVCellTextField *)cell).textField withKey:#"door.door_location" andValue:door.door_location];
((UTVCellTextField *)cell).textField.delegate = self;