Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on October 25, 2019

Basic iOS tutorial : Delegation Pattern

iOS & Swift

Contents

  • Chuẩn bị
  • 1. Vấn đề
    • 1.1. Vấn đề 1
    • 1.2. Vấn đề 2
  • 2. Delegation Pattern
    • 2.1. Giải thích
    • 2.2. Sử dụng
  • 3. Delegation trong View
    • 3.1. UI Control cơ bản
    • 3.2. Custom View
  • 4. DataSource
  • Tạm kết

Chào mừng bạn đã quay lại với seri Lập trình iOS cho mọi người.

Bài viết này sẽ đề cập tới 1 chủ đề khá kinh điển và sử dụng nhiều trong iOS. Đó là Delegation Pattern , hay gọi theo tên dân gian là Delegate & DataSource. Trước tiên thì bạn cần phải nắm được các bài cơ bản sau:

  • Basic Swift trong 10 phút
  • Protocol trong 10 phút

Vì bản chất của Delegate thì là 1 Protocol, nên bạn phải thành thạo Protocol trước đã. Bước tiếp theo là áp dụng mô hình Delegation vào trong lập trình iOS.

Bắt đầu thôi!

Chuẩn bị

  • MacOS 10.14.4
  • Xcode 11.0
  • Swift 5.1

1. Vấn đề

1.1. Vấn đề 1

Đây là một vấn đề khá là đau đầu trong lập trình.

  • Tại View A, có 2 chức năng
    • Mở View B
    • Đóng View B
  • Tại View B, có 2 chức năng
    • Mở View A
    • Đóng View A

Giải quyết cho vấn đề này thì phải

  • Tạo con trỏ B trong A
  • Tạo con trỏ A trong B

Khi đó thì tại mỗi View sẽ điều kiển được View kia và ngược lại. Nhưng vấn đó lại phát sinh khi close hay huỷ đối tượng con trỏ kia. Dẫn tới deadlock

1.2. Vấn đề 2

Vấn đề này là quan hệ cha-con giữa các đối tượng:

  • Tại View A, việc tạo đối tượng con (sub-view A) và truyền giá trị cho thuộc tính của nó (data) thì rất đơn giản.
    • Sub-view A tạo ra và View A xét giá trị cho property của Sub-View A tại đó
  • Tại Sub-View A, việc trả giá trị về View A thì không đơn giản như vậy
    • Vì lúc này việc thực thi đang nằm tại function của Sub-View A và không có con trỏ của View A (con trỏ cha)

Giải quyết vấn đề này thì có thể áp dụng phương pháp trên để giải quyết. Tạo con trỏ cha trong đối tượng con. Và nó cũng gặp phải khó khăn như trên.

Bây giờ ta phải tìm cách giải quyết tối ưu hơn.

2. Delegation Pattern

Đầu tiên, delegate là một design pattern dùng để truyền dữ liệu giữa các class hoặc struct. Từ “delegate” ở đây có nghĩa là ủy quyền, uỷ thác, và để dễ hiểu hơn thì sẽ sử dụng những từ ngữ tương tự.

Ta phải giải quyết được 2 điểm:

  1. Truyền dữ liệu giữa các đối tượng
  2. Tránh việc deadlock khi các đối tượng release

2.1. Giải thích

  • Khai báo 1 Protocol P
    • Khai báo các function của P
  • Tại View A thì implement P. Khi đó:
    • A đã kế thừa thêm P
    • Có thể hiểu A có thêm 1 kiểu dữ liệu nữa là P
    • A định nghĩa lại các function mà đã được khai báo tại P
  • Tại Sub-View A, tại đó:
    • Khai báo 1 property là p (có thể tên đặt delegate) với kiểu là P
  • Tạo đối tượng Sub-View A
    • Thực thi tại View A
    • Các thuộc tính Sub-View A sẽ được View A gán giá trị cho
    • Trong đó, gán p = View A (hay là self) được chấp nhận vì View A đã implement Protocol P và xem như View A có kiểu dữ liệu là P
    • Đây chính là chiều truyền dữ liệu thứ nhất: Từ cha sang con
  • Return Data
    • Thực thi tại Sub-View A
    • Sử dụng thuộc tính p (đang là thuộc tính trong class Sub-View A) để gọi các function của nó (đã khai báo tại Protocol P)
    • Dữ liệu sẽ truyền vào các tham số của các function được khai báo tại Protocol P
    • Đây chính là chiều truyền dữ kiệu thứ hai: Từ con sang cha
  • Vì p chính là View A, nên
    • p gọi function của nó với các đối số là dữ liệu từ Sub-View A
    • cũng chính là View A gọi các function mà đã implement Protocol P, với các đối số là dữ liệu từ Sub-View A

Về bản chất p cũng chính là con trỏ View A nên sẽ thực thi được các function mong muốn và dữ liệu được truyền đi dựa theo việc gán các đối số cho các function mong muốn đó.

2.2. Sử dụng

  • Đâu tiên sử dụng Protocol để khai báo các Delegate.
  • Bước 1: Khai báo Delegate
protocol SubViewADelegate {
    func passData(data: String)
}
  • Bước 2: tạo các class View A và Sub-View A
//MARK: Class View A
class ViewA {
    init() { }
}

//MARK: Class Sub-View A
class SubViewA {
    init() { }
}
  • Bước 3: khai báo delegate cho Sub-View A
    • Delegte phải được khai báo là optional
//MARK: Class Sub-View A
class SubViewA {
    var delegate: SubViewADelegate?
    
    init() { }
}
  • Bước 4: Implement Protocol cho View A
    • Định nghĩa lại các function của Delegate
class ViewA: SubViewADelegate {
    init() { }
    
    //MARK: SubviewA Delegate
    func passData(data: String) {
        print("Data: \(data)")
    }
}
  • Bước 5: thực thi truyền dữ liệu từ Sub-View A sang View A
//MARK: Do something
var viewA = ViewA()

var subViewA = SubViewA()
subViewA.delegate = viewA
//pass data
subViewA.delegate?.passData(data: "do something")
  • Kết quả

Làm đẹp một chút cho nó nhìn chuyên nghiệp hơn.

//MARK: Define delegate
protocol SubViewADelegate {
    func passData(data: String)
}

//MARK: Class View A
class ViewA {
    init() { }
}
//SubviewA Delegate
extension ViewA: SubViewADelegate {
    func passData(data: String) {
        print("Data: \(data)")
    }
}

//MARK: Class Sub-View A
class SubViewA {
    var delegate: SubViewADelegate?
    
    init() { }
    
    // action
    func doSomething(data: String) {
        if let delegate = delegate {
            delegate.passData(data: data)
        }
    }
}

//MARK: Do something
var viewA = ViewA()

var subViewA = SubViewA()
subViewA.delegate = viewA

//do something
subViewA.doSomething(data: "OK, let's me go")

Trong đó:

  • Viết extension cho View A để implement delegate
  • Viết function xử lý và truyền dữ liệu tại Sub-View A & kiểm tra delegate

3. Delegation trong View

Delegate và Datasource xuất hiện ở mọi ngóc ngách trong hệ sinh thái iOS, và phần lớn các developer đơn giản là chỉ copy & paste chúng để dùng mà không biết rõ bản chất hoạt động là ra sao. Sau đây, mình sẽ nói về cách sử dụng nó trong code iOS.

3.1. UI Control cơ bản

Hầu hết tất cả UI Control cơ bản đều có delegate và datasource của riêng nó. Tại sao vậy?

Delegate của UI Control nhằm thông báo cho các View hay Controller biết sự thay đổi về trạng thái của chính bản thân nó.

Ví dụ:

Khi dùng tay tác động để kéo 1 scrollview và thả ra. Thì scrollview vẫn tiếp tục chạy 1 tí, mặc dù người dùng còn tác động nữa. Đó chính là sự thay đổi trạng thái của scrollview. Cho tới lúc scrollview dừng và người dùng muốn bắt được sự kiện đó thì scroll view phải gởi đi 1 sự kiện bằng delegate của nó. Nếu View hay Controller implement và lắng nghe sự kiện từ delegate của scrollview thì sẽ nhận được .

Làm thế nào để tìm ra tụi nó?

Ví dụ với UITableViewDelegate > nhấn command + kích chuột.

Nhảy trực tiếp tới file define các function của Delegate đó.

Copy ra và dùng tại class mà implement Delegate đó.

Đây cũng chính là cách bắt sự kiện tiếp theo (ngoài IBAction, Touch Event và cảm ứng) trong lập trình iOS.

 

3.2. Custom View

Phần này sẽ hướng dẫn chính về sử dụng delegate trong custom view. Mục đích:

    • Truyền dữ liệu từ custom view về View Controller
    • Không cần phải phải tạo các property để quản lý các custom view như là 1 biến toàn cục của class ViewController
    • Sử dụng được với nhiều đối tượng custom tạo ra

Tiến hành tạo 1 custom view với phương pháp LoadNibName . Nếu bạn chưa biết thì có thể đọc bài custom view:

    • Basic iOS tutorial : Custom View

Trong đó:

  • UserView
    • Là 1 sub-class của UIView
    • Có 1 UIImageView và 1 UILabel

Khai báo thêm một Protocol cho UserView:

  • Đặt tên là UserViewDelegate
  • Khai báo 1 function với mục đích trả về thứ tự của view khi người dùng chạm vào.
protocol UserViewDelegate: class {
    func userView(userView: UserView, didSelectedWith index: Int)
}

Triển khai sử dụng trên custom view

class UserView: UIView {
    
    weak var delegate: UserViewDelegate?
    var index: Int = 0
    
    @IBOutlet weak var avatarImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    
    @IBAction func tap(_ sender: Any) {
        if let delegate = delegate {
            delegate.userView(userView: self, didSelectedWith: index)
        }
    }
    
}

Trong đó:

  • Khai báo property delegate với từ khoá weak và kiểu optional
  • Điều này tránh leak bộ nhớ khi đối tượng custom view bị giải phỏng thì delegate cũng bị giải phóng theo
  • Khi sử dụng delegate để đảm bảo cho chương trình không bị crash thì bên kiểm tra sự tồn tại của chúng.

Tại file ViewController, tạo custom view và  addSubview

import UIKit

class HomeViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let userView = Bundle.main.loadNibNamed("UserView", owner: self, options: nil)?.first as! UserView
        userView.frame = CGRect(x: 50, y: 50, width: 100, height: 125)
        userView.index = 10
        view.addSubview(userView)
    }
}

Viết extension của ViewController cho việc implement Delegate. Ở đây, định nghĩa lại các function của delegate.

extension HomeViewController: UserViewDelegate {
    func userView(userView: UserView, didSelectedWith index: Int) {
        print("Did select UserView with index \(index)")
    }
}

Tiếp theo, xét giá trị delegate của custom view chính là ViewController

userView.delegate = self

Kết quả:

Bạn để ý thì sẽ thấy:

  • Biến userView được tạo ra và addSubview vào view của ViewController thì kết thúc hàm viewDidLoad nó sẽ bị giải phỏng khỏi bộ nhớ
  • Tuy nhiên, delegate của nó đã trỏ tới ViewController nên chúng ta vẫn có thể quản lý và nhận dữ liệu từ Custom View

Ngoài việc sử dụng với Custom View thì delegate được sử dụng ở hầu hết các class/struct/enum. Điều này nhăm gia tăng sức mạnh của class của bạn.

4. DataSource

Theo các phần trên thì chúng ta mới đi được 1 chiều là:

Truyền dữ liệu từ đối tượng con sang đối tượng cha.

Vậy khi đối tượng con muốn lấy giá trị từ đối tượng cha thì sẽ như thế nào?

Có một khái niệm được đưa ra là datasource với ý nghĩa đảo ngược lại quá trình delegate.

Phần tích một chút về delagate:

  • function được thực thi và dữ liệu được gán vào các tham số của function
  • các function thường sẽ return về là Void

Đảo ngược lại quá trình delegate thì sẽ như thế nào? Tham khảo đoạn code sau:

protocol UserViewDataSource: class {
    func userView(nameOf userView: UserView) -> String
    func userView(indexOf userView: UserView) -> Int
}

Trong đó:

  • Function return về kiểu dữ liệu khác Void
  • Cách khai báo DataSource vẫn giống như Delegate
  • Các function chỉ khai báo

Sử dụng trong custom view

  • Khai báo property dataSource
weak var dataSource: UserViewDataSource?
  • Sử dụng dataSource tại Custom View, cũng tương tự như delegate.
func configView() {
        if let dataSource = dataSource {
            //set index
            index = dataSource.userView(indexOf: self)
            
            //set name
            nameLabel.text = dataSource.userView(nameOf: self)
        }
    }

Tại file ViewController:

  • implement UserViewDataSource bằng cách tạo 1 extension cho ViewController.
extension HomeViewController: UserViewDataSource {
    func userView(nameOf userView: UserView) -> String {
        return "Fx Studio"
    }
    
    func userView(indexOf userView: UserView) -> Int {
        return 999
    }
}
  • Xét giá trị cho property dataSource của Custom View là ViewController
userView.dataSource = self
  • Gọi hàm configView() của Custom View để thay đổi dữ liệu của Custom View.

Xem lại toàn bộ code của ViewController và chạy chương trình để kiểm tra:

import UIKit

class HomeViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let userView = Bundle.main.loadNibNamed("UserView", owner: self, options: nil)?.first as! UserView
        userView.frame = CGRect(x: 50, y: 150, width: 100, height: 125)
        userView.index = 10
        userView.delegate = self
        userView.dataSource = self
        view.addSubview(userView)
        
        userView.configView()
    }
}


extension HomeViewController: UserViewDelegate {
    func userView(userView: UserView, didSelectedWith index: Int) {
        print("Did select UserView with index \(index)")
    }
}

extension HomeViewController: UserViewDataSource {
    func userView(nameOf userView: UserView) -> String {
        return "Fx Studio"
    }
    
    func userView(indexOf userView: UserView) -> Int {
        return 999
    }
}

Kết quả:

  • Ban đầu Custom View được tạo và addSubview vào View của ViewController với index = 10
  • Gọi hàm configView() thì giá trị của index và name đã được thay đổi
  • Khi click vào Custom View thì delegate sẽ truyền dữ liệu index về ViewController và in ra
  • Bây giờ giá trị của index = 999

 

Qua ví dụ trên thì ta thấy delegate và datasource thực chất đều là Protocol. Nhưng tuỳ vào cách sử dụng là truyền hay lấy dữ liệu thì chúng ta sẽ có tên gọi riêng cho nó.

  • Truyền dữ liệu : Delegate
  • Lấy dữ liệu: DataSource

Tạm kết

  • Nắm được các vấn đề trong việc truyền dữ liệu
  • Hiểu được mô hình Delegation
  • Khai báo và sử dụng các Delagate & DataSource Protocol
  • Nắm được sự khác nhau cơ bản của Delegate và DataSource

 

FacebookTweetPinYummlyLinkedInPrintEmailShares16

Related Posts:

  • feature_bg_3
    Clean Architecture trong iOS
  • feature_bg_3
    Builder Pattern trong 10 phút
  • testing
    Hello Testing iOS
  • feature_bg_3
    Observer Pattern trong 10 phút
Tags: basic ios tutorial, delegate, iOS, protocol
Written by chuotfx

Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift AI api AppDistribution autolayout basic ios tutorial blog ci/cd closure collectionview combine concurrency crashlytics dart dart basic dart tour Declarative delegate deploy design pattern fabric fastlane firebase flavor flutter GCD gradients iOS MVVM optional Prompt engineering protocol Python rxswift safearea Swift Swift 5.5 SwiftData SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Role-playing vs. Persona-based Prompting
  • [Swift 6.2] Raw Identifiers – Đặt tên hàm có dấu cách, tại sao không?
  • Vibe Coding là gì?
  • Cách Đọc Sách Lập Trình Nhanh và Hiệu Quả Bằng GEN AI
  • Nỗ Lực – Hành Trình Kiến Tạo Ý Nghĩa Cuộc Sống
  • Ai Sẽ Là Người Fix Bug Khi AI Thống Trị Lập Trình?
  • Thời Đại Của “Dev Tay To” Đã Qua Chưa?
  • Prompt Engineering – Con Đường Để Trở Thành Một Nghề Nghiệp
  • Vấn đề Ảo Giác (hallucination) khi tương tác với Gen AI và cách khắc phục nó qua Prompt
  • Điều Gì Xảy Ra Nếu… Những Người Dệt Mã Trở Thành Những Người Bảo Vệ Cuối Cùng Của Sự Sáng Tạo?

You may also like:

  • Clean Architecture trong iOS
    feature_bg_3
  • Builder Pattern trong 10 phút
    feature_bg_3
  • Memento Pattern trong 10 phút
    feature_bg_3
  • Hello Testing iOS
    testing
  • Observer Pattern trong 10 phút
    feature_bg_3

Archives

  • May 2025 (2)
  • April 2025 (1)
  • March 2025 (8)
  • January 2025 (7)
  • December 2024 (4)
  • September 2024 (1)
  • July 2024 (1)
  • June 2024 (1)
  • May 2024 (4)
  • April 2024 (2)
  • March 2024 (5)
  • January 2024 (4)
  • February 2023 (1)
  • January 2023 (2)
  • November 2022 (2)
  • October 2022 (1)
  • September 2022 (5)
  • August 2022 (6)
  • July 2022 (7)
  • June 2022 (8)
  • May 2022 (5)
  • April 2022 (1)
  • March 2022 (3)
  • February 2022 (5)
  • January 2022 (4)
  • December 2021 (6)
  • November 2021 (8)
  • October 2021 (8)
  • September 2021 (8)
  • August 2021 (8)
  • July 2021 (9)
  • June 2021 (8)
  • May 2021 (7)
  • April 2021 (11)
  • March 2021 (12)
  • February 2021 (3)
  • January 2021 (3)
  • December 2020 (3)
  • November 2020 (9)
  • October 2020 (7)
  • September 2020 (17)
  • August 2020 (1)
  • July 2020 (3)
  • June 2020 (1)
  • May 2020 (2)
  • April 2020 (3)
  • March 2020 (20)
  • February 2020 (5)
  • January 2020 (2)
  • December 2019 (12)
  • November 2019 (12)
  • October 2019 (19)
  • September 2019 (17)
  • August 2019 (10)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (44)
  • Code (11)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (102)
  • No Category (1)
  • RxSwift (37)
  • SwiftUI (80)
  • Tutorials (87)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2025 Fx Studio - All rights reserved.