I'm writing some generic code, so all view controllers can use it.
One of the things I like to make generic is an alert function.
The problem here is that I must code the actions in the replies.
That's fine with warnings (only press OK), or generic responses (Cancel, No), but when a (generic) function needs to run, I need to trick to pass on which function.
If I can run a function from some text, it will reduce the problem (and I don't have to hard-code all the functions that can be called).
Or is there a much better way of achieving my 'generic' alert?
Sample here:
func DoAlert(title: String, message: String, actions: String, sender: AnyObject, viewController : UIViewController) {.......
.....
if (actions as NSString).containsString("Yes") {
alert.addAction(UIAlertAction(title: "Yes", style: .Default) { action -> Void in
if (actions as NSString).containsString("Yes'DoAfunction()'") {
DoAfunction() }
})}
.....
}
// Rather than hard-coding, I like to abstract the function between ' ' and use that to call the function
// I call the function as follows:
DoAlert("Warning", alertText, "Yes'DoAfunction()'No", sender, self)
/////////// SOLUTION: ////////////////
Following Bluehound's suggestion to use closures, I ended up adding optional closures for different responses.
For those wishing to do same, below is my solution:
Solution here:
func DoAlert(title: String, message: String, actions: String, sender: AnyObject, viewController : UIViewController, YesClosure: ()->() = {}, NoClosure: ()->() = {}) {.......
.....
if (actions as NSString).containsString("Yes") {
alert.addAction(UIAlertAction(title: "Yes", style: .Default) { action -> Void in
YesClosure() // This will the run the function if provided
})}
.....
}
// I call the function as follows:
DoAlert("Warning", alertText, "YesNo", sender, self, YesClosure: DoYesFunction, NoClosure: DoNoFunction)
If there is nu function to be performed, leave the option out. (below only has function for NO)
DoAlert("Warning", alertText, "YesNo", sender, self, NoClosure: DoNoFunction)
Instead of passing the name of a function to complete, you can pass a closure as a parameter and when calling the function, you define what goes function is passed in. For example:
func foo(closure: () -> Void) {
closure()
}
foo {
println("Some text")
} // prints Some Text
Now for using multiple actions you can pass an array of closures like so:
func foo(closures: [() -> Void]) {
for closure in closures {
closure()
}
}
foo([{println("a")},
{println("b")},
{println("c")}]) // prints a b c
Related
func check(a: () -> ()){
func a(){
print("A Calling")
}
a()
}
Calling function
check {
print("Check Calling")
}
In the above code, i am not able to call a() function and it will directly be executing "check calling "
Result : "Check Calling"
When i am calling check function it should be execute a() function also and order should be
Result should be:
"A Calling"
"Check Calling"
The problem with the code in your question is that you do not call the nested function at all. It doesn't help that you've named it the same as the passed in function which is perhaps why you are having trouble isolating the issue.
This works
func check(completion: () -> Void) {
func localFunction() {
print("A Calling")
}
localFunction()
completion()
}
check {
print("Check Calling")
}
// Outputs:
//
// A Calling
// Check Calling
so i'm doing my first app,
and i want to do a function that will be a uniform funciton to sevral places in the system, and not belong to only one specific class.
Is there a way for me to pass a callback function as a parameter to another function ?
here is just a demonstration of what i mean.
ClassA {
func classAcallbackFunction (displayString: String) {
print (displayString)
}
ClassB().classBFunction(classAcallbackFunction())
}
ClassB {
func classBfunction (myCallbackfunc: func) {
mycallbackfunc("all is working !!!")
}
}
The parameter you have declared is not correct. Replace it with something like this:
func classBFunction(_ completion: (String) -> Void) {
completion("all working")
}
Like shared by regina_fallangi in the comments, callbacks are usually called completion handlers, which is why I replaced the names accordingly.
Extra Credit:
If you only want to optionally pass a function, you could do this:
func classBFunction(_ completion: ((String) -> Void)? = nil) {
completion?("all working")
}
Now you could also just call classBFunction().
Say we have this enum
enum Action: String {
case doThing
case doOtherThing
}
This enum is used this way:
func run(action: Action, block: () -> Void)
Now, I unit test the run method so I need to pass an Action this way:
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
As I need to test other situations on ActionRunner, I ended with a lot of .doThing spread over the whole test suite.
The problem is: if I make a change in production code and change case doThing to case doThatThing now all my test suite fails because there is no a case doThing.
The perfect thing would be to declare a dummy case in test code to allow something like
sut.run(action: .dummyAction) {
}
but enum does not allow that as it doesn't allows inheritance nor a extension to add a case.
The first option that came to my mind was to convert Action into a protocol, but that change is unnecessary in production and its only purpose is to accomplish something in test code.
So, is it there another option to achieve this?
The question of how to avoid coupling when using enums is a tricky one. I bumped into that myself a few times with no solid answer :/
One point you raise is the one of using a protocol, and that feels unnecessary in production. I sort of agree with that, but most time it's the necessary evil.
In the example you showed though I think maybe a tweak in the design might solve part of the problem.
In particular when looking at this code
func run(action: Action, block: () -> Void) {
// ...
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
What comes to mind to me is that your Action specifies a certain behaviour. That is when you test the run method passing .doThing you expect a different behaviour than when passing .doOtherThing.
If that's right, is there any reason why you need to pass the action enum instance and an action block to the run function?
You could separate the code that defines the behaviour from the one performs the actual action even more that what you've done already. For example:
protocol Actionable {
var action: () -> () { get }
}
enum Action: Actionable {
case doThing
case doOtherThing
var action {
switch self {
case .doThing: return ...
case .doOtherThing: return ...
}
}
class ActionRunner {
func run(actionable: Actionable) {
actionable.action()
}
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(actionable: FakeActionable()) {
expectation.fulfill()
}
waitForExpectations(timeout: 0.1, handler: nil)
}
class FakeActionable: Actionable {
let action = { }
}
func testDoThing() {
let sut = Action.doThing
sut.action()
// XCTAssert for the expected effect of the action
}
Note: I haven't actually compiled that code, so bear with me if it has some mistakes. It should give the idea though.
This way you have ActionRunner which only purpose is to properly run a given Actionable, and the Action enum which only purpose is to describe what different actions should do.
This example code is rather restrict in what it can do, only run () -> () actions, but you could build on top of it to achieve more advanced behaviours.
If you change your production code you have to change your test code too in order to test those new changes.
Maybe you can set the value on an Action variable in the setUp func of your XCTestCase class
import XCTest
class SharingKitTests: XCTestCase {
var theAction: Action!
override func setUp() {
super.setUp()
self.theAction = .doThing
}
}
Then you will be able to use this theAction var in all your test methods, and if you need to change the value you only need to change it in one place.
I am trying to grok how to use NSLayoutManager using Swift closures. I can successfully register an undo as follows:
doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
undoThing();
}
undoManager?.setActionName("do thing")
Of course I need to support redo which amounts to an undo of an undo. I can do that:
doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
undoThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
doThing();
}
undoManager?.setActionName("do thing")
}
undoManager?.setActionName("do thing")
But now I need to support an undo of the redo... hmmm.... ok:
doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
undoThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
undoThing();
}
undoManager?.setActionName("do thing")
}
undoManager?.setActionName("do thing")
}
undoManager?.setActionName("do thing")
As you can see its "turtles all the way down." How do I escape from this madness? i.e., in all the example code I can find, folks use the selector version of the code to register a method that can undo itself -- this is not obviously doable with the closure method I am using... How does one use the closure version and get unlimited undo/redo?
What you're looking for is mutual recursion. You need two functions, each of which registers a call to the other. Here are a couple of different ways to structure it:
In doThing(), register the undo action to call undoThing(). In undoThing, register the undo action to call doThing(). That is:
#IBAction func doThing() {
undoManager?.registerUndoWithTarget(self, handler: { me in
me.undoThing()
})
undoManager?.setActionName("Thing")
// do the thing here
}
#IBAction func undoThing() {
undoManager?.registerUndoWithTarget(self, handler: { me in
me.doThing()
})
undoManager?.setActionName("Thing")
// undo the thing here
}
Note that you should not refer to self in the closure unless you capture it with weak, because capturing it strongly (the default) may create a retain cycle. Since you're passing self to the undo manager as target, it's already keeping a weak reference for you and passing it (strongly) to the undo block, so you might as well use that and not reference self at all in the undo block.
Wrap the calls to doThing() and undoThing() in separate functions that handle undo registration, and connect user actions to those new functions:
private func doThing() {
// do the thing here
}
private func undoThing() {
// undo the thing here
}
#IBAction func undoablyDoThing() {
undoManager?.registerUndoWithTarget(self, handler: { me in
me.redoablyUndoThing()
})
undoManager?.setActionName("Thing")
doThing()
}
#IBAction func redoablyUndoThing() {
undoManager?.registerUndoWithTarget(self, handler: { me in
me.undoablyDoThing()
})
undoManager?.setActionName("Thing")
undoThing()
}
I'm using optional closures, but can't find a way to pass on a parameter.
Searched everywhere, tried all suggestions, but can't get it to work.
My code:
func DoAlert(title: String
, message: String
, actions: String
, sender: AnyObject?
, Ctlr : UIViewController
, SegueString: String?
, YesClosure: ()->() = {}
, NoClosure: ()->() = {}
, StartClosure: ()->() = {}
, EndClosure: ()->() = {}
) {
if (actions.rangeOfString("Ok") != nil {
alert.addAction(UIAlertAction(title: "OK", style: .Default ) { action -> Void in
EndClosure()
})}
} // end function
I want to add a closure for Ok, where the 'self' parameter is needed.
Something like below:
// add to func doAlert:
, OkClosure: (AnyObject)->() = {}
// add to action Ok (before the EndClosure:
OkClosure(sender!)
Getting error on first line:
AnyObject is not subtype of ()
If I leave AnyObject out of first line, Getting error:
Cannot convert the expression's type 'AnyObject' to type '() => ()'
All other trials give me similar 'Tuple' errors.
How do I code the passing of parameters in the optional closures in my code?
Firstly, to use closures as an argument for a function, you should declare them like so:
func myFunc(closure: (Int) -> Void) {
// Now I can call closure like so:
let myInt = 10
closure(myInt)
}
(As pointed out by #Airspeed Velocity, the parenthesis around Int are not strictly required because there is only one argument. Whether you include them is just personal preference)
Secondly, you can modify the previous function to include an optional closure, as follows:
(Note the ? and parenthesis around the closure that indicate the closure is an optional, not the return type)
func myFunc(closure: ((Int) -> Void)?) {
// Now when calling the closure you need to make sure it's not nil.
// For example:
closure?(10)
}
Thirdly, to add a default value of nil, which is what it looks like you're trying to do with the = {} on the end of YesClosure: ()->() = {}, you could do:
func myFunc(closure: ((Int) -> Void)? = nil) {
// Still need to make sure it's not nil.
if let c = closure {
c(10)
}
}
Finally, just as a note, you can set the names of the arguments of the closure, which can make it easier to identify what you're passing to the closure when calling it. For example:
(Note - here parenthesis are required around value: Int)
func myFunc(closure: ((value: Int) -> Void)) {
closure(value: 10)
}
Even more finally, you could use typealias. According to the documentation:
A type alias declaration introduces a named alias of an existing type into your program.
Here's an example of how to use it with a closure:
typealias MyClosureType = () -> Void
func myFunc(closure: MyClosureType) {
closure()
}
Hope that helps!
I think I found it. I can't use parameters in the closure when I call func. In func itself I need to define the parameters used (closure: (sender: AnyObject) -> Void), make sure the variables are defined (or provided as a separate parameter) and add them to the closure call.
#IBAction func buttonPressed(sender: AnyObject) {
myFunc (sender, doClosure)
}
func myFunc(sender: AnyObject, closure: (sender: AnyObject) -> Void) {
// Now I can call closure like so:
closure (sender: sender)
}
func doClosure(sender: AnyObject) {
println("sender = \(sender)")
}