Calling Objective-c function in swift - ios

Im trying to implement CometChat in my swift application. I managed to import the Objective-c framework successfully via a bridging header. But now I'm stuck trying to call Objective-C methods from swift.
This is the method from the interface i want to call:
- (void)loginWithURL:(NSString *)siteURL
username:(NSString *)username
password:(NSString *)password
success:(void(^)(NSDictionary *response))success
failure:(void(^)(NSError *error))failure;
And this is how the method is called from Objective-C:
[cometChat loginWithURL:#"localhost/cometchat/" username:usernameTextField.text password:passwordTextField.text success:^(NSDictionary *response) {
NSLog(#"SDK log : Username/Password Login Success %#",response);
[self handleLogin];
} failure:^(NSError *error) {
NSLog(#"SDK log : Username/Password Login Error%#",error);
[self handleLoginError:#[#0,error]];
}];
So far i have this:
cometChat.loginWithURL("localhost/cometchat/", username: EmailField.text, password: PasswordField.text){
(success: [NSDictionary], failure:NSError) in {
println("did i make it here?")
}
}
The problem is, it says that there is missing argument "success", but its a mystery to me how it can be an argument, when it clearly returns the response. I want to know how to put together this method call. I also used the objectivec2swift converter, but it wasn't any help. Also, i have no clue what the # means before the #[#0,error]
I know its a beginners question, but i already wasted a whole day on this, since i couldn't find any tutorials on how to call such "complex" Obj-C methods from swift.

Try this :-
cometChat.loginWithURL("localhost/cometchat/", username: "abc", password: "123", success: { (response) -> Void in
print("SDK log : Username/Password Login Success \(response)")
}) { ( error) -> Void in
print("SDK log : Username/Password Login Error \(error)")
}

When you look at the Objective-C signature, you see that the method takes two closures: success is a void function that takes a dictionary, and failure is a void function that takes an error.
In your Swift code you have only one closure: a void function that takes a dictionary and an error.
You either need to change the Objective-C method to take just one closure, or change the Swift code to provide two closures.

When you call a function, and the last parameter is a block / closure, then you can write the last parameter after the function call in { }. That applies to the last block only.
Anyway, you are trying to pass a closure with two parameters success and failure. You need to pass two closures, one as the success parameter of your function, with a parameter response, and one either as the failure parameter of your function, or following the function, with a parameter error.

Related

Crash on server Socket.io callback, using swift client library

I've this swift code:
[[self.socket emitWithAck:#"setup_request" with:#[]] timingOutAfter:0 callback:^(NSArray* data) {
NSLog(#"%#", data);
}];
paired with this server function:
client.on('setup_request', function(data, callback) {
callback({ success:true});
});
but when executing it, the server crashes with this message:
callback({ success:true});
^
TypeError: callback is not a function
I'm using server version 2.0.4, and the 13.1.0 Swift client library.
I can't see what I'm doing wrong, and how to fix this error. Thanks for your help.
So i guess there exist some kind of bridging in your project as clearly the first block is Objective C code, and your code is in swift.
Also callback:^(NSArray* data), means the callback expects an NSArray to be passed, but in your code you are passing a closure.
Pass an NSArray instead : callback(["success"] as? NSArray)
Found the issue, the server method interface has changed in version 2.0, now it's like:
client.on('setup_request', (callback) => {
callback({success:true});
});

Alternatives to lots of nested completion blocks?

I'm using OAuthSwift to access the goodreads api. First I have an api call to get the user id. Then, using that id, I get the books the user has in a shelf. Then I check if there are any books with a certain title, and if there aren't, I call the api again to create that book. And then, being sure the book is there, I post a status update on its progress.
This is the code to get the user id:
oAuth.client.get(
"https://www.goodreads.com/api/auth_user",
success: {
data, response in
// Here I use the returned ID to get the books.
},
failure: {
error in
//…
}
)
Once I got the ID, I need to use it for the next api call, which gets the books. The thing is, I can't just add the next call after that first one, because they're asynchronous, so the second call would start before I got the ID from the first one.
What I'm doing, then, is to add the second api call inside that success closure (I pointed it out in the code with a comment). So I have two very similar pieces of code one inside the other; when the first api call succeeds it calls the next. But then I have to use the result of that second call to decide if I'll add a new book or not. So I have a third api call (to add the book) inside the success block of the second call, which is inside the success block of the first call… and you can see things are starting to get pretty confusing. And it doesn't even stop there: I have a fourth api call to post a status update, and it's inside the completion handler of the third call, which is inside that of the second, which is inside that of the first.
So… what should I do instead of that? I'm sorry if it's a silly question, but I'm new at swift and at programming overall.
I think Apple has provided best way of handling this scenario using GCD, GCD provides serial queue to execute blocks/API in serial manner. That is in FIFO Order
let serialQueue : dispatch_queue_t = dispatch_queue_create("com.yourSerialQueue.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue) { () -> Void in
//Call First API
print("First API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Second API
print("Second API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Third API
print("Third API call succeed")
};
dispatch_async(serialQueue) { () -> Void in
//Call Fourth API
print("Fourth API call succeed")
};
OutPut:
First API call succeed
Second API call succeed
Third API call succeed
Fourth API call succeed
I do not have deep knowledge of GCD yet, for your scenario what i can suggest you in real quick is call each API with completion block as below, (this is what i can help you as of now)
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
[self callGetUserIdAPIAndCompletion:^(NSDictionary *dictResponse) {
[self callGetBooksAPIForUserId:dictResponse[#"userId"] AndCompletion:^(NSDictionary *dictResponse) {
[self callAddBookAPIForUserId:dictResponse[#"userId"] bookId:dictResponse[#"bookId"] AndCompletion:^(NSDictionary *dictResponse) {
[self callPostStatusUpdateAPIForUserId:dictResponse[#"userId"] status:#"status" AndCompletion:^(NSDictionary *dictResponse) {
}];
}];
}];
}];
});
And write your API calls functions as below
-(void)callGetUserIdAPIAndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call First API
NSLog(#"API 1 completes");
completion(dictResponse);
}
-(void)callGetBooksAPIForUserId:(NSString*)userId AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Second API with userId
NSLog(#"API 2 completes");
completion(dictResponse);
}
-(void)callAddBookAPIForUserId:(NSString*)userId bookId:(NSString*)bookId AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Third API with userId and bookId
NSLog(#"API 3 completes");
completion(dictResponse);
}
-(void)callPostStatusUpdateAPIForUserId:(NSString*) userId status:(NSString*) status AndCompletion:(void(^)(NSDictionary *dictResponse))completion {
NSDictionary *dictResponse;
//Call Fourth API with userId and status
NSLog(#"API 4 completes");
completion(dictResponse);
}

Parse PFCloud.callInBackground completion block never called i.e no response received

I have defined a function in the Parse Cloud Code called "relatedWords". When I try call this function in my iOS app, the completion block/closure is never called i.e no response is received.
I've tested the function in the Parse API Console and it is working fine there, so I know it's not an issue with the cloud code.
Any ideas on what the issue is?
My swift code:
func fetchRelatedKeyWordsForWord(word: String)
{
PFCloud.callFunctionInBackground("relatedWords", withParameters: ["hashtag": word]) { (response, error) -> Void in
//This is never called
print(response)
print(error)
}
}
Snippet of the cloud code:
Parse.Cloud.define("relatedWords", function(request, response) {
Parse.Cloud.useMasterKey();
var hashtag = request.params.hashtag;
...
...
//Run a query
var query = new Parse.Query(parseClassName);
query.find({
success: function(results) {
if (results.length != 0) {
console.log("Found Objects! Returning Objects");
response.success(results);
return;
}
Edit:
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem. It was silly mistake by me. The reason the cloud code was not getting called is that I had not setup parse in my ApplicationDidFinishLaunching i.e I did not call Parse.setApplicationId("...", clientKey: "...")
I figured out the problem.
you can use other server, other vise pay money on parse and solve the problem.

Converting Obj C to Swift Error:type of expression is ambiguous without more context

I've been using an objC framework in my swift project so I'm converting an objC method to swift. The objC method is as follows:
[[MySingleton sharedInstance] provisionUserWithPIN:pin
organizationID:kOrgID
accessToken:kOrgToken
completion:^(NSString *UID, User *user) {
}];
My swift conversion is:
MySingleton.sharedInstance().provisionUserWithPIN(pin, organizationID: kOrgID, accessToken: kOrgToken) {(UID: String, user: User) in
}
I'm getting an Error:type of expression is ambiguous without more context pointed after the ( in provisionUserWithPIN.
I don't know what I'm doing wrong. Been stuck with this for hours.
Declaration:
- (void)provisionUserWithPIN:(NSString *)PIN organizationID:(NSString *)organizationID accessToken:(NSString *)accessToken completion:(void (^)(NSString *UID, User *user))completion;
You should note that a parameter declared like this in Objective-C
(NSString *)something
does not becomes this in Swift
something:String
but it actually is translated into this:
something:String!
So you can invoke your Objective-C method in Swift writing
MySingleton.sharedInstance().provisionUserWithPIN(
"pin",
organizationID: "orgID",
accessToken: "accessTken") { (UID: String!, user: User!) -> () in
}
Hope this helps.

Objective-C completion block in Swift

I would like to call a completion block in Swift as I have always done in Objective-C. I took a look at closures, but for some reason, I cannot get it to work...
Obj-C:
- (void)someMethodWithParam1:(NSString *)param completion:(void(^)(NSArray* arr, NSURLResponse *resp))callback
{
callback(arr, resp);
}
Swift:
func someMethodWithParam1(param: NSString, completion:((NSArray?, NSURLResponse?)->())) ->(){
^
|
/* Error: Insert ',' here */
completion(arr, resp)
}
EDIT
Okay, it was not a syntactical error, but an error from my side. The method header I used here is different from the one in my code. So this code that I originally posted should work fine.
Typealias's are your friend here, just to make the code more readable
typealias onComplete = (NSArray?, NSURLResponse?) -> ()
func someMethodWithParam1(param: NSString, completion:onComplete) -> ()
{
completion(arr, resp)
}

Resources