I am a new programmer. I started learning swift 2.0 without objective c, which I guess was a mistake.
I am trying to integrate quickblox into my swift app, however this line of code is really confusing me. I was wondering if someone could give me a hand
- (void (^)(QBResponse *response, QBUUser *user))successBlock
{
return ^(QBResponse *response, QBUUser *user) {
// Login succeeded
};
}
The function returns a block function, which gets two parameters: the response and the user. Its return type is void.
So in swift, it should basically look like this:
func successBlock() -> (QBResponse, QBUUser) -> Void {
return { (response, user) in
//Login succeeded.
}
}
It could also be converted to a computed property as it does not have side effects and does not rely on any parameters:
var successBlock: (QBResponse, QBUUser) -> Void {
return { (response, user) in
//Login succeeded.
}
}
Related
I have the following code -
[self.camera capture:^(LLSimpleCamera *camera, UIImage *image, NSDictionary *metadata, NSError *error) {
if(!error) {
}
}];
which, to me, should translate to
self.camera.capture({(camera: LLSimpleCamera, image: UIImage, metadata: [NSObject : AnyObject], error: NSError) -> Void in
if !error {
}
})
but it's throwing all the errors about 'cannot convert value of type etc, between the front and the return Void in section. Can someone tell me, is this a place when you have to use unSafeMutablePointers? If so, how would i know that.
Thanks!
The problem is that these are nullables, so you need exclamation marks. So:
self.camera.capture {
(camera:LLSimpleCamera!, image:UIImage!, metadata:[NSObject : AnyObject]!, error:NSError!) -> Void in
// whatever
}
Or more compactly, just omit the types and let Swift infer them from the Objective-C declaration:
self.camera.capture {
camera, image, metadata, error in
// whatever
}
Also, you can't say !error in Swift, but you can cross that bridge when you come to it.
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.
I am attempting to make all my user sessions with Parse exclusive, meaning if a user is already logged in on a certain device in a certain location, if another device logs in with the same credentials, I want the previous session(s) to be terminated, with a message of an alert view of course. Sort of like the old AOL Instant Messaging format. I figured the code for this action should be written in the login logic, so I wrote this within my login "succession" code :
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> Void in
if user != nil || error == nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSuccess", sender: self)
PFCloud.callFunctionInBackground("currentUser", withParameters: ["PFUser":"currentUser"])
//..... Get other currentUser session tokens and destroy them
}
} else {
Thats probably not the correct cloud code call, but you get the point. When the user logs in once again on another device, I want to grab the other sessions and terminate them. Does anyone know the correct way to go about making this request in swift?
I speak swift with a stutter, but I think I can answer adequately in almost-swift. The key idea is to start the success segue only after the cloud says it's okay. Here's what I think you want:
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> Void in
if (user != nil) {
// don't do the segue until we know it's unique login
// pass no params to the cloud in swift (not sure if [] is the way to say that)
PFCloud.callFunctionInBackground("isLoginRedundant", withParameters: []) {
(response: AnyObject?, error: NSError?) -> Void in
let dictionary = response as! [String:Bool]
var isRedundant : Bool
isRedundant = dictionary["isRedundant"]!
if (isRedundant) {
// I think you can adequately undo everything about the login by logging out
PFUser.logOutInBackgroundWithBlock() { (error: NSError?) -> Void in
// update the UI to say, login rejected because you're logged in elsewhere
// maybe do a segue here?
}
} else {
// good login and non-redundant, do the segue
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
}
} else {
// login failed for typical reasons, update the UI
}
}
Please don't take me too seriously on swift syntax. The idea is to nest the segue in the completion handlers to know that you need to do it before starting it. Also, please note that the explicit placement on the main_queue within the completion handler is unnecessary. The SDK runs those blocks on the main.
A simple check to determine if a user's session is redundant (not unique) looks like this...
Parse.Cloud.define("isLoginRedundant", function(request, response) {
var sessionQuery = new Parse.Query(Parse.Session);
sessionQuery.equalTo("user", request.user);
sessionQuery.find().then(function(sessions) {
response.success( { isRedundant: sessions.length>1 } );
}, function(error) {
response.error(error);
});
});
This Is how i get the instance of my network client:
let networkClient = DBNetworkClient(baseURL: NSURL(string: "http://mysite.pl/api"))
I also have one method:
func citiesWithParameters(parameters: [String: String], completionBlock: DBSearchOptionHandler) {
GET("cities", parameters: parameters, success: { operation, response in
if let error = NSError(response: response) {
completionBlock([], error)
} else {
let cities = DBCity.parseCitiesWithDictionary(response as! NSDictionary)
completionBlock(cities, nil)
}
}) { operation, error in
completionBlock([], error)
}
}
This is how I call this method:
networkClient.citiesWithParameters(parameters, completionBlock: { cities, error in
//do sth
})
This way I pass some parameters, and get the REAL response from server. I would like to mock THAT response when I ask for this. How to do this?
func testCities() {
let mockNetworkClient = OCMockObject.mockForClass(DBNetworkClient.classForCoder())
//what should I perform here to be able to do sth like this:
let expectation = expectationWithDescription("")
mockNetworkClient.citiesWithParameters(["a": "b"]) { cities, error in
expectation.fulfill()
XCTAssertNotNil(cities)
XCTAssertNil(error) //since I know what is the response, because I mocked this
}
waitForExpectationsWithTimeout(10, handler: nil)
}
And this is how my method GET is defined within DBNetworkClient:
override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {
return super.GET(URLString, parameters: parameters, success: { (operation, response) in
print("GET \(operation.request.URL)")
print("GET \(response)")
success(operation, response)
}, failure: { (operation, error) in
print("GET \(operation.request.URL)")
print("GET \(operation.responseObject)")
failure(operation, error)
})
}
Once I will be able I will award 50 bounty for the one, who help me do this.
Writing mock tests for AFNetworking is unfortunetaly not helpful. It is not working for me.
I do not have experience with Alamofire therefore I don't know where is declared your GET method but you should definitely stub this method instead of citiesWithParameters.
For example if it's declared in your DBNetworkClient:
func testCities()
{
//1
class DBNetworkClientMocked: DBNetworkClient
{
//2
override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {
//3
success(nil, ["San Francisco", "London", "Sofia"])
}
}
//4
let sut = DBNetworkClientMocked()
sut.citiesWithParameters(["a":"b"]) { cities, error in
//5
XCTAssertNotNil(cities)
XCTAssertNil(error)
}
}
So what happens here:
You define class that is children to DBNetworkClient, therefore making a 'Mock' for it as described in article you posted. In that class we will override only methods that we want to change(stub) and leave others unchanged. This was previously done with OCMock and was called Partial Mock.
Now you stub it's GET method in order to return specific data, not actual data from the server
Here you can define what to return your stubbed server. It can be success failure etc.
Now after we are ready with mocks and stubs, we create so called Subject Under Test(sut). Please note that if DBNetworkClient has specific constructor you must call this constructor instead of default one - ().
We execute method that we want to test. Inside it's callback we put all our assertions.
So far so good. However if GET method is part of Alamofire you need to use a technique called Dependency Injection(you could google it for more info).
So if GET is declared inside another class and is only referenced in citiesWithParameters, how we can stub it? Let's look at this:
//1
class DBNetworkClient
{
//2
func citiesWithParameters(parameters: [String: String], networkWorker: Alamofire = Alamofire(), completionBlock: DBSearchOptionHandler) {
//3
networkWorker.GET("cities", parameters: parameters, success: { operation, response in
if let error = NSError(response: response) {
completionBlock([], error)
} else {
let cities = DBCity.parseCitiesWithDictionary(response as! NSDictionary)
completionBlock(cities, nil)
}
}) { operation, error in
completionBlock([], error)
}
}
}
func testCities()
{
//4
class AlamofireMock: Alamofire
{
override func GET(URLString: String!, parameters: AnyObject!, success: ((AFHTTPRequestOperation!, AnyObject!) -> Void)!, failure: ((AFHTTPRequestOperation!, NSError!) -> Void)!) -> AFHTTPRequestOperation! {
success(nil, ["San Francisco", "London", "Sofia"])
}
}
//5
let sut = DBNetworkClient()
sut.citiesWithParameters(["a":"b"], networkWorker: AlamofireMock()) { cities, error in
//6
XCTAssertNotNil(cities)
XCTAssertNil(error)
}
}
First we have to slightly change our citiesWithParameters to receive one more parameter. This parameter is our dependency injection. It is the object that have GET method. In real life example it will be better this to be only protocol as citiesWithParameters doesn't have to know anything more than this object is capable of making requests.
I've set networkWorker parameter a default value, otherwise you need to change all your call to citiesWithParameters to fulfill new parameters requirement.
We leave all the implementation the same, just now we call our injected object GET method
Now back in tests, we will mock Alamofire this time. We made exactly the same thing as in previous example, just this time mocked class is Alamofire instead of DBNetworkClient
When we call citiesWithParameters we pass our mocked object as networkWorker. This way our stubbed GET method will be called and we will get our expected data from 'fake' server.
Our assertions are kept the same
Please note that those two examples do not use OCMock, instead they rely entirely on Swift power! OCMock is a wonderful tool that we used in great dynamic language - Objective-C. However on Swift dynamism and reflection are almost entirely missing. Thats why even on official OCMock page we had following statement:
Will there be a mock framework for Swift written in Swift? Maybe. As
of now it doesn't look too likely, though, because mock frameworks
depend heavily on access to the language runtime, and Swift does not
seem to provide any.
The thing that is missing with in both implementations provided is verifying GET method is called. I'll leave it like this because that was not original question about, and you can easily implement it with a boolean value declared in mocked class.
One more important thing is that I assume GET method in Alamofire is instance method, not a class method. If that's not true you can declare new method inside DBNetworkClient which simply calls Alamofire.GET and use that method inside citiesWithParameters. Then you can stub this method as in first example and everything will be fine.
Please help me to solve this problem - after a lot of (not so efficent...) search I can't do this alone.
I have the following methods:
showLoadingAnimation()
to show the loading animation while background tasks are running
hideLoadingAnimation()
to hide the loading animation as soon as all the background tasks are finished
getUserFacebookData()
to get Facebook-user data
uploadUserFacebookDataToServer()
to upload the Facebook-user data to the server (and perform tasks with them).
What I want to perform:
Show up the loading animation: showLoadingAnimation()
Get the user data from Facebook: getFacebookData()
Wait until these data are being downloaded
As soon as the Facebook-user data are being download, upload these data to the server: uploadUserFacebookDataToServer()
Wait untile these data are being uploaded
Hide the loading animation: hideLoadingAnimation()
Now my problem is, that I don't know how to solve this problem. I know, that I should use sync and/or async tasks, GCD... But I don't know how, and I can't find a proper guide to it.
Could someone explain it to me through these functions?
Thanks!
UPDATE:
Thank you, Zhi-Wei Cai, that was the kind of answer what I was hoping for.
Now it seems to work, the calling order is OK,
but now the problem is the same as the beginning:
uploadUserFacebookDataToServer()
doesn't wait until
getUserFacebookData
downloads the user data from Facebook, that's why it won't be able to work with the necessary data given back from
getUserFacebookData
Any idea? Is there anything to do with dispatch?
UPDATE 2:
As you requested, here are the fuctions. I hope, with these information you can help me to solve this problem and to understand this whole process.
func getFacebookUserData(completionHandler: () -> Void)
{
println("getFacebookUserData")
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
println("Error: \(error)")
}
else
{
let userID : NSString = result.valueForKey("id") as NSString!
var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(userID, forKey: "settings_facebookID")
self.facebookID_fromSettings = userID
}
})
and
func getObjectIDfromFacebookID(completionHandler: () -> Void)
{
println("getObjectIDfromFacebookID")
var query = PFQuery(className:"users")
query.whereKey("facebookID", equalTo:facebookID_fromSettings)
println("getObjectIDfromFacebookID: facebookID: " + facebookID_fromSettings)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if (objects!.count == 0) {
// New user, registering
println("New user, registering")
}
else
{
//User is already regsitered, reading out objectID
println("User is already regsitered, reading out objectID")
}
if let objects = objects as? [PFObject] {
for object in objects {
println("objectID: " + object.objectId)
var objectID: String = object.objectId
println(objectID)
var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(objectID, forKey: "settings_objectID")
}
}
}
}
completionHandler()
}
So the first function gets the facebookID from the FB-server, but this process takes time, it won't give a result immediately. The second function should work with this data, that's why it should "wait" until the first gives back the requested data.
I can solve this problem by building these 2 fuctions together in one, but that's "not elegant", and I also would like to use this (sync/async dispatch) method in other parts of the project,
Thanks for trying to help me!
You can use completion handlers:
func showLoadingAnimation() {
self.getUserFacebookData({ () -> Void in
self.uploadUserFacebookDataToServer({ () -> Void in
self.hideLoadingAnimation()
})
})
}
func getUserFacebookData(completionHandler: () -> Void) {
println("getUserFacebookData")
completionHandler()
}
func uploadUserFacebookDataToServer(completionHandler: () -> Void) {
println("uploadUserFacebookDataToServer")
completionHandler()
}
func hideLoadingAnimation() {
println("hideLoadingAnimation")
}
Once showLoadingAnimation() is called, the rest will be done asynchronously.
Reference: https://developer.apple.com/library/ios/featuredarticles/Short_Practical_Guide_Blocks/
[EDIT] Use dispatch_group_t
check following code
Step 1: showLoadingAnimation()
Step 2: dispatch_group_t faceBookService = dispatch_group_create();
Step 3:dispatch_group_async_enter(faceBookService,queue,^{
getUserFacebookData()
// The below given line should be inside completion handler or after the above task has finished
dispatch_group_leave(faceBookService);
});
Step 4:dispatch_group_async_enter(faceBookService,queue,^{
uploadUserFacebookDataToServer()
// The below given line should be inside completion handler or after the above task has finished
dispatch_group_leave(faceBookService);
});
Step 5:dispatch_group_notify(faceBookService,dispatch_get_main_queue(),^{
//Called after finishing both tasks.
hideLoadingAnimation()
});