RACSignal map with error - ios

I have a simple question but I can't find a nice solution to it.
I have a signal that sends strings, and a map after it. The map converts the strings into JSON.
It may happen that the string has a wrong format and the JSON parser will fail to parse with an error.
[stringGeneratorSignal map:^(NSString *bussinessObjectString){
NSError *error;
BussinessObject *obj = [[BussinessObject alloc] initWithString:bussinessObjectString error:&error];
if (error) {
NSLog(#"%#", error);
}
return obj;
}];
But because I'm inside the map I can't return an error signal. What I would like is to get an error with the error provided by the parser.
A couple of possibilities I've analyzed that I don't like:
Return the error in the map and then have a wrapper signal that actually convert the data (or error) into an error signal. The problem is that I'm delegating the same problem (converting the data into an error signal).
Use flattenMap instead. This will allow to return an error message, but the problem is that it's not the same behaviour as map.
What is the best approach for this kind of scenarios?
Thanks!

Look at -tryMap. It allows you to return data or nil and then set the error
/// Runs `mapBlock` against each of the receiver's values, mapping values until
/// `mapBlock` returns nil, or the receiver completes.
///
/// mapBlock - An action to map each of the receiver's values. The block should
/// return a non-nil value to indicate that the action was successful.
/// This block must not be nil.
///
/// Example:
///
/// // The returned signal will send an error if data cannot be read from
/// // `fileURL`.
/// [signal tryMap:^(NSURL *fileURL, NSError **errorPtr) {
/// return [NSData dataWithContentsOfURL:fileURL options:0 error:errorPtr];
/// }];
///
/// Returns a signal which transforms all the values of the receiver. If
/// `mapBlock` returns nil for any value, the returned signal will error using
/// the `NSError` passed out from the block.
- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock;

Related

Synchronous Save in Parse using Swift

Using Swift 2.1 (Xcode 7.2.) and Parse 1.12.0, I'm getting an error that PFObject.save() is unavailable in Swift. My code is:
let operation = NSBlockOperation { () -> Void in
do {
let success = try rating.save()
}
catch let er as NSError {
error = er
}
}
In PFObject+Synchronous.h, there is this:
///--------------------------------------
#pragma mark - Saving Objects
///--------------------------------------
/**
*Synchronously* saves the `PFObject`.
#return Returns whether the save succeeded.
*/
- (BOOL)save PF_SWIFT_UNAVAILABLE;
/**
*Synchronously* saves the `PFObject` and sets an error if it occurs.
#param error Pointer to an `NSError` that will be set if necessary.
#return Returns whether the save succeeded.
*/
- (BOOL)save:(NSError **)error;
So it seems as if Xcode can't tell which function to use: it should try to use the one that handles the error. Is there a way of forcing this, or am I calling the function incorrectly?
Although the function not marked as unavailable to swift:
-(BOOL)save:(NSError **)error
is defined to return a bool, the Swift implementation (which throws) apparently does not, so the code compiles fine if I'm not expecting to receive a return value, i.e.:
let operation = NSBlockOperation { () -> Void in
do {
try rating.save()
}
catch let er as NSError {
error = er
}
}
I'm still now sure how I could have determined this without trial and error.
The first overload is marked unavailable to Swift, hence it is not visible.
The second overload is made available, but as you discovered yourself, it requires a try since it returns a NSError output parameter. The BOOL return value in Cocoa is there to indicate whether the operation was successful or not. In Swift, this is handled by catching the NSError instead. This behaviour was introduced in (I think) Swift 2.0, and is documented here.
To summarize, an Obj-C method
- (BOOL) doSomething:(NSError**)error {}
maps to the following Swift method
func doSomething() throws

How to cast from AnyObject to the dynamic type?

I have a scenario where I've to cast an AnyObject to a dynamic type (available only at runtime). Is this possible with Swift? If yes, how would I do it?
To explain the scenario further, I have a function which has a completion block that returns the response or error from a service call. In the block, the success object is in fact an Array of AnyObject where the dynamicType of the object is that of the calling class.
someObject.callService(classObject, withCompletionBlock: {(objectOrNil : [AnyObject]!, errorOrNil:NSError?) -> Void in
if(errorOrNil != nil) { self.delegate.didFailWithError(errorOrNil)
}
else {
// Here objectOrNil is during runTime an Array of class X
// X.response gives the response JSON
// In a particular scenario, I need to intercept the response before the delegate
self.delegate.didReceiveResponse(objectOrNil)
}
}, withProgressBlock: nil)
In the block objectOrNil is an array of a class X ([X]) where the X would be class invoking the service call. All the classes that call the method has a response dictionary object which is populated with the response after the service call is completed and the delegate invoked.
To handle a particular scenario, I need to intercept the service response (X.response) where X is objectOrNil[0] from within this block and do additional processing.
I'm able to see the class name when I do a
po objectOrNil[0].dynamicType
in the console, but am unable to cast the objectOrNil[0] to the right type X to fetch X.response. When I tried
po objectOrNil[0].response
I'm getting an error that response is ambiguous with NSURLResponse response. But
po objectOrNil[0].response as NSDictionary
returns the right JSON response in console during runtime (with breakpoint).
However the below code gives a compile time error that AnyObject does not have response attribute.
let responseDict = objectOrNil[0].response as NSDictionary
Can anyone guide me on how to cast to the dynamicType type and fetch the response dictionary for me to proceed?
Thanks
You could use a protocol
protocol Respondable {
var response : NSDictionary {get}
}
and then constrain the type of objectOrNil to that protocol
someObject.callService(classObject, withCompletionBlock: {(objectOrNil : [Respondable]!, errorOrNil:NSError?) -> Void

Object from JSON with Mantle: ignore a property

My app creates some ObJC objects from JSON. Things were working fine, until I added a new property to my ObjC model class, which doesn't have a counterpart in JSON.
I've configured the mapping as follows:
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"firstName" : #"firstname",
#"middleName" : #"middlename",
#"lastName" : #"lastname",
// etc.
#"phoneNumber" : NSNull.null // no JSON data for this one
};
}
However I get an assertion failure in Mantle, in MTLJSONAdapter initWithModelClass: "phoneNumber must either map to a JSON key path or a JSON array of key paths, got: null."
This is how I create the model object:
MyData *myData = [MTLJSONAdapter modelOfClass:[MyData class]
fromJSONDictionary:json error:&error];
How can I have the phoneNumber property in the data class without it mapping to a JSON value?
Just don't specify its mapping to + JSONKeyPathsByPropertyKey.
The accepted answer didn't end up working for me (I wasn't parsing from a JSON object) and I found subclassing the encodingBehaviorsByPropertyKey to work. The comments in the header file read as follows:
/// Determines how the +propertyKeys of the class are encoded into an archive.
/// The values of this dictionary should be boxed MTLModelEncodingBehavior
/// values.
///
/// Any keys not present in the dictionary will be excluded from the archive.
///
/// Subclasses overriding this method should combine their values with those of
/// `super`.
///
/// Returns a dictionary mapping the receiver's +propertyKeys to default encoding
/// behaviors. If a property is an object with `weak` semantics, the default
/// behavior is MTLModelEncodingBehaviorConditional; otherwise, the default is
/// MTLModelEncodingBehaviorUnconditional.
+ (NSDictionary *)encodingBehaviorsByPropertyKey;
Overriding that method from within my MTLModel subclass like so ended up working for me:
+ (NSDictionary *)encodingBehaviorsByPropertyKey {
NSMutableDictionary *encodingBehaviorsDictionary = [[super encodingBehaviorsByPropertyKey] mutableCopy];
[encodingBehaviorsDictionary removeObjectForKey:#"propertyToBeOmitted"];
return encodingBehaviorsDictionary;
}
FYI: I'm using Mantle version 2.1.

How does filter method work?

-(instancetype)filter:(BOOL (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^ id (id value) {
if (block(value)) {
return [class return:value];
} else {
return class.empty;
}
}] setNameWithFormat:#"[%#] -filter:", self.name];
}
This is the implementation of filter of ReactiveCocoa.I don't know what this code means.Also I can't get any reference to the second return method.
return [class return:value];
Also, what does this instancetype mean? Suppose the value is a string and I check whether its length is greater than 2. What will be returned by using filter method?
The filter method invokes the class method of the current class to get a RACStream subclass using that method. Using return: will give a signal that sends the value passed and then completes. Using empty gives a signal that immediately sends completed without sending a next value, which removes the value filtered value from the stream thanks to flattenMap: switching out the signal with the one being created.

Does Mantle require a Model to be fully specified?

I am consuming JSON that is constantly evolving. I started writing code to consume the JSON using Mantle lately. It seemed like a very good choice for what I want to do. However, it seems that if the JSON being consumed has properties that do not exist in the model, the JSON transformation fails. I'm using the [MTLJSONAdapter modelOfClass:fromJSONDictionary:error:]; call to map the JSON.
Thanks in advance,
Upon closer inspection of the code, Mantle does require that all json properties map to something in the model. Otherwise, what will happen is an exception will be thrown for that property.
inside of MTLValidateAndSetValue of MTLModel, it doesn't check for the existence of the property before it sets it.
#try {
if (![obj validateValue:&validatedValue forKey:key error:error]) return NO;
if (forceUpdate || value != validatedValue) {
[obj setValue:validatedValue forKey:key];
}
return YES;
} #catch (NSException *ex) {
NSLog(#"*** Caught exception setting key \"%#\" : %#", key, ex);
// Fail fast in Debug builds.
#if DEBUG
#throw ex;
#else
if (error != NULL) {
*error = [NSError mtl_modelErrorWithException:ex];
}
return NO;
#endif
}
This is problematic if the JSON you are consuming isn't guaranteed to match your model.
I ended up doing a custom JSON representation for my work having the constructor build the object based on the incoming JSON rather than against the model. It will first iterate over the json properties and tries to map them directly to the model properties using implicit mapping. If there are any properties that require special handling, it is up to the subclass to override the init call and apply the transformation manually.

Resources