Implement a condition which is based on calls from different external IBActions? - swift

I have these two IBActions in WorkoutsController.swift.
#IBAction func startWalkingButton() {
print("Walking start button pressed")
presentControllerWithName("Dashboard", context: sessionContext)
wSM!.startWorkout()
}
#IBAction func startCyclingButton() {
print("Cycling start button pressed")
presentControllerWithName("Dashboard", context: sessionContext)
wSM!.startWorkout()
}
They are calling the startWorkout() function in WorkoutSessionManager.swift
func startWorkout() {
self.healthStore.startWorkoutSession(self.workoutSession)
if ... {
print("startWorkout() called from startWalkingButton")
} else if ... {
print("startWorkout() called from startCyclingButton")
}
}
How do I create a condition to print out different print statements depending on which button function called the method? Should I use an if statement or switch statement?
I know there is already a print statement for the separate IBActions but I want to know if it's possible to do it in the reverse for redundancy.

Simply add one Bool parameter with your method startWorkout
func startWorkout(isFromWalking: Bool) {
if (isFromWalking) {
print("startWorkout() called from startWalkingButton")
}
else {
print("startWorkout() called from startCyclingButton")
}
}
Now call this function from startWalkingButton method with passing true
startWorkout(true)
and from startCyclingButton method with passing false
startWorkout(false)
Edit:
You haven't told that you have multiple option, then best option is to used enum in this case, create one enum like this and use that with the method
enum Workout {
case Walking
case Cycling
//Add case that you have
}
Now change the function like this
func startWorkout(workout: Workout) {
switch(workout) {
case .Walking :
print("Walking")
case .Cycling:
print("Cycling")
}
}
And call the function like this
self.startWorkout(.Walking)
self.startWorkout(.Cycling)

Simply add some sort of 'sender' parameter to your startWorkout() method.
Example:
// Hold a reference to your buttons, connected from IB
#IBOutlet var startWalkingButton: UIButton!
#IBOutlet var startCyclingButton: UIButton!
// here are your .TouchUpInside actions
// UIControl action methods receive the sender of the event as the first parameter (sender)
#IBAction func startWalkingButtonTouched(sender: AnyObject) {
...
startWorkout(sender)
}
#IBAction func startCyclingButtonTouched(sender: AnyObject) {
...
startWorkout(sender)
}
func startWorkout(sender: AnyObject) {
self.healthStore.startWorkoutSession(self.workoutSession)
switch sender {
case startWalkingButton:
print("startWorkout() called from startWalkingButton")
break
case startCyclingButton:
print("startWorkout() called from startCyclingButton")
break
default: ()
}
}
Hope this helps.

I feel you should probably use a block here. startWorkout method should accept an optional block. This approach avoids passing arguments and also avoids having if and case statements.
class Walking {
let workout = Workout()
func startWalkingButton() {
print("startWalkingButton")
workout.startWorkout() {
print("Walking Over")
}
}
}
class Cycling {
let workout = Workout()
func startCyclingButton() {
print("startCyclingButton")
workout.startWorkout() {
print("Cycling Over")
}
}
}
class Workout {
func startWorkout( afterWorkout: () -> Void ){
print("startWorkout")
afterWorkout()
}
}
let w = Walking()
w.startWalkingButton()

Related

Different button actions from the same delegate method

So I have a FeatureTutorialDelegate protocol with one buttonPressed() method. I have a FeatureTutorialViewController that functions as the delegate. A PageViewController calls these FeatureTutorialViewController classes. The different pages have multiple buttons.
How do I make the different buttons do different things with the buttonPressed() method? I need the button to dismiss the tutorial, open other ViewControllers, etc, depending on which tutorial page the user taps the button on.
Create an enum for the actions.
enum Action {
case .dismiss
case .open
}
protocol ADelegate {
func buttonPressed(action: Action)
}
class A(){
func navigate(){
let b = B()
b.delegate = self
}
}
extension A: ADelegate {
func buttonPressed(action: Action) {
if action == .dismiss {
} else if action == .open{
}
}
}
class B {
var delegate: ADelegate?
func dismiss(){
self.delegate?.buttonPressed(action: .dismiss)
}
func open(){
self.delegate?.buttonPressed(action: .dismiss)
}
}
Read this thread should you need to know why you should not use string as the other answer suggests.
What are enums and why are they useful?
You may define multiple methods in the protocol. Doing so you avoid using if-else statement. It is cleaner.
protocol ADelegate {
func dismiss()
func open()
func navigate()
}
you need to pass variable in delegate method so when delegate is call check that variable and perform action accordingly.
protocol MoveFeatureTutorial {
func buttonPressed(check: String)
}
var delegate: MoveFeatureTutorial
put below code in button action method
self.delegate?.buttonPressed(check: "For Button A")
self.delegate?.buttonPressed(check: "For Button B")
It's is the method that that protocol
func buttonPressed(check: String?) {
if check == "For Button A" {
print(check)
} else if check == "For Button B" {
print(check)
} else {
print(check)
}
}

Why is my data not passing between View Controllers using closure?

I am trying to pass data receive from a network call to another view controller when user has clicked on a button. When making printing on the FirstVC, data is in, but when printing the result in the SecondVC, there is no more value. I don' t want to use delegate but closure instead.
Also, when trying to retain the memory cycle, an error appear...
class APIsRuler {
static var oneRecipeFound: ((OneRecipeSearch) -> ())?
}
class FirstVC: UIViewController {
func cellIsClicked(index: Int) {
APIsRuler.shared.getRecipe(from: recipeID) { (success, oneRecipe) in
if success, let oneRecipe = oneRecipe {
APIsRuler.oneRecipeFound?(oneRecipe)
self.performSegue(withIdentifier: "goToSecondVC", sender: self)
}
}
}
}
Class SecondVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIsRuler.oneRecipeFound = { result in
print(result)
}
}
}
Doing this in SecondVC
APIsRuler.oneRecipeFound = { result in
print(result)
}
and this in first
APIsRuler.oneRecipeFound?(oneRecipe)
have no inner communications , you need to read your data directly from the shared class in the secondVc after the segue or send it in
self.performSegue(withIdentifier: "goToSecondVC", sender: <#Herererere#>)
and implement prepareForSegue
Let’s think about the order in which things are happening:
class APIsRuler {
static var oneRecipeFound: ((OneRecipeSearch) -> ())? // 1
}
class FirstVC: UIViewController {
func cellIsClicked(index: Int) {
APIsRuler.shared.getRecipe(from: recipeID) { (success, oneRecipe) in
if success, let oneRecipe = oneRecipe {
APIsRuler.oneRecipeFound?(oneRecipe) // 2
self.performSegue(withIdentifier: "goToSecondVC", sender: self)
}
}
}
}
Class SecondVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIsRuler.oneRecipeFound = { result in // 3
print(result)
}
}
}
oneRecipeFound starts out life empty: it is nil.
In FirstVC, the cell is clicked. We call oneRecipeFound. It is still nil, so nothing happens.
In SecondVC, we set the value of oneRecipeFound. Now it has a value, but the call has already happened.
So unless you have a time machine in your pocket, so that you can reverse that order of events somehow, the strategy you’ve outlined is doomed to failure. Of course, if you call oneRecipeFound after setting it, it will work. For example:
Class SecondVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
APIsRuler.oneRecipeFound = { result in
print(result)
}
APIsRuler.oneRecipeFound?(oneRecipe) // prints
}
}

Creating a selector with variable of function type

I am working on two views that are subclassing subclass of UITableViewCell. In the base one (subclass of UITableViewCell) I am trying to setup gesture recognizer in a way that each of super class could change the behavior (eventually call didTapped method on it's delegate) of the tap.
I have written following code. I can use #selector(tap), however I think that using a variable instead of overriding a tap method in each super class is a much cleaner way. Is it even possible to use something like #selector(tapFunc)? If no what would be the cleanest and best from engineering point of view solution?
class BaseCell: UITableViewCell {
#objc var tapFunc: () -> () = { () in
print("Tapped")
}
#objc func tap() {
print("TEST")
}
func setupBasicViews(withContent: () -> ()) {
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(tapFunc))
contentView.isUserInteractionEnabled = true
contentView.addGestureRecognizer(tapGestureRecoginzer)
}
}
And then two views that are building on top of this one:
class ViewA: BaseCell {
//don't want to do this
override func tap() {
//do stuff
}
func setup {
//setup everything else
}
class ViewB: BaseCell {
var delegate: ViewBProtocool?
func setup {
tapFunc = { () in
delegate?.didTapped(self)
}
//setup everything else
}
You're not too far off. Make the following changes:
class BaseCell: UITableViewCell {
var tapFunc: (() -> Void)? = nil
// Called by tap gesture
#objc func tap() {
tapFunc?()
}
func setupBasicViews(withContent: () -> ()) {
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(tap))
contentView.isUserInteractionEnabled = true
contentView.addGestureRecognizer(tapGestureRecoginzer)
}
}
class ViewA: BaseCell {
func setup() {
//setup everything else
}
}
class ViewB: BaseCell {
var delegate: ViewBProtocol?
func setup() {
tapFunc = {
delegate?.didTapped(self)
}
//setup everything else
}
}
Now each subclass can optionally provide a closure for the tapFunc property.
I show above that tapFunc is optional with no default functionality in the base class. Feel free to change that to provide some default functionality if desired.

Check if the user pass the value or it used the default optional parameter in Swift function

This is not a common case, I want to know if the user passed the value or it used the default value in Swift. For example
func test(firstThing: Int? = nil) {
if firstThing.isNil {
if // firstThing used the default value {
print("default")
} else {
print("nil ingresed")
}
}
}
It should have the following behaviour:
test() // "default"
test(firstThing: nil) // "nil ingresed"
For this you must write two function with same name. But in one function u set perimeter and in another without perimeter, for example :-
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.myFunc()
self.myFunc("demo")
// Do any additional setup after loading the view, typically from a nib.
}
func myFunc() {
print("empty")
}
func myFunc(_ str : String){
print(str)
}
}

Is self captured within a nested function

With closures I usually append [weak self] onto my capture list and then do a null check on self:
func myInstanceMethod()
{
let myClosure =
{
[weak self] (result : Bool) in
if let this = self
{
this.anotherInstanceMethod()
}
}
functionExpectingClosure(myClosure)
}
How do I perform the null check on self if I'm using a nested function in lieu of a closure (or is the check even necessary...or is it even good practice to use a nested function like this) i.e.
func myInstanceMethod()
{
func nestedFunction(result : Bool)
{
anotherInstanceMethod()
}
functionExpectingClosure(nestedFunction)
}
Unfortunately, only Closures have "Capture List" feature like [weak self]. For nested functions, You have to use normal weak or unowned variables.
func myInstanceMethod() {
weak var _self = self
func nestedFunction(result : Bool) {
_self?.anotherInstanceMethod()
}
functionExpectingClosure(nestedFunction)
}
Does not seem to be the case anymore. This is valid in swift 4.1:
class Foo {
var increment = 0
func bar() {
func method1() {
DispatchQueue.main.async(execute: {
method2()
})
}
func method2() {
otherMethod()
increment += 1
}
method1()
}
func otherMethod() {
}
}
The question remains: How is self captured ?