BatmansDelegate :: Beginner Tutorial focused solely on the Delegate Pattern - Batman Style!

image

TL;DR
AlfredViewController Source Code
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    lessonTopicTextField.text = nil
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }
    
    let batman = segue.destination as! BatmanViewController
    batman.delegate = self
    
    guard let lessonTopic = lessonTopicTextField.text else { return }
    batman.lessonTopic = 
      !lessonTopic.isEmpty ? lessonTopic : "The Delegate Pattern"
  }
  
}

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
}
BatmanViewController Source Code
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}


class BatmanViewController: UIViewController {
  
  // MARK: - Public API
  
  var lessonTopic: String?
  weak var delegate: BatmanViewControllerDelegate?
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)
  }
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    guard let lessonTopic = lessonTopic else { return }
    lessonTopicLabel.text = "Current lesson is on \(lessonTopic)"
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}

Screenshots:

User inputs topic, inputs new topic, touches Change Topic


Batman learns Swift!

User does not input topic, User does not input new topic, touches Bat-Signal


Alfred neglects to set a lesson topic, Batman cancels the lesson via Bat-Signal, Alfred bat-signal response


BatmansDelegate :: Beginner Tutorial Exploring the Delegate Pattern :: Part I

Batman and his Delegate, Alfred

Tutorial Time: 5-8 minutes. Actual time may vary based on InterfaceBuilder/Storyboard proficiency. This is a tutorial designed for beginners by a beginner, and for anyone who’d like to build a simple app designed around the Delegate Pattern for reference or practice.

Purpose: To create an app that exemplifies the delegate pattern with minimal storyboard/InterfaceBuilder design, and applies the delegate pattern to a pseudo real life analogy so that the steps/concepts can be recalled from memory, and eventually lead to mastery.

Disclaimer: This mini tutorial is not intended as a replacement of iOS Apprentice Chapter 13 “Delegates and Protocols”. Use this as supplemental material.

Note: This mini tutorial relies heavily on the assumption that you are able to recreate the application based upon the images & values provided - this is not a User Interface guide.
You should be able to accomplish this if you have completed up to Chapter 14, possibly Chapter 13 [Could someone please confirm?].

Possible Exceptions
  • The utilization of UIKit’s property Tint Colour - Chapter 19 “UI Improvements”, page 430 (but it is not necessary to build this app)
  • The utilization of UIStacks - This book doesn’t go into it, but there are videos on the site that shows you how (but it is not necessary to build this app)
    https://raywenderlich.com/tag/stack-view
  • Where guard statements are used I have provided the code for an if statement.

Scenario

  • Life Example A Student needs a Mentor to teach a Lesson Topic and respond to the Student’s needs.
  • In iOS ViewControllerB needs ViewControllerA to be it’s delegate so Data/Notifications can be sent to ViewControllerA from ViewControllerB

Pseudo Real Life Analogy:
Think of the Student as Batman or Bruce, and his Mentor as Alfred.

Remember it: (Who's Who?)

Batman starts with B: ViewControllerB.
Alfred starts with A: ViewControllerA.


Summary of the Delegate Pattern - Batman Style

BatmanViewController - Batman's Process
  • Batman requires a mentor, a delegate, who will be able to carry out the task of teaching in a way that will work with his superhero lifestyle.
  • Batman writes out his specific needs in a custom delegate protocol.
  • He then sets up a means to communicate these needs through a delegate object he creates.
  • Batman will be able to use the delegate object as a means to communicate his needs to his delegate during his lesson.

AlfredViewController - Alfred's Process
  • Alfred is destined to become Batman’s mentor, his delegate.
  • He conforms to Batman’s custom delegate protocol so he will be able to receive & respond to Batman’s communicated needs via Batman’s delegate object and it’s methods, because he will be of the BatmanViewControllerDelegate type.

While Batman is out fighting crime, their comlink goes down, and Alfred has to give his cell a ring - using their established segue connection, in prepare(for:sender:).

  1. When Alfred dials Batman, he makes sure that the segue identifier is the right one - else he’ll end up calling Catwoman, and word on the street is that she’s not looking for a delegate right now.
  2. Alfred reaches Batman’s voice mail - which indicates that his message will end up at the right destination - batman is assigned segue.destination as! BatmanViewController.
  3. Alfred finally is able to announce to Batman that he will be Batman’s Delegate! - batman.delegate = self

Make the BatmansDelegate App

Initial set up of Alfred & Batman ViewControllers

Set Up the New Project in Xcode

iOS Application: Single View App
Product Name: BatmansDelegate
Language: Swift
3 boxes unchecked

View AlfredViewController Source Code
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  }

}
View BatmanViewController Source Code
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class BatmanViewController: UIViewController {
  
// MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    navigationController?.popViewController(animated: true)
  }
  
  @IBAction private func cancelInstruction() {
    navigationController?.popViewController(animated: true)
  }
  
  // MARK: - ViewController Life Cycle
  
  override internal func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}


Alfred & Batman Scenes wired up with constraints using UIStackViews

Alfred Scene

Set Up

image
AlfredViewController with constraints

  • Open the device configuration pane (looks like View as: iPhone 8) and select iPhone SE
  • Set Simulator to be iPhone SE (upper left)
  • Identity Inspector → Custom Class: AlfredViewController
  • Embed AlfredViewController in Navigation Controller

Customize Navigation Controller

  • select Navigation Bar → checkmark Prefers Large Titles (page 245)

Drag in 1 of each:

  • Label
  • Button
  • Text Field

Set the following in Attributes Inspector:

  • Label: System 24.0
  • Button: System 17.0
  • Round Style Text Field: System 17.0
  • View Controller → Title: Alfred
  • Drag in: Navigation Item → Title: Alfred

Add Constraints

image
AlfredViewController with constraints

See Document Outline

image
AlfredViewController Document Outline

_Constrain to Margins_ Top Left Right Bottom
Label 15 20 20
Button 5 (to label) 20
RS Text Field 5 (to label) 20 5 (to button)

AutoLayout Error due to Content Size Ambiguity

To resolve the AutoLayout Error: Decrease horizontal hugging of “Round Style Text Field” from 250 to 249 to make it grow before other views (the button in this case)

There are 2 ways you can do this:

image
Resolve this in the Document Outline

image
Resolve in the Size Inspector of Round Style Text Field
Size Inspector → Content Hugging Priority → Horizontal: 249


Add Constraints using UIStackView <- Not Required

image
AlfredViewController with constrained stacks

Topic Stack View

  • Embed Round Style Text Field and Button in a UIStack
  • Axis: Horizontal stack
  • Alignment: Fill
  • Distribution: Fill Proportionally
  • Spacing 5

Message & Topic Stack View

  • Embed Label and Topic Stack View in a UIStack
  • Axis: Vertical stack
  • Alignment: Fill
  • Distribution: Fill Proportionally
  • Spacing 5

Set Round Style Text Field (75%) and Button (25%) of width of the UIStackView

  • Set Round Style Text Field and Button to equal widths
    (ctrl drag from Text Field to Button → Equal Widths
  • Select the equal widths constraint you just created
  • Equal Widths Constraint → Multiplier: 3
    image
    Change Equal Widths Constraint from 1 (equal), to 3
     
See Document Outline

image

_Constrain to Margins_ Top Left Right Bottom
Message & Topic Stack View 15 20 20

Wire Connections

Wire Label to outlet messageLabel
Set text: Time for your lesson, Master Bruce.
Lines: 0

Wire Round Style Text Field to outlet lessonTopicTextField
Set Placeholder Text: Lesson Topic

Button is already set up via ActionSegue: Show
Set Default Title: Teach

See Wired AlfredViewController

image
Wired AlfredViewController

 

image
Wired AlfredViewController - UIStackView ← Not Required

See Document Outline

image
AlfredViewController Document Outline

 

image
AlfredViewController Document Outline - UIStackView ← Not Required


Batman Scene

Set Up

image
BatmanViewController with constraints

  • Drag in: ViewController → Custom Class: BatmanViewController
  • Segue from Alfred’s Button to Batman → ActionSegue: Show

Drag in 1 of each

  • Label
  • Text Field

Set the following in Attributes Inspector

  • Label: System 24.0
  • Round Style Text Field: System 17.0
  • View Controller → Title: Batman
  • Drag in: Navigation Item → Title: Batman
  • Drag in: 2 Bar Button Items onto the Navigation Item
    Left Bar Button Item → Title: Bat-Signal
    Right Bar Button Item → Title: Change Topic
  • Segue → Identifier: TeachLessonTopic

Add Constraints

image
BatmanViewController with constraints

Constrain to margins for all choices

See Document Outline

image

_Constrain to Margins_ Top Left Right Bottom
Label 15 20 20
RS Text Field 10 (to label) 20 20

Add Constraints using UIStackView <- Not Required

image
BatmanViewController with constrained stacks

New Topic Stack View

  • Embed Label and Round Style Text Field in a stack
  • Axis: Vertical stack
  • Alignment: Fill
  • Distribution: Fill
  • Spacing 10
     
See Document Outline

image

_Constrain to Margins_ Top Left Right Bottom
New Topic Stack View 15 20 20

Wire Connections

Wire Label to outlet lessonTopicLabel
Set text: Current Lesson is on Lesson Topic
Lines: 0

Wire Round Style Text Field to outlet newLessonTopicTextField
Set Placeholder Text: New Lesson Topic

Wire Left Bar Button Item: Bat-Signal to action cancelInstruction
Wire Right Bar Button Item: Change Topic to action changeLessonTopic

See BatmanViewController

image
Wired BatmanViewController

 

image
Wired BatmanViewController - UIStackView ← Not Required

See Document Outline

image
BatmanViewController Document Outline

 

image
BatmanViewController Document Outline - UIStackView ← Not Required


Build and Run BatmansDelegate :: #1

Build the app, your screens should look identical to the iPhone Screen Shots provided and function as described in Current State Functionality

Current State Functionality

Actions
Teach segues to BatmanViewController
Bat-Signal unwinds to AlfredViewController
Change Topic unwinds to AlfredViewController

Placeholder text
Lesson Topic enter text
New Lesson Topic enter text


iPhone Screen Shots


Constraints applied, Segues and Actions wired

 


Wired Outlets


Delegate

Delegate Pattern:
iOS Apprentice Chapter 13 “Delegates and Protocols” page 287 summarizes the delegate pattern and provides visual representations of ViewControllerA & ViewControllerB, this tutorial complements these diagrams.

Remember: (Delegate Definition)

Delegate: A person acting for another. [Source: merriam-webster.com]
Think of a delegate as an appointed/chosen object that does work for another object.

Delegate Pattern Relationship:

  • Life Alfred is Batman’s mentor / Alfred is the mentor of Batman
  • iOS VCA is VCB’s delegate / VCA is the delegate of VCB

Delegate Protocol:
iOS Apprentice Chapter 13 “Delegates and Protocols” page 289 summarizes what the delegate protocol is, and touches on protocols in general.

  • Life Batman appoints Alfred to be his mentor, so Alfred can teach/respond to Batman.
  • iOS VCB appoints VCA to be it’s delegate, so VCA can send/recieve Data to/from VCB.

BatmanViewController

Summary of Batman's Process - Batman Style

A copy of the process was placed here for connivence

  • Batman requires a mentor, a delegate, who will be able to carry out the task of teaching in a way that will work with his superhero lifestyle.
  • Batman writes out his specific needs in a custom delegate protocol.
  • He then sets up a means to communicate these needs through a delegate object he creates.
  • Batman will be able to use the delegate object as a means to communicate his needs to his mentor/delegate during his lesson.

Batman’s Delegate Protocol
Batman’s ideal mentor (delegate):

  1. Delegate must change the lesson to a different topic per Batman’s request.
  2. Delegate must realize that Batman canceled the lesson early due to the Bat-Signal.

Place this code at the top of BatmanViewController right above the class definition. (page 289)

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}

Batman’s Delegate Variable:
Batman needs a way to communicate the following to his delegate:

  1. Batman changed the lesson topic, and needs to let the delegate know what that new lesson topic is.
  2. Batman needs to go due to the Bat-Signal, and needs his delegate to cancel the lesson.

Batman needs to create an object of type BatmanViewControllerDelegate? through which his delegate will be able to call Batman’s delegate methods and effectively receive the messages Batman communicated.

  // MARK: - Public API
 
  weak var delegate: BatmanViewControllerDelegate? 
//weak var delegateAlfred  <- change the name to this if it helps!

In the changeLessonTopic() action, Batman’s new lesson topic is set up to be passed to his delegate by means of his batmanViewController(_:didChangeLessonTopic:) delegate method.

  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)

    navigationController?.popViewController(animated: true)
  }
See guard let <-> if let code

The author touches briefly on guard on page (490)
guard let -assignment- else { return } is similar to if let -assignment- {}

// Same code as above
if let lessonTopic = newLessonTopicTextField.text {
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
}
navigationController?.popViewController(animated: true)

In the cancelInstruction() action, Batman leaves to go fight crime due to the Bat-Signal. He communicates this departure via his delegate method batmanViewControllerCancel() which can be synonymous with cancel().
More about cancel() on page 291.

  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)

    navigationController?.popViewController(animated: true)
  }

Batman has successfully set up a way to communicate his needs/any changes in self, to his delegate object to ensure his mentor lessons will go smoothly.
It’s up to the object that decides to take on the responsibly of being Batman’s delegate to take the final steps.


Build and Run BatmansDelegate :: #2

Build the app to make sure it still works.

Current State Functionality :: No changes since last build

Actions
Teach segues to BatmanViewController
Bat-Signal unwinds to AlfredViewController
Change Topic unwinds to AlfredViewController

Placeholder text
Lesson Topic enter text
New Lesson Topic enter text


iPhone Screen Shots :: No changes since last build


Constraints applied, Segues and Actions wired

 


Wired Outlets


View AlfredViewController Source Code :: No changes since last build
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  }

}
View BatmanViewController Source Code
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}


class BatmanViewController: UIViewController {
  
  // MARK: - Public API
  
  weak var delegate: BatmanViewControllerDelegate?
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)

    navigationController?.popViewController(animated: true)
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)

    navigationController?.popViewController(animated: true)
  }
  
  // MARK: - ViewController Life Cycle
  
  override internal func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}

1 Like

BatmansDelegate :: Beginner Tutorial Exploring the Delegate Pattern :: Part II

AlfredViewController

Summary of Alfred's Process - Batman Style

A copy of the process was placed here for connivence

  • Alfred is destined to become Batman’s mentor, his delegate.
  • He conforms to Batman’s custom delegate protocol so he will be able to receive & respond to Batman’s communicated needs via Batman’s delegate object and it’s methods, because he will be of the BatmanViewControllerDelegate type.

While Batman is out fighting crime, their comlink goes down, and Alfred has to give his cell a ring - using their established segue connection, in prepare(for:sender:) takes these final 3 steps.

  1. When Alfred dials Batman, he makes sure that the segue identifier is the right one - else he’ll end up calling Catwoman, and word on the street is that she’s not looking for a delegate right now.
  2. Alfred reaches Batman’s voice mail - which indicates that his message will end up at the right destination - batman is assigned segue.destination as! BatmanViewController.
  3. Alfred finally is able to announce to Batman that he will be Batman’s Delegate! - batman.delegate = self

Alfred Adopts Batman’s Delegate Protocol
Alfred knows he is the ideal mentor that Batman seeks. Alfred must adopt the BatmanViewControllerDelegate protocol which will make him Batman’s delegate type and be able to use BatmanViewControllerDelegate methods as his own - because now they are.

  • Alfred conforms to Batman’s BatmanViewControllerDelegate Protocol

In an extension on AlfredViewController, conform to the BatmanViewControllerDelegate protocol by adding it’s delegate methods.

class AlfredViewController: UIViewController {
...
}

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {

  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    <#code#>
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    <#code#>
  }
  
}
View reason for extension

Note You have the option to set it up like it’s shown in the book:
class AlfredViewController: BatmanViewControllerDelegate {...}.

Swift Apprentice Chapter 18: “Access Control and Code Organization”
Access Control It’s best practice to write loosely coupled code (delegate pattern) and highly cohesive code (delegate methods). Take note that most of my properties and methods are marked with the keyword private.
Code Organization Create extensions based on protocol conformance. AlfredViewController’s class properties and methods remain separate from the BatmanViewControllerDelegate methods.

If you don’t already own Swift Apprentice, you can pick up this amazing book in the Store.

If you find that Swift Apprentice is too advanced

There may be an intermediate step you must take to ready you for Swift Apprentice

Right now To get the most out of iOS Apprentice I suggest referring to Apple’s free iBook The Swift Programming Language every time you encounter a new concept.

Next Steps After iOS Apprentice I suggest reading Apple’s free iBook series Everyone Can Code: Intro to App Development with Swift & App Development with Swift. They are tutorial books that will have you create many apps that are much smaller than the ones in iOS Apprentice. Apple does a phenomenal job at teaching you the Swift Language as you go.

Must Read You should now be ready for Swift Apprentice if you weren’t before. This book is a must read if you wish to take iOS design seriously. The amount of information can be overwhelming, and getting through a chapter can take days, but it is a necessary step in you journey toward Swift mastery, if that is your intended goal.


In batmanViewContoller(_:didChangeLessonTopic:) Alfred will display Batman’s desired lessonTopic via messageLabel.

Popping the BatmanViewController off the stack via popViewController(animated:) is handled here now, so delete it from BatmanViewController.

  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."

    navigationController?.popViewController(animated: true) // Handled here now
  }
View this delegate method's definition in BatmanViewController
@IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }

    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
  }

In batmanViewContollerCancel(_:) Alfred displays a message when he is notified of Batman’s absence due to the Bat-Signal.

popViewController(animated:) is also handled here so delete it from BatmanViewController as well.

  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."

    navigationController?.popViewController(animated: true) // Handled here now
  }
View this delegate method's definition in BatmanViewController
  @IBAction private func cancelInstruction {
    delegate?.batmanViewControllerCancel(self)
  }

Build and Run BatmansDelegate :: #3

Build the app to discover what no longer works as it should.

Current App Functionality

Changes since last build are in bold

Actions
Teach segues to BatmanViewController
Bat-Signal Does Nothing
Change Topic Does Nothing

Placeholder Text
Lesson Topic enter text
New Lesson Topic enter text


iPhone Screen Shots


Alfred attempts to teach a lesson topic, Batman attempts to change topic & leave via bat-signal


Batman’s Bar Button Items are Broken?!
They aren’t broken per say but they aren’t unwinding to Alfred like they were before.
The removal of popViewController(animate:) within BatmanViewController has rendered Batman’s UIBarButtonItems Bat-Signal and Change Topic to no longer be able to unwind to Alfred.

Locations of popViewController(animate:) in BatmanViewController
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)

->  navigationController?.popViewController(animated: true) // Remove this line
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)

->  navigationController?.popViewController(animated: true) // Remove this line
  }

By moving these methods into AlfredViewController, Batman has appointed his delegate the job of popping the BatmanViewController off the stack.
Alfred has yet to take the the necessary steps in becoming Batman’s delegate, and therefore will be unable to do this job until then.

View AlfredViewController Source Code
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  }

}

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."

    navigationController?.popViewController(animated: true)
  }
  
}
View BatmanViewController Source Code
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}


class BatmanViewController: UIViewController {
  
  // MARK: - Public API
  
  weak var delegate: BatmanViewControllerDelegate?
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)
  }
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}

Becoming Master Bruce’s Delegate

Final Steps Summary of Alfred's Process - Batman Style

While Batman is out fighting crime, their comlink goes down, and Alfred has to give his cell a ring - using their established segue connection, in prepare(for:sender:) takes these final 3 steps.

  1. When Alfred dials Batman, he makes sure that the segue identifier is the right one - else he’ll end up calling Catwoman, and word on the street is that she’s not looking for a delegate right now.
  2. Alfred reaches Batman’s voice mail - which indicates that his message will end up at the right destination - batman is assigned segue.destination as! BatmanViewController.
  3. Alfred finally is able to announce to Batman that he will be Batman’s Delegate! - batman.delegate = self

Batman attempts to communicate with his mentor via the bar buttons Bat-Signal and Change Topic with no luck because Alfred isn’t officially his delegate yet. While the Dark Knight is out fighting crime, their comlink goes dark and Alfred has no choice but to use the telephone as a means for communication. It’s been a while since he’s called Batman, so Alfred makes sure that he is dialing the correct number before he can communicate further.

AlfredViewController

Step 1 In prepare(for:sender:) Alfred uses the ActionSegue that was previously set up between himself and Batman and confirms that the segue.identifier is correct. The guard statement will guard against him making a mistake and dialing Catwoman instead, - word on the street is that she isn’t looking for a mentor right now, nor would he want to be.

// MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }     // step 1

                                                                     // step 2
                                                                     // step 3
  }
See guard <-> if code

The author touches briefly on guard on page (490)
guard -condition- else { return } is similar to if -condition- { }

// Same code as above

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let segue.identifier == "TeachLessonTopic" {         // step 1
                                                            // step 2
                                                            // step 3
    }
  }

Step 2 Alfred reaches Batman’s voice mail, confirmation that he dialed the correct number. Now he must declare that his intended message will be destined for Batman and for Batman alone as! BatmanViewController.

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }     // step 1

    let batman = segue.destination as! BatmanViewController          // step 2
                                                                     // step 3
  }

Step 3 It’s finally time for Alfred to officially become Batman’s Delegate!
Message to Batman Alfred declares him(self) as Batman’s Delegate!

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }     // step 1

    let batman = segue.destination as! BatmanViewController          // step 2
    batman.delegate = self                                           // step 3
  }

Build and Run BatmansDelegate :: #4

Build the app to discover what now works as it should.

Current App Functionality

Changes since last build are in bold

Actions
Teach segues to BatmanViewController
Bat-Signal unwinds to AlfredViewController :: prompts Alfred’s bat-signal response
Change Topic unwinds to AlfredViewController :: prompts Alfred’s changed topic response

Placeholder Text
Lesson Topic enter text
New Lesson Topic enter text :: entered text is passed to Alfred via Change Topic


iPhone Screen Shots


Alfred attempts to teach a lesson topic, Batman suggests change topic, Alfred changed topic response

 

image
Batman cancels the lesson via Bat-Signal and Alfred bat-signal response is presented


Batman’s Bar Button Items are Working Again!
Alfred has taken the necessary steps in becoming Batman’s delegate, and therefore is able to do the job Batman appointed his delegate to do:

  1. Pop BatmanViewController off the stack when he suggests Change Topic or cancels the lesson due to the Bat-Signal.
  2. Set Batman’s new lesson topic as the current lesson topic being taught.
  3. Give Batman a proper response when he cancels the lesson in order to go clean the streets of Gotham.
View AlfredViewController Source Code
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }

    let batman = segue.destination as! BatmanViewController
    batman.delegate = self 
  }

}

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."

    navigationController?.popViewController(animated: true)
  }
  
}
View BatmanViewController Source Code :: No changes since last build
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}


class BatmanViewController: UIViewController {
  
  // MARK: - Public API
  
  weak var delegate: BatmanViewControllerDelegate?
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)
  }
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}

Batman Prepares for Lesson Time

The current state of the app has Batman being able to send data to his delegate Alfred, but not the other way around.

  • Batman can input a new lesson topic and Change Topic - Alfred responds accourdingly, mentioning the lesson topic by name.
  • Batman also can leave via Bat-Signal and Alfred will respond accourdingly, mentioning he will he will keep his tea warm.

Even if Alfred was able to send the lessonTopic to Batman, Batman would have no place to store it. He needs to set up an instance variable that will store the LessonTopic that Alfred will send, and then he will be ready for the lesson and be able to respond accordingly.

BatmanViewController
in BatmanViewController add the instance var lessonTopic to the Public API section above weak var delegate: BatmanViewControllerDelegate?

  // MARK: - Public API
  
  var lessonTopic: String?

Batman’s lessonTopicLabel.text response is currently set in Interface Builder as the string "Current Lesson is on Lesson Topic"
In viewDidLoad() set a new response that will incorporate Alfred’s proposed lessonTopic using string interpolation.

  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    guard let lessonTopic = lessonTopic else { return }
    lessonTopicLabel.text = "Current lesson is on \(lessonTopic)"
  }

Notice a Pattern?
Batman has successfully set up a way to communicate with his his delegate object to ensure his mentor lessons will go smoothly.
It’s up to Alfred prepare the final steps.

See Build #5 for BatmanViewController’s final Source Code


Lesson Time for Master Bruce

Now that Batman is 100% ready to learn a given topic, Alfred can prepare to send the lessonTopic data to him via segue.

At the bottom of prepare(for:sender:) set Alfred’s lessonTopicTextField.text to Batman’s lessonTopic, and provide a default lesson topic string value should Alfred neglect to set one.
Note: lessonTopicTextField.text is currently unwrapped and assigned to the local constant lessonTopic.
This is not to be confused with Batman’s global instance lessonTopic, to which you are assigning the text from Alfred’s lessonTopic to.

  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return } 
    ...

    guard let lessonTopic = lessonTopicTextField.text else { return }
    batman.lessonTopic = 
      !lessonTopic.isEmpty ? lessonTopic : "The Delegate Pattern" // ternary condition operator
See ternary condition operator <-> if else code

Author touches on using the ternary condition operator (page 415)

// same as above

if !lessonTopic.isEmpty {
  batman.lessonTopic = lessonTopic
} else {
  batman.lessonTopic = "The Delegate Pattern"
}

Add this final bit of code to Alfred’s ViewController Life Cycle section (right above viewDidAppear().
It will clear the initial lesson topic text input every time the Bat-Signal or Change Topic is touched.
To see the change, compare this build #5, to #4’s iPhone Screen Shots, take note that “iOS” remains in Alfred’s lessonTopicTextField the entire time.

  // MARK: - ViewController Life Cycle
  
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    lessonTopicTextField.text = nil
  }

Build and Run BatmansDelegate :: #5

The app is complete and everything should work as intended

Current App Functionality

Changes since last build are in bold
Actions
Teach segues to BatmanViewController :: sends Alfred’s proposed lesson topic
Bat-Signal unwinds to AlfredViewController :: prompts Alfred’s bat-signal response
Change Topic unwinds to AlfredViewController :: prompts Alfred’s changed topic response

Placeholder Text
Lesson Topic enter text :: entered text is passed to Batman via Teach
New Lesson Topic enter text :: entered text is passed to Alfred via Change Topic


iPhone Screen Shots


Alfred proposes a lesson topic, Batman changes topic, Alfred changed topic response

 


Alfred neglects to set a lesson topic, Batman cancels the lesson via Bat-Signal, Alfred bat-signal response

View AlfredViewController Source Code :: Final
//
//  AlfredViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

class AlfredViewController: UIViewController {
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var messageLabel: UILabel!
  @IBOutlet weak private var lessonTopicTextField: UITextField!
  
  // MARK: - ViewController Life Cycle
  
  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    lessonTopicTextField.text = nil
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
  // MARK: - Navigation
  
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    guard segue.identifier == "TeachLessonTopic" else { return }
    
    let batman = segue.destination as! BatmanViewController
    batman.delegate = self
    
    guard let lessonTopic = lessonTopicTextField.text else { return }
    batman.lessonTopic = !lessonTopic.isEmpty ? lessonTopic : "The Delegate Pattern"
  }
  
}

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
}
View BatmanViewController Source Code :: Final
//
//  BatmanViewController.swift
//  BatmansDelegate
//
//  Created by h1i2j3 on 5/25/18.
//  Copyright © 2018 h1i2j3. All rights reserved.
//

import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}


class BatmanViewController: UIViewController {
  
  // MARK: - Public API
  
  var lessonTopic: String?
  weak var delegate: BatmanViewControllerDelegate?
  
  // MARK: - UI Connections
  
  @IBOutlet weak private var lessonTopicLabel: UILabel!
  @IBOutlet weak private var newLessonTopicTextField: UITextField!
  
  @IBAction private func changeLessonTopic() {
    guard let lessonTopic = newLessonTopicTextField.text else { return }
    delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic)
  }
  
  @IBAction private func cancelInstruction() {
    delegate?.batmanViewControllerCancel(self)
  }
  
  // MARK: - ViewController Life Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    guard let lessonTopic = lessonTopic else { return }
    lessonTopicLabel.text = "Current lesson is on \(lessonTopic)"
  }
  
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
  
}

Paint it Black :: UI Improvements

Navigation Controller
image

Select the Navigation Bar in the Document Outline

Set in File Inspector:

  • Interface Builder Document → Global Tint: Yellow
    (red 255, green 251, blue 0; Hex# FFFB00)

Note: The author shows how to do this on page (431)

Set the following in Attributes Inspector:

  • Navigation Bar → Bar Tint: Black
    (red 0, green 0, blue 0; Hex# 000000)

  • Large Title Text Attributes → Title Colour: White
    (red 255, green 255, blue 255; Hex# FFFFFF)

  • View → Tint: Default (colour of box should be Yellow)


Alfred Scene
image

Set in Attributes Inspector

Select Message Label

  • Label → Colour: White

Select View

  • View → Background: Black

image image


Batman Scene
image

Select Lesson Topic Label

In Attributes Inspector

  • Label → Colour: White

Select View

  • View → Background: Black
    image image

2 Likes

How to handle optional methods in custom delegates ?

1 Like

Awesome suggestion!
Before your message I’d never heard of optional methods, but I managed to make this code work. If someone more experienced could check this, fix this or perhaps expand on it that would be appreciated.

In BatmanViewController I modified the delegate protocol:

  • added keyword @objc to the protocol
  • added @objc optional func batmanViewController(_:didAddLessonTopic:) to the bottom

The idea is to allow Alfred the option of teaching both his lesson topic and Batman’s suggested lesson topic - rather than just setting the topic to Batman’s choice.

BatmanViewController

import UIKit

/// The delegate of a `BatmanViewController` object must adopt the 
/// `BatmanViewControllerDelegate` protocol.
@objc protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate to dismiss the specified controller
  func batmanViewControllerDismiss(_ controller: BatmanViewController)
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate that lessonTopic should be added to existing lessonTopic.
  @objc optional func batmanViewController(_ controller: BatmanViewController, 
                          didAddLessonTopic lessonTopic: String)
}
Original Protocol for comparison
import UIKit

// MARK: - BatmanViewControllerDelegate Protocol

/// The delegate of a `BatmanViewController` object must adopt the
/// `BatmanViewControllerDelegate` protocol.
protocol BatmanViewControllerDelegate: class {
  
  /// Tells the delegate that the lessonTopic was changed by the user/Batman.
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String)
  
  /// Tells the delegate to cancel changes to the specified controller.
  func batmanViewControllerCancel(_ controller: BatmanViewController)
}

In the body of BatmanViewController I added batmanViewController!(_:didAddLessonTopic:) and commented out the original.

  @IBAction private func notifyAction() {
    guard let lessonTopic = lessonTopicTextField.text else { return }

    delegate?.batmanViewController!(self, didAddLessonTopic: lessonTopic) // added this!
//  delegate?.batmanViewController(self, didChangeLessonTopic: lessonTopic) // original
  }

AlfredViewController

// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    // Moved into Optional Method
  }

  // Optional Method
  func batmanViewController(_ controller: BatmanViewController, 
           didAddLessonTopic lessonTopic: String) {
    guard let initialLessonTopic = lessonTopicTextField.text else { return }
    messageLabel.text = "Teaching \(initialLessonTopic) & \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
}
Original delegate method definitions for comparison
// MARK: - BatmanViewController Delegates

extension AlfredViewController: BatmanViewControllerDelegate {
  
  func batmanViewController(_ controller: BatmanViewController,
        didChangeLessonTopic lessonTopic: String) {
    messageLabel.text = "Teaching \(lessonTopic) as requested, Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
  func batmanViewControllerCancel(_ controller: BatmanViewController) {
    messageLabel.text = "I'll keep the tea warm until you return Master Bruce."
    
    navigationController?.popViewController(animated: true)
  }
  
}

I realize that the body of batmanViewController(_:didChangeLessonTopic:) could have been modified to do the same effect. Right now I’m not able to come up with a clever way to incourporate an optional method into such a simple app - nor do I know for sure this is correct.

I hope I’m on the correct path and it has helped you a bit. And I apologize if this info is wrong.

1 Like

I have an error occurring in tableview all the time. The DidSelect function is not working on the first click ,only the cell selection colour is changing…also tableview did not detecting the second click on the same cell, but when I click on other cells the select action of the colour changed(first clicked) cell is working.

please get back me in my personal email : jithinspegatechekm@gmail.com

First, since this thread has nothing to do with the issue you posted about, you might want to start a new thread :slight_smile:

Second, asking people to respond via e-mail rather than on the forums might seem a liiiiiiitle bit inconsiderate :wink:

Third, it sounds as if you might be implementing the didDeselect delegate method rather than the didSelect delegate method. So check your code again - that mistake is pretty easy to make.

1 Like

This is great work Rebecca. Learned a lot from your analogy. Interestingly, we’re both going through iOS Apprentice and I’m also at Chp20.

1 Like

@fahim Hats off to you sir, for an amazing book. I’m currently following your MyLocations app tutorial (chapter 23 “Use Location Data”) and learning so much. This is my first experience with the Core Location framework, and what works for me is keeping the documentation open to refer to the CLLocationManager class to make sense of it all - it is a ton of new information for me.
Awesome tutorial so far, I really like your choice of helper method names :+1: , naming doesn’t come easy for me so I’m always happy to see some examples from the pros. Also, much respect to you for your tutorials, having just completed a tiny tutorial that took about a week to complete, I can’t even begin to imagine the amount of time and dedication it took for this book… it’s over 1000 pages! Amazing and thank you for this book.

I was wondering if you could explain why the ternary conditional operator works in this spot, and nil-coalescing won’t in my tutorial:
In AlfredViewController prepare(for:any:)

guard let lessonTopic = lessonTopicTextField.text else { return }
batman.lessonTopic = lessonTopicTextField.text ?? “The Delegate Pattern”  // doesn’t work
batman.lessontTopic = lessonTopic != nil ? lessonTopic! : “The Delegate Pattern” // works!

lessonTopicTextField.text should be String?, and in viewWillAppear(animated:) I have it set to nil. When the user doesn’t input any text, lessonTopicTextField.text should be nil and the nil-coalescing operation should work, but it doesn’t. Any idea why?


@dreyes Cheers David! That’s awesome! I started with a Student / Instructor relationship to help myself conceptualize the delegate pattern, but the analogy didn’t fit perfectly and from it grew into this. I feel like I learned so much in making this tutorial, making it really made me understand it. I’m so happy it helped you!
@jithin_k mentioned Optional Methods and I found a mention of them in this iOS Apprentice book in Chapter 31: “Polishing the App” on page 750 and I resorted to the UITableViewDelegate in Apple’s Documentation in Xcode (it’s my go to for understanding delegate matters). Xcode’s ‘Fix It’ helped with figuring out the rest in terms of @objc.
Also, in general, I found that looking at UITextFieldDelegate’s methods helps with argument label naming conventions for delegate methods. :+1:
I’m actually on Section 3: MyLocations app chapter 23 Use Location Data, (you’ll breeze through chapter 21 since you’ve read Swift Apprentice, it’s an excellent review resource for Swift - I was really excited to learn about if statements with an early return (page 490)! I prefer using guard/guard let where I can, but I will def start implementing more if’s with early returns… neat!
It’s a good feeling to know we’re learning iOS Apprentice at the same pace.
What do you think will be your next book?

Thank you for the kind words about the book :slight_smile: Most of the credit goes to @matthijs since he did the hard work. I’m simply continuing what he started.

Regarding your question, there are a few things you might want to consider about the above code snippet:

  1. Since you do a guard statement on lessonTopicTextField.text on the first line, your second and third lines would never be executed for cases where the text is nil :slight_smile: That is not actually the error you’re seeing with the code (I don’t know what the actual error is since you didn’t mention it) but any checks for the text after the guard statement would only work if the text is not nil and so, your text assignment on line 2 would never happen.

  2. I believe what you might be looking for is this:

    guard let topicField = lessonTopicTextField else { return }
    batman.lessonTopic = topicField.text ?? “The Delegate Pattern”
    

    However, the above supposes that you had defined lessonTopicTextField as an optional or a force unwrapped optional. I don’t know if that is the case or not since there’s a fair amount of code to go through in this thread and I have not looked through it all.

  3. The third line works (I assume the compiler complains otherwise) because you actually have a non-nil string by the time you get there. So you can’t do a nil-coalescing operation at that point. However, checking for nil can be done anywhere - but since you’ve already passed the guard statement (see #1), you actually will never hit the nil branch of this condition.

Hope that makes sense?

sorry, I asked her to respond in my personal email only because I can send the project for further checking…nothing else.
I could not fix the problem…its occurring only in a single view controller, working fine in other view controller. So I created a new view controller.

Thanks for your consideration :slight_smile:

You can always simply create a ZIP file of the project, upload it somewhere and provide a link to the file here. That way, more than one person can take a look (if they are so inclined) and everybody who reads the forum posts can benefit from the discussion.

Nice to talk to you dear. You people are really helpful here.
I think its not too bad to know someone in personal. Anyways I will keep in touch with you all. I am just a new face in this forum, thanks for all your support and consideration.

This topic was automatically closed after 166 days. New replies are no longer allowed.