Cannot access more than one value from function using PromiseKit Swift - ios

TemplateClass.m
+ (AnyPromise *) promisefunctionReturnThreeValus:(NSString *)sampleName {
return [self anotherPromiseFunction:sampleName].then(^(NSMutableDictionary *sampleDict) {
DataArray *data = [DataArray dataArrayFromDict:sampleDict];
PropertyArray *property = [PropertyArray PropertyArrayFromDict:sampleDict];
if ([sampleDict objectForKey:NAME])
{
NameModel *name = [[NameModel alloc]initWithDictionary:[responseDict objectForKey:NAME]];
return (PMKManifold(data,property,name));
}
else
{
return (PMKManifold(data,property,nil));
}
});
}
well i can able to access this from objc using the below code
[TemplateClass promisefunctionReturnThreeValus:#"hello"].then(^(DataArray *data,PropertyArray *property,NameModel *name) {
//Here i can able to access the three values data,property and name
}
But when i try to access this from swift
TemplateClass.promisefunctionReturnThreeValus(sampleName: "hello").then{ data,property,name in
// it show me error " Contextual closure type '(Any?) -> AnyPromise' expects 1 argument, but 3 were used in closure body "
}
i can able to access only data but not the other two
i also tried debug it and print through log it show only the data of DataArray Object
lldb output
<DataArray : 0x1c0631340 count:1 value:{
"id" = 3631;
}
>

Related

swift: EXC_BAD_ACCESS with using Realm and FolioReaderKit

I installed FolioReaderKit using Cocoapods to read epub book. I'm trying to highlight text and add it to a list of highlights. But when I do this I get this error and my app crashes:
Thread 1: EXC_BAD_ACCESS (code=257, address=0x41c4a1c96ae55d46)
line:
value = RLMGetOptional(static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)));
in RLMAccessor.mm:
id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
__unsafe_unretained RLMProperty *const prop) {
// Property value from an NSArray
if ([obj respondsToSelector:#selector(objectAtIndex:)]) {
return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
}
// Property value from an NSDictionary
if ([obj respondsToSelector:#selector(objectForKey:)]) {
return [obj objectForKey:prop.name];
}
// Property value from an instance of this object type
id value;
if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
if (prop.array) {
return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
}
else if (prop.swiftIvar == RLMDummySwiftIvar) {
// FIXME: An invalid property which we're pretending is nil until 4.0
// https://github.com/realm/realm-cocoa/issues/5784
return NSNull.null;
}
else { // optional
value = RLMGetOptional(static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)));
}
}
else {
// Property value from some object that's KVC-compatible
value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
_info.rlmObjectSchema.className);
}
return value ?: NSNull.null;
}
How to fix it?

CloudKit CKError extension not available in Objective-C?

I read somewhere here that CKError is not available in Objective-C, and I concur. For instance, this extension is available in Swift.
#available(OSX 10.10, iOS 8.0, watchOS 3.0, *)
extension CKError {
/// Retrieve partial error results associated by item ID.
public var partialErrorsByItemID: [AnyHashable : Error]? { get }
/// The original CKRecord object that you used as the basis for
/// making your changes.
public var ancestorRecord: CKRecord? { get }
/// The CKRecord object that was found on the server. Use this
/// record as the basis for merging your changes.
public var serverRecord: CKRecord? { get }
/// The CKRecord object that you tried to save. This record is based
/// on the record in the CKRecordChangedErrorAncestorRecordKey key
/// but contains the additional changes you made.
public var clientRecord: CKRecord? { get }
/// The number of seconds after which you may retry a request. This
/// key may be included in an error of type
/// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
public var retryAfterSeconds: Double? { get }
}
The problem is that I need these objects in my Objective-C project.
I've somehow (I believe) managed to get the partialErrorsByItemID in Objective-C by making a category for NSError and a little comprehension of the documentation of CKError.h, like so:
CKErrorCode ckErrorCode = (CKErrorCode) _code;
if (ckErrorCode == CKErrorPartialFailure) {
// When a CKErrorPartialFailure happens this key will be set in the error's userInfo dictionary.
// The value of this key will be a dictionary, and the values will be errors for individual items with the keys being the item IDs that failed.
NSDictionary *dicError = _userInfo;
if ([dicError objectForKey:CKPartialErrorsByItemIDKey] != nil) {
NSDictionary *dic = (NSDictionary *)[dicError objectForKey:CKPartialErrorsByItemIDKey];
for (NSString* key in dic) {
NSError *newError = dic[key];
if (code == newError.code) {
match = YES;
}
}
} else {
return NO;
}
}
But again, my problem is how to get the objects serverRecord and the clientRecord. Any idea?
Here's an Objective-C category that replicates most of the CKError structure of Swift. I didn't add errorCode, localizedDescription or errorUserInfo since NSError already provides those as code, localizedDescription, and userInfo.
CloudKitExtensions.h
#import <CloudKit/CloudKit.h>
NS_ASSUME_NONNULL_BEGIN
extern const double UnknownRetrySeconds;
#interface NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID;
- (CKRecord * _Nullable)ancestorRecord;
- (CKRecord * _Nullable)clientRecord;
- (CKRecord * _Nullable)serverRecord;
- (double)retryAfterSeconds; // returns UnknownRetrySeconds if not available
#end
NS_ASSUME_NONNULL_END
CloudKitExtensions.m
#import "CloudKitExtensions.h"
const double UnknownRetrySeconds = -1;
#implementation NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure) {
return self.userInfo[CKPartialErrorsByItemIDKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)ancestorRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorAncestorRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)clientRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorClientRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)serverRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorServerRecordKey];
} else {
return nil;
}
}
- (double)retryAfterSeconds {
if ([self.domain isEqualToString:CKErrorDomain]) {
NSNumber *delayVal = self.userInfo[CKErrorRetryAfterKey];
return delayVal ? [delayVal doubleValue] : UnknownRetrySeconds;
} else {
return UnknownRetrySeconds;
}
}
#end

Prevent a closure from running until another has completed

Here is code for two closures in two different IBAction button presses. The desired outcome is for the button press to turn on/off an LED, then to access a light sensor and read the light value after the change in LED status.
What happens is a race condition where the function getVariable runs and returns before the callFunction has implemented the change. The result is that the value displayed in getLightLabel.text is that of the prior condition, not the current condition.
My question is how to rewrite the code below so that myPhoton!.getVariable does not execute until after the myPhoton!.callFunction has returned (completed its task).
I have tried placing getVariable inside callFunction, both before and after the } closing if (error == nil), but the result was identical to the code shown here.
#IBAction func lightOn(sender: AnyObject) {
let funcArgs = [1]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is on"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
#IBAction func lightOff(sender: AnyObject) {
let funcArgs = [0]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is off"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
Here is the callFunction comments and code from the .h file. This SDK is written in Objective C. I am using it in Swift with a bridging header file.
/**
* Call a function on the device
*
* #param functionName Function name
* #param args Array of arguments to pass to the function on the device. Arguments will be converted to string maximum length 63 chars.
* #param completion Completion block will be called when function was invoked on device. First argument of block is the integer return value of the function, second is NSError object in case of an error invoking the function
*/
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion;
/*
-(void)addEventHandler:(NSString *)eventName handler:(void(^)(void))handler;
-(void)removeEventHandler:(NSString *)eventName;
*/
Here is the .m file code
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion
{
// TODO: check function name exists in list
NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:#"v1/devices/%#/%#", self.id, functionName]];
NSMutableDictionary *params = [NSMutableDictionary new]; //[self defaultParams];
// TODO: check response of calling a non existant function
if (args) {
NSMutableArray *argsStr = [[NSMutableArray alloc] initWithCapacity:args.count];
for (id arg in args)
{
[argsStr addObject:[arg description]];
}
NSString *argsValue = [argsStr componentsJoinedByString:#","];
if (argsValue.length > MAX_SPARK_FUNCTION_ARG_LENGTH)
{
// TODO: arrange user error/codes in a list
NSError *err = [self makeErrorWithDescription:[NSString stringWithFormat:#"Maximum argument length cannot exceed %d",MAX_SPARK_FUNCTION_ARG_LENGTH] code:1000];
if (completion)
completion(nil,err);
return;
}
params[#"args"] = argsValue;
}
[self setAuthHeaderWithAccessToken];
[self.manager POST:[url description] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion)
{
NSDictionary *responseDict = responseObject;
if ([responseDict[#"connected"] boolValue]==NO)
{
NSError *err = [self makeErrorWithDescription:#"Device is not connected" code:1001];
completion(nil,err);
}
else
{
// check
NSNumber *result = responseDict[#"return_value"];
completion(result,nil);
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
if (completion)
completion(nil,error);
}];
}
One solution is to put the second closure inside the first, where the first returns and provides and Error value. If no error,then execuet the second closure. That is one way to tightly couple the two closures without resorting to semaphores or other messaging schemes.
In this application, the problem I was encountering cannot be solved on the IOS/Swift side of the stack. The cloud API and embedded uP are not tightly coupled, so the cloud returns to the IOS with a completion before the full function code has run on the Particle uP.
The solution to this overall problem actually lies in either modifying the cloud API or adding some additional code to the uP firmware to tightly couple the process to the IOS app with additional communication.

Type NSArray cannot be downcast implicitly

I'm working on converting my app over to swift but I'm running into a swift error I don't quite understand yet.
In Obj-c I have a for in loop here:
for (MYVisitor *visitor in visitors) {
if (visitor.boxState == MYVisitorBoxStateChatting) {
[self.chattingVisitors addObject:visitor];
}
else {
[self.unknownVisitors addObject:visitor];
}
}
In swift I have:
for visitor: MYVisitor in visitors {
if visitor.boxState == MYVisitorBoxStateChatting {
self.chattingVisitors.addObject(visitor)
}
else {
self.unknownVisitors.addObject(visitor)
}
}
I'm getting an error in swift that says: NSArray cannot be implicitly downcast to MYVisitor, did you mean to use as to force downcast?
Xcode recommends this change: for visitor: FHVisitor in visitors as MYVisitor {...
which then gives me this error: MYVisitor does not conform to protocol SequenceType
If visitors is an NSArray and it is guaranteed that all its elements are of
the MYVisitor type then you can cast the array to [MYVisitor]:
for visitor in visitors as [MYVisitor] {
// ...
}
Note that this will crash at runtime if any array element is not a MYVisitor.
Alternatively, you can check the array with
if let theVisitors = visitors as? [MYVisitor] {
for visitor in theVisitors {
println(visitor.name)
}
} else {
// This is not an array of MYVisitor objects.
}
Or you can check each array element:
for item in visitors {
if let visitor = item as? MYVisitor {
// ...
} else {
// This is not a MYVisitor.
}
}

iOS5 - How to parse JSON response from Facebook [duplicate]

I am using Facebook Graph API...for fetching the data of news feed of the facebook profile..
and here is the response that i am getting in the console
{
application = {
id = 2309869772;
name = Links;
};
"created_time" = "2011-02-10T09:44:27+0000";
from = {
id = 1845195019;
name = "Paritosh Raval";
};
icon = "http://static.ak.fbcdn.net/rsrc.php/v1/yD/r/aS8ecmYRys0.gif";
id = "1845195019_192144087475935";
likes = {
count = 1;
data = (
{
id = 1845195019;
name = "Paritosh Raval";
}
);
};
link = "http://www.facebook.com/AMDAVAD";
name = "once you live in AHMEDABAD u cannot live anywhere else in the world..";
picture = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/203562_115963658443669_4129246_n.jpg";
properties = (
{
name = Page;
text = "21,803 people like this.";
}
);
type = link;
"updated_time" = "2011-02-10T09:44:27+0000";
},
{
application = {
id = 2392950137;
name = Video;
};
"created_time" = "2011-02-02T04:18:22+0000";
description = "must watch and explore :))";
from = {
id = 1845195019;
name = "Paritosh Raval";
};
icon = "http://static.ak.fbcdn.net/rsrc.php/v1/yD/r/aS8ecmYRys0.gif";
id = "1845195019_194836027209359";
likes = {
count = 1;
data = (
{
id = 100000701228096;
name = "Bhargav Jani";
}
);
};
link = "http://www.facebook.com/video/video.php?v=152586058110610&comments";
name = "It Happens Only in....";
"object_id" = 152586058110610;
picture = "http://vthumb.ak.fbcdn.net/hvthumb-ak-snc4/50893_152586468110569_152586058110610_18299_1832_t.jpg";
properties = (
{
name = Length;
text = "0:54";
}
);
source = "http://video.ak.fbcdn.net/cfs-ak-ash2/70137/56/152586058110610_53804.mp4?oh=481e53b824f6db8e3195fc9c0d07571d&oe=4DAFC300&__gda__=1303364352_7670272db65e93ec75dcaaed16b6d805";
type = video;
"updated_time" = "2011-02-02T04:18:22+0000";
}
And I want to show every data in the organized structure in the console. Can anyone help me with this?
it's unclear what you exactly asking but I try to answer.
First of all you need to parse this response in the method
- (void)request:(FBRequest *)request didLoad:(id)result of Facebook iOS SDK
result can be a string, a NSArray if you have multiple results and NSDictionary
In you console output we can see NSDictionary with included arrays and dictionaries in it.
I have little tutorial about it but it's on russian only and site is down today :( so i just copy one example from my article.
Let say we want to know what facebook user Likes
- (IBAction)getUserInfo:(id)sender {
[_facebook requestWithGraphPath:#"me/likes" andDelegate:self];
}
if we try this Graph API response in browser or output to console we can see what this request returns. It returns dictionary with one and only key - "data" and corresponded array to this key. This array contents dictionary objects again with keys -
«name»,"category","id","created_time". Dont forget request «user_likes» permission before.
So we have parsing method like that:
- (void)request:(FBRequest *)request didLoad:(id)result {
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
if ([result objectForKey:#"owner"]) {
[self.label setText:#"Photo upload Success"];
} else if ([result objectForKey:#"data"]){
NSArray *likes = [result objectForKey:#"data"];
NSString *text=#"You don't like Steve";
for (NSDictionary* mylike in likes) {
NSString *mylikeName = [mylike objectForKey:#"name"];
if ([mylikeName isEqualToString:#"Steve Jobs"]) {
text=#"You like Steve";
break;
}
}
[self.label setText:text];
}
};
You can parse you result same way and fill your object's variables and then use it to display information in TableView for example. good luck!

Resources