Sending a function to another function as a parameter - ios

I want to send a func as a parameter to another func but I know how to do it only in case the func being sent has an input and an output parameters:
aFunc (sentFunc: Int -> String) {
...
}
But I want to achieve something like the following code where the function being sent does not have parameters:
func firstFunc(<i want to declare paramenter for a function here that itself does not have any parameters>) {
if (servicedCities != nil) {
<execute sent fuction>
} else {
objectMapper().getServicedCitiesifAvailable()
<execute sent fuction>
}
}
the func calling the above func should look like
func secondFunc() {
func myNestedFunction() {
....
}
....
firstFunc(myNestedFunction())
....
}

func firstFunc(passedFunc:()->()) {
passedFunc()
}
func secondFunc() {
func myNestedFunction() {
print("hi")
}
firstFunc(myNestedFunction)
}
secondFunc()
will ouput
hi
in the context of the firstFunc call.
Note that you have to omit the () in the last line after the myNestedFunction because you dont want to call the method but pass the method itself.

func firstFunc(sentFunc: () -> ()) {
sentFunc()
}
or
func firstFunc(sentFunc: Void -> Void) {
sentFunc()
}

I think you are looking for Swift closures.
Check this Swift closures and functions
func someFunc(paramFunc: () -> ()) {
paramFunc()
}
...
self.someFunc { () -> () in
//Do some code here;
}

Related

Set function as variable in function declaration

I want to have function as variable in function declaration and than call that variable within declared function. How to do it in Swift?
Pseudo code:
let something = 0
func one() {
print("one")
}
// Definition
func two( funcVariable: Void, number: Int) {
print("\(number)")
funcVariable() // here I want to call variable function
}
// Call
two(funcVariable: one(), number: somethhing)
How to do it?
Example code appreciated.
Here is how you do it:
let something = 0
func one() {
print("one")
}
// Definition
func two(funcVariable: () -> Void, number: Int) {
print("\(number)")
funcVariable()
}
// Call
two(funcVariable: one, number: something)

Swift Nested try inside a closure

I have a requirement in the below way
enum CustomError1: Error {
case errorA
}
enum CustomError2: Error {
case errorA
}
public func func1(completion: #escaping () -> Void) throws {
//some code
if #somecondition {
throw CustomError1.errorA
}
completion()
}
public func func2(completion: #escaping () -> Void) throws {
//some code
if #somecondition {
throw CustomError2.errorA
}
completion()
}
func result() {
do {
try func1() {
try self.func2 (){
}
}
} catch {
}
}
the result function gives an error as below
Invalid conversion from throwing function of type '() throws -> ()' to non-throwing function type '() -> Void'
That is because the func1 and func2 are giving different type of Error.
Due to this I need to write another do catch inside the first closure as below
func result() {
do {
try func1() {
do {
try self.func2 (){
}
} catch {
}
}
} catch {
}
}
Is there a way to simplify this kind of nested try catches
The problem is that the argument to func1 is typed as escaping () -> Void. That means you cannot throw inside the function you pass as that argument. You would need to have typed this as escaping () throws -> Void.
enum CustomError1: Error {
case errorA
}
enum CustomError2: Error {
case errorA
}
public func func1(completion: #escaping () throws -> Void) throws {
//some code
if true {
throw CustomError1.errorA
}
try completion()
}
public func func2(completion: #escaping () throws -> Void) throws {
//some code
if true {
throw CustomError2.errorA
}
try completion()
}
func result() {
do {
try func1(completion: {
try func2 (completion: {
})
})
} catch {
}
}
I wouldn't suggest using throw with completions. The better way is using better completion implementation. Something like this:
public func func1(completion: #escaping (Error?) throws -> Void) throws {
//some code
if true {
completion(CustomError1.errorA)
}
completion(nil)
}

What Completion Handlers Should I Write?

Having just learned about how to create completion handlers, I understand them in principle, but I don't know how to put the ideas into practice to accomplish what I need.
I have the following general code and storyboard structure: sessionVC (a UIViewController) and its UIView hold a container view with an embed segue to animationVC (also a UIViewController) and its SKView.
From sessionVC I want to run a series of animations in animationVC’s SKView. I’d like to prepare each animation as soon as possible (e.g., while each prior animation is still running), and I’d like each animation to wait for the last one to finish before it begins. See the code, below.
My question is, what do I put in place of the ???s in my code to accomplish the effects that I want (mentioned above, as well as after each *** in the code comments)?
// TO DELEGATE ANIMATION TO animationVC
protocol AnimateContentDelegate: AnyObject {
prepareContent(_ Content, contentWasPrepared: ???)
animateContent(animationDidComplete: ???)
playAnimation()
pauseAnimation()
}
// CONTROL THE OVERALL SESSION
class sessionVC: UIViewController {
// INITIALIZE contentArray
weak var delegate: AnimateContentDelegate?
override func viewDidLoad {
super.viewDidLoad()
delegate = self.childViewControllers[0] as? AnimateContentDelegate
runSession(contentArray)
}
func runSession(_ contentArray) {
for content in contentArray {
delegate?.prepareContent(content, contentWasPrepared: ???)
// ***DON’T START THE NEXT ANIMATION UNTIL contentWasPrepared
// DO CONTINUE THE CURRENT ANIMATION, AND ALLOW INTERACTIONS
delegate?.animateContent(animationDidComplete: ???)
// ***DON’T START THE NEXT ANIMATION UNTIL animationDidComplete
// DO CONTINUE THE CURRENT ANIMATION, AND ALLOW INTERACTIONS
}
}
#IBAction func playOrPause(_ sender: UILongPressGestureRecognizer) {
if sender == .possible || sender.state == .ended {
delegate?.playAnimation()
} else if sender.state == .began {
delegate?.pauseAnimation()
}
}
}
// PREPARE AND ANIMATE CURRENT CONTENT
class animationVC: UIViewController, AnimateContentDelegate {
// SET UP SKVIEW
func prepareContent(_ content: Content, prepCompleteHandler: ???) {
// PREPARE THE CONTENT
// ***REPORT WHEN IT IS FINISHED
}
func animateContent(animationCompleteHandler: ???) {
// ANIMATE THE CONTENT
// ***REPORT IF IT RAN TO COMPLETION
}
func playAnimation() {
skView?.scene?.isPaused = false
}
func pauseAnimation() {
skView?.scene?.isPaused = true
}
}
Following the usual conventions, you would declare the functions with a slightly different wording:
prepareContent(_ Content, completionHandler: { () -> Void })
animateContent(completionHandler: { () -> Void })
Then, below SET UP SKVIEW, you would call the delegate functions like this:
delegate?.prepareContent(content, completionHandler: {
self.delegate.animateContent(completionHandler: {
// do whatever needs to be done upon completion
})
})
(You could as well use the trailing closure syntax.)
By nesting the delegate calls like this you make sure animation is performed only after preparation has been finished.
The function bodies would look like this:
func prepareContent(_ content: Content, completionHandler: (() -> Void)) {
// ...
completionHandler()
}
func animateContent(completionHandler: (() -> Void)) {
// ...
completionHandler()
}
And if you prefer to have optional completion handlers, i. e. those which can be nil, change the function like this:
func prepareContent(_ content: Content, completionHandler: (() -> Void)?) {
// ...
completionHandler?()
}
It should look something like :
func prepareContent(_ content: Content, completionHandler: #escaping (Bool, Any?) -> Void) -> Void {
var finished : Bool = false
var output : Any? = nil
// prepare the content
// possibly update the output
//when ready :
finished = true
completionHandler(finished, output)
}
Then you use it :
prepareContent(content: content, completionHandler: { (f, o) in
if f {
if o != nil {
//do something
}
//else handle error
}
//else handle error
})
You can adapt it to your needs, adding more or less outputs if needs, error logs, and so on.
Do you want something like that ?
protocol AnimateContentDelegate: AnyObject {
func prepareContent(content : Content, contentWasPrepared: ((Bool) -> Void)?)
func animateContent(animationDidComplete: ((Bool) -> Void)?)
func playAnimation()
func pauseAnimation()
}
class YourObject : AnimateContentDelegate {
func prepareContent(content: Content, contentWasPrepared: ((Bool) -> Void)?) {
// prepare content
}
func animateContent(animationDidComplete: ((Bool) -> Void)?) {
// animate content
}
func playAnimation() {
}
func pauseAnimation() {
}
}
Use it like that :
let yourObject = YourObject()
yourObject.animateContent(animationDidComplete: { finished in
// animation did complete
})

callback function from caller to callee in swift

I have a view controller and a class for doing the bits to call the services and get the data from server.
The ViewController code is below,
class ViewController : UIViewController
{
override func viewDidLoad() {
let parser = Parser()
parser.connectServer("abc URL" , ..... <gotDataFromServer> ..... )
}
func gotDataFromServer(response:String)
{
...... Do our things here .......
}
}
and the parser code is below,
class Parser
{
func connectServer(apiURL:String,...<call back function name>...)
{
let manager = RequestOperationManager.sharedManager()
manager.GET(apiURL ,
parameters: nil,
success: { (operation,responseObject) ->Void in
.....<Call back the function which is passed in parameter> ....
},
failure: { (operation , error) in
print ("error occurred")
})
}
}
Now in the above sample code i want to pass call back function "gotDataFromServer" as a parameter and when the inner function get the response from the server then i want to call this function back.
Can anyone please help.
You can use delegates to achieve that. Try out following code
class ViewController : UIViewController, DataDelegate
{
override func viewDidLoad() {
let parser = Parser()
parser.delegate = self
parser.connectServer("abc URL" , ..... <gotDataFromServer> ..... )
}
func gotDataFromServer(response:String)
{
...... Do our things here .......
}
}
And add protocol in parser as follows
protocol DataDelegate {
func gotDataFromServer(response:String)
}
class Parser
{
var delegate : DataDelegate!
func connectServer(apiURL:String,...<call back function name>...)
{
let manager = RequestOperationManager.sharedManager()
manager.GET(apiURL ,
parameters: nil,
success: { (operation,responseObject) ->Void in
delegate.gotDataFromServer("") //parameter is your data
},
failure: { (operation , error) in
print ("error occurred")
})
}
}
Here's an example how you can do it using closure
class Parser {
func connectServer(apiURL: String, completion: String -> Void) {
// ... make call, get data
// share the results via completion closure
completion("data")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
let parser = Parser()
// Option #1
parser.connectServer("mybackend.com/connect") {
print("received data \($0)")
}
// Option #2 is the same as Option #1 but a bit longer
parser.connectServer("mybackend.com/connect") { (data) -> Void in
print("received data \(data)")
}
// Option #3 - Or if you have a separate funciton
// be careful with retain cycle
parser.connectServer("mybackend.com/connect", completion: gotDataFromServer)
}
func gotDataFromServer(response:String) { }
}

block in Swift : return error " is not convertible to "

I made a mistake but I cannot see how to solve it. I would like to load all the assets from GameScene and send a Bool in a completion method. I use typealias : should it be renamed twice for the two files (gameScene and gameController)?
Then I have got an error on this line GameScene.loadSceneAssetsWithCompletionHandler{ :
((Bool) -> Void) is not convertible to 'GameScene'
Here is the code :
//gameController:
typealias OnComplete = (Bool) -> ()
override func viewDidLoad() {
super.viewDidLoad()
GameScene.loadSceneAssetsWithCompletionHandler{ (success:Bool)->Void in
println("2/ yes")
return
}
//gameScene : rewrite typealias?
typealias OnComplete = (Bool) -> ()
func loadSceneAssetsWithCompletionHandler( completion:OnComplete ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
self.loadSceneAssets()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println("1/ yes")
completion(true)
})//main
})//global
}
I read some threads that said to add a "return", but it does not solve the error here.
Thanks
It's almost working, but you've got a couple things going wrong here. First of all, you can't redeclare a typealias. Second of all you're calling loadSceneAssetsWithCompletionHandler as a class function when it's set up as an instance function. Note changes:
typealias OnComplete = (Bool) -> ()
class GameController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
GameScene.loadSceneAssetsWithCompletionHandler { success in
println("2/ yes")
return
}
}
}
class GameScene: UIViewController {
func loadSceneAssets() {
}
class func loadSceneAssetsWithCompletionHandler( completion:OnComplete ) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
let gameScene = GameScene()
gameScene.loadSceneAssets()
dispatch_async(dispatch_get_main_queue()) {
println("1/ yes")
completion(true)
}
}
}
}

Resources