Ứng dụng State Machine trong Swift
ios
53
swift
65
state machine
2
White

Vu Nhat Minh viết ngày 09/04/2017

State Machine là một kỹ thuật không mới trong lập trình nói chung và rất hữu dụng để giải quyết các bài toán về trạng thái. Trước đây đã có 1 bài viết của @huydx trong series hard-core nói về State Machine khá chi tiết. Lần này mình sẽ giới thiệu một ứng dụng trong lập trình IOS (Swift) khi ứng dụng có biểu đồ di chuyển phức tạp.

State Machine là gì

State Machine là ... một cái máy tính đơn giản, bao gồm một tập nhiều trạng thái (state) và các di chuyển giữa các trạng thái đó với nhau. Mỗi khi có 1 sự kiện (event) xảy ra, cái máy của chúng ta sẽ chuyển từ trạng thái này sang trạng thái khác.

Ở hình bên trên trạng thái nguyên thuỷ là State A.Với sự kiện T1 diễn ra, trạng thái State A sẽ di chuyển đến trạng thái State B v.v...

Vậy nếu liên tưởng mỗi màn hình trong ứng dụng là 1 trạng thái, và sự kiện để chuyển màn hình là input thì chúng ta sẽ có 1 State Machine để quản lý toàn bộ quá trình di chuyển màn hình.

    |--------------------|      |--------------------|      |--------------------|      |--------------------|
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |      initial       | -->  |      greeting      | -->  |       login        | -->  |       home         |
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |                    |      |                    |      |                    |      |                    |
    |--------------------|      |--------------------|      |--------------------|      |--------------------|

                          launch                       start                     loggedIn

Điều này có lợi gì so với cách làm thông thường ?

Khi số lượng màn hình của 1 tính năng (từ đây sẽ gọi là 1 flow) tăng đến tầm 20~30 màn hình, chúng ta thường sẽ không muốn cho tất cả vào trong 1 storyboard mà chia làm các storyboard riêng biệt rồi kết nối lại với nhau bằng code. Các đoạn kết nối nói trên sẽ bị chia lẻ tẻ vào nhiều file source khác nhau, khó quản lý và điều chỉnh khi có thay đổi về tính năng.

Dùng State Machine sẽ quản lý được toàn bộ các dữ kiện lưu chuyển của flow bằng một machine duy nhất.

Implement State Machine

Trước hết là implement phiên bản đơn giản cho một State Machine. Ở đây mình dùng một từ điển 2 tầng routes để lưu tham chiếu từ một trạng thái, 1 sự kiện để dẫn đến 1 trạng thái mới.

import Foundation

protocol StateType: Hashable {}
protocol EventType: Hashable {}

struct Path<S: StateType, E:EventType> {
    let from: S
    let to: S
    let by: E
}

class StateMachine<S: StateType, E: EventType> {
    var currentState: S
    var routes: [S: [E: S]] = [:]

    init(_ initState: S, _ config: (StateMachine) -> Void) {
        currentState = initState
        config(self)
    }

    func addPath(_ p: Path<S, E>) {
        var dict = routes[p.from] ?? [:]
        dict[p.by] = p.to
        routes[p.from] = dict
    }

    func transition(from: S, by: E) -> Bool {
        let next = routes[from].flatMap { $0[by] }
        guard let existed = next else { return false }
        currentState = existed
        return true
    }
}

Tạo ra 1 State Machine cụ thể

Đến bây giờ mình có thể định nghĩa tất cả các trạng thái và sự kiện thành kiểu enum, mà chỉ cần thoả mãn StateTypeEventType bên trên:

enum ScreenState: StateType {
    case initial
    case greeting
    case login
    case home
}

enum MovingEvent: EventType {
    case launch
    case start
    case loggedIn
}

Và cùng với đó là quản lý đồ thị di chuyển tập trung tại 1 chỗ

let graph: [Path<ScreenState, MovingEvent>] = [
    Path(from: .initial, to: .greeting, by: .launch),
    Path(from: .greeting, to: .login, by: .start),
    Path(from: .login, to: .home, by: .loggedIn)
]

class StateMachineManager {
    static let machine = StateMachine<ScreenState, MovingEvent>(.initial, { machine in
        for p in graph {
            machine.addPath(p)
        }
    })
}

Nối các màn hình vào vào các trạng thái tương ứng

Để sử dụng State Machine ở trên, mình sẽ tạo 1 protocol chuyên biệt để tiện sử dụng cho các ViewController mong muốn

 protocol StatefulViewController {
    var state: State { get }
    func moveNext(event: Event) -> UIViewController?
}

extension StatefulViewController {
    func moveNext(event: Event) -> UIViewController? {
        let machine = StateMachineManager.machine
        guard machine.transition(from: state, by: event) else { return nil }

        switch machine.currentState {
        case .initial: // Return correcsponding View Controller
        case .greeting: // Return correcsponding View Controller
        case .login: // Return correcsponding View Controller
        case .home: // Return correcsponding View Controller
        }
    }
}

Xong! Đơn giản hơn mọi người vẫn nghĩ phải không nào :smile:

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Vu Nhat Minh

54 bài viết.
753 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
117 29
Nếu bạn thường vào trang mua sắm của amazon, chắc sẽ chẳng lạ gì với menu Shop by Department. Tốc độ hiển thị nội dung của menu là tức thì so với d...
Vu Nhat Minh viết hơn 2 năm trước
117 29
White
90 4
Lời người dịch Người dịch là một developer , sau khi tìm đọc được bài viết này bằng bản gốc tiếng Anh đã cảm thấy như được "khai sáng" về khả năng...
Vu Nhat Minh viết gần 3 năm trước
90 4
White
60 7
Form là thành phần quan trọng nhất khi design flow đăng ký của 1 web hay 1 app, dù là view gồm nhiều bước hay chỉ là một màn hình đơn điệu. Bài này...
Vu Nhat Minh viết hơn 1 năm trước
60 7
Bài viết liên quan
White
2 3
Xin chào mọi người. Mình xin chia sẽ một UILabel Helper nhỏ dùng trong truờng hợp cần tính chiều cao của UILabel để xác định "Show More" button có ...
DonDinh viết 3 năm trước
2 3
White
11 4
(Link) (Link) (Link) Ở 2 phần tut trước, mình đã hướng dẫn khá chi tiết cách viết một ứng dụng camera có tích hợp chức năng nhận diện khuôn mặ...
HoangPH viết 3 năm trước
11 4
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
54 bài viết.
753 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!