john doe
john doe

Reputation: 9690

Calling Swift Closure Inside Closure

I have the following code:

  twitterAPI?.verifyCredentialsWithUserSuccessBlock({ (userName, password) -> Void in


            twitterAPI?.getUserTimelineWithScreenName(userName, count: 100, successBlock: { ([AnyObject]!) -> Void in



                }, errorBlock: { (error :NSError!) -> Void in

            })



            }, errorBlock: { (error :NSError!) -> Void in

                println("error block")
        })

I am getting the following errors:

enter image description here

I tried saying self inside the outer closure but it did not work. What am I missing?

UPDATED: Still having build errors:

enter image description here

UPDATE: If I put the getUserTimeline method outside the closure then it works. THIS ONE WORKS.

//        twitterAPI?.getUserTimelineWithScreenName("", successBlock: { (objects :[AnyObject]!) -> Void in
//            
//            }, errorBlock: { (error: NSError!) -> Void in
//        
//        })

But this DOES NOT:

twitterAPI?.verifyCredentialsWithUserSuccessBlock({ (userName, password) -> Void in


    self.twitterAPI?.getUserTimelineWithScreenName("", successBlock: { (objects :[AnyObject]!) -> Void in

        }, errorBlock: { (error: NSError!) -> Void in

    })



    }, errorBlock: { (error :NSError!) -> Void in


})

UPDATE: Definition of getUserTimeLine method

self.twitterAPI?.getUserTimelineWithScreenName(<#screenName: String!#>, successBlock: <#(([AnyObject]!) -> Void)!##([AnyObject]!) -> Void#>, errorBlock: <#((NSError!) -> Void)!##(NSError!) -> Void#>)

enter image description here

UPDATE: Now, I am getting a build error saying missing argument sinceID. I am not even using that constructor.

 if let twitterAPI = self.twitterAPI {

            twitterAPI.verifyCredentialsWithUserSuccessBlock({ (userName, password) -> Void in

                twitterAPI.getUserTimelineWithScreenName(userName, successBlock: { (objects :[AnyObject]!) -> Void in

                    }, errorBlock: { (error :NSError!) -> Void in

                })


                }, errorBlock: { (error :NSError!) -> Void in

            })

        }

Upvotes: 18

Views: 6769

Answers (4)

dimpiax
dimpiax

Reputation: 12697

Swift 4

Here is the simple example. But better to make an implementation through monad.

...
guard let api = twitterAPI else { return }

api.verifyCredentialsWithUserSuccessBlock({ userName, password in
    api.getUserTimelineWithScreenName(
        userName, 
        count: 100, 
        successBlock: { value in
            // success
        }) { error in 
            print("get user error: \(error)") 
        }
}) { error in 
    print("verify error: \(error)") 
}

Upvotes: 1

Warren Burton
Warren Burton

Reputation: 17378

This is a really misleading error message. The problem is that the inner variable can't be of optional type so you need to if/let it.

Check this out in a playground...

class Foo:NSObject {
    func doThing(bug:Int,completion:((Void)->(Void))) {

    }
}

let woot = Foo()
var bar:Foo? = Foo()

bar?.doThing(7, completion: {});

woot.doThing(3, completion: {

     bar?.doThing(4, completion:{});

});

It doesnt compile and the message

Cannot convert the expression type '(IntegerLiteralConvertible, completion:()->()-$T3)' to type '()'

Isn't exactly illuminating to the problem.

So you unwrap the optional

woot.doThing(3, completion: {

    if let bar = bar {
        bar.doThing(4, completion:{});
    }

});

And it now compiles.

And to the other issue

If you check the STTwitterAPI.h header

- (NSObject<STTwitterRequestProtocol> *)getUserTimelineWithScreenName:(NSString *)screenName
                                                         successBlock:(void(^)(NSArray *statuses))successBlock
                                                           errorBlock:(void(^)(NSError *error))errorBlock;

is just a convenience for the full signature of this.

- (NSObject<STTwitterRequestProtocol> *)getUserTimelineWithScreenName:(NSString *)screenName
                                                              sinceID:(NSString *)sinceID
                                                                maxID:(NSString *)maxID
                                                                count:(NSUInteger)count
                                                         successBlock:(void(^)(NSArray *statuses))successBlock
                                                           errorBlock:(void(^)(NSError *error))errorBlock;

Obj-C to Swift bridging places everything after the the first selector chunk inside the brackets so convenience methods tend to confuse things by code completing but not providing the canonical case.

So in your case (without me having STTwitter) this is what you want.

twitterAPI?.verifyCredentialsWithUserSuccessBlock({ (userName, password) -> Void in

    if let twitterAPI = self.twitterAPI {
       twitterAPI.getUserTimelineWithScreenName("JohnSmith",sinceID:someID,maxID:anotherID,count:1000, successBlock: { (objects)[AnyObject]!) -> Void in

        }, errorBlock: { (error: NSError!) -> Void in

       })
   }


    }, errorBlock: { (error :NSError!) -> Void in


})

how you choose to populate sinceID , maxID and count is up to you . I've never used the API so won't guess. They might be nil'able

Upvotes: 0

rintaro
rintaro

Reputation: 51911

Try:

        twitterAPI?.verifyCredentialsWithUserSuccessBlock({ (userName, password) -> Void in
            self.twitterAPI?.getUserTimelineWithScreenName(userName, successBlock: { (objects :[AnyObject]!) -> Void in

                }, errorBlock: { (error :NSError!) -> Void in
            })

            return  // <-- ADDED

            }, errorBlock: { (error :NSError!) -> Void in
        })

In this case

{ (userName, password) -> Void in
    self.twitterAPI?.getUserTimelineWithScreenName("", successBlock: { (objects :[AnyObject]!) -> Void in
    }, errorBlock: { (error: NSError!) -> Void in
    })
}

is a "single expression closure" that has implicit non Void return.

As of Xcode 6.2 / Swift 1.1, you need explicit return here.

Or, use Xcode 6.3 / Swift 1.2 that has fixed this problem.

See this question: One-line closure without return type or Swift - 'Bool' is not a subtype of 'Void'?

Upvotes: 4

danielbeard
danielbeard

Reputation: 9149

Ok, by the method names you are using, I'm guessing you are using the STTwitter library. If that's the case, you'll want something like this:

    if let twitterAPI = self.twitterAPI {
        twitterAPI.verifyCredentialsWithSuccessBlock({ (String) -> Void in
            twitterAPI.getUserTimelineWithScreenName("test", successBlock: { (objects: [AnyObject]!) -> Void in
                println("success")
                }, errorBlock: { (error: NSError!) -> Void in
                    println("failure")
            })
            }, errorBlock: { (error: NSError!) -> Void in

        })
    }

Note the let call before using the optional self.twitterAPI variable.

Upvotes: 1

Related Questions