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 March 7, 2021

SwiftUI App Life Cycle – SwiftUI Notes #6

SwiftUI

Contents

  • Chuẩn bị
  • 1. UIKit App Delegate vs. SwiftUI App
  • 2. Create a new App
  • 3. SwiftUI App Protocol
    • 3.1. @main
    • 3.2. App Protocol
    • 3.3. Scene
  • 4. App States Callbacks
  • 5. App Init
  • 6. Deeplink URLs
  • 7. Connect to AppDelegate
  • Tạm kết

Chào bạn đến với Fx Studio. Chúng ta tiếp tục hành trình với thế giới SwiftUI trong series notes này. Bài viết này sẽ nói về một chủ đề mới và cũng là một chủ đề khá quan trọng. Đó là Life Cycle.

Trước khi vào bài, bạn là một người mới hay chưa biết về SwiftUI hoặc đã quên mất nó rồi. Bạn có thể cập nhật lại ở link bài viết sau:

    • SwiftUI – Phần 1 : Làm quen với SwiftUI

Nếu mọi thứ đã ổn rồi, thì …

Bắt đầu thôi!

Chuẩn bị

Với SwiftUI App Life Cycle, đây là một tính năng mới nhất được Apple cung cấp. Do đó, bạn cần phải chuẩn bị phiên bản môi trường và Xcode mới nhất. Thông tin cấu hình như sau:

    • Xcode 12
    • Swift 5.3
    • SwiftUI 2.0
    • MacOS 10.15.x

Về demo, bạn hãy bắt đầu bằng một project mới với SwiftUI. Ngoài ra, bạn có thể checkout lại toàn bộ code demo của series ở đây:

    • Toàn bộ repo: https://github.com/fx-studio/swiftui-notes
    • Project bài trước: https://github.com/fx-studio/swiftui-notes/tree/main/005-ExtractingSubviews

1. UIKit App Delegate vs. SwiftUI App

Life cycle

Life Cycle là một trong những thứ mà một dev iOS cần phải nắm được. Hiểu được các hình thái/trạng thái của ứng dụng mình đang như thế nào. Bạn sẽ biết được sự kiện gì sẽ xảy ra khi bạn tương tác với ứng dụng của bạn … vâng vâng và mây mây!

Bạn có thể đọc thêm về Life Cycle với UIKit AppDelegate tại đây. 

Mình xin phép không trình bày về nó để tập trung vào Life Cycle mới.

Từ khai sinh lập địa của iOS, chúng ta vẫn sử dụng duy nhất một Life Cycle với đại diện là AppDelegate. Nhưng cũng theo sự phát triển của lập trình thì iOS cũng không ngoài xu thế đó. Nhưng cái hay mà Apple đem tới cho bạn là việc …

Âm thầm chuẩn bị giúp bạn.

Tất cả những gì mình chia sẻ lại từ bài viết này, thì đã được Apple chuẩn bị trước đó 1 năm. Còn quay về với 1 năm trước thì bạn có Xcode 11. Với Xcode 11, project của bạn không chỉ có một màn hình main. Bạn có thể thêm các cấu hình để AppDelegate cho phép UIKit có thể triển khai trên nhiều màn hình thiết bị khác nhau.

Nhưng đó là sự phát triển của UIKit. Bạn có thể xem lại bài viết này để thấy sự thay đổi trong Life Cycle của UIKit. Nó chuyển từ AppDelegate thành AppDelegate + SceneDelegate.

Và cái mà bạn cần quan tâm trong series này. Chính là SwiftUI. Và tới Xcode 12, Apple đã ưu ái cho nó riêng một cách quản lý Life Cycle của riêng SwiftUI.

2. Create a new App

Chúng ta sẽ bắt đầu từng bước một để khai phá Life Cycle mới nào! Trước tiên, bạn hãy tạo một project mới với SwiftUI.

SwiftUI App Life Cycle

Với tuỳ chọn Interface là SwiftUI, thì phần Life Cycle bạn có thêm 2 sự lựa chọn.

  • UIKit App Delegate : đây là cách truyền thống với sự quản lý từ UIKit.
  • SwiftUI App : đây là cách mới, thời đại mới.

Với lựa chọn Interface là Storyboard thì bạn chỉ có duy nhất một sự lựa chọn cho Life Cycle mà thôi. Đừng cố gắng thử làm gì! Ahihi!

Trước tiên, chúng ta hãy dạo chơi một vòng với SwiftUI App mới có những gì. Sau khi tạo xong, project chúng ta sẽ hiện ra. Thứ cần quan tâm là cấu trúc file cho template mới này có những gì.

Đập vào mắt là số lượng file có vẻ ít đi rất nhiều. Hay nói cách khác, SwiftUI App với template rất đơn giản. Bạn chỉ cần tập trung vào 2 file sau:

  • ContentView.swift : đây là nơi chưa màn hình đầu tiên. À chính xác là view đầu tiên.
  • TheNewAppApp.swift : mình đặt tên hơi củ chuối nên có 2 chứ App. Bạn chỉ cần biết có hậu tố App phía sau tên. Nó tương tự như file AppDelegate vậy. Nó sẽ quản lý toàn bộ ứng dụng.

3. SwiftUI App Protocol

Điểm khởi đầu mới.

Ta sẽ xem có gì trong file SwiftUI App mới đó.

import SwiftUI

@main
struct TheNewAppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

3.1. @main

Nếu bạn nào thực sự tìm hiểu mỗi lần Swift mới ra mắt thì có những gì thay đổi. Thì mới hiểu được từ khoá @main có tầng lớp ý nghĩa như thế nào. Mình sẽ tóm tắt lại như sau:

    • Có từ Swift 5.3
    • Không sử dụng được với file main.swift
    • Chỉ định struct/class nào đó là điểm bắt đầu khi khởi chạy một chương trình
    • Nếu thêm 2 lần @main thì báo lỗi
    • Không thêm vào thì cũng bị lỗi

Bạn có thể đọc thêm về Swift 5.3 và từ khoá @main tại đây.

3.2. App Protocol

Ấn tượng tiếp theo là bạn có một protocol với cái tên rất bá đạo, là App. Nó quá cụ thể luôn. Đây chính là ứng dụng của bạn. Với việc implement protocol này, bạn cần phải khai báo thêm một phương thức của nó là:

@SceneBuilder var body: Self.Body { get }

Đây là một SceneBuilder, thứ cần để ứng dụng hiển thị cho người dùng thấy được. Trong đó, bạn cần tiếp tục khai báo thêm một WindowGroup. Em này lại cần một em là ContentView. Chắc phần này cũng dễ đoán mà thôi.

3.3. Scene

Bạn cũng đã biết mục đích chính của SwiftUI là có thể đưa ứng dụng lên nhiều nền tảng khác nhau. Do đó, thành phần Scene này cũng khá là linh hoạt.

Với mặc định từ Xcode 12 tạo ra thì bạn có đại diện chính là WindowGroup. Nó như là window trong UIKit hay nhiều window với MacOS.

Tuy nhiên, với nền tảng khác thì không chỉ có một Window mà còn thêm nhiều thứ khác nữa. Ví dụ: MacOS có những view đặc biệt như Setting thì chúng ta có thể if else sau nha. Bạn xem thêm tại đây.

Thêm tí tổng hợp về họ hàng bà con nhà nó như sau:

    • WindowGroup
    • DocumentGroup
    • Settings
    • WKNotificationScene
    • CommandsBuilder
    • CommandMenu
    • Command Protocol

Có điều kiện, mình sẽ viết bài riêng cho từng em một nha. Giờ tập trung sang phần chính tiếp theo nào!

4. App States Callbacks

Tiếp tục, bạn cần phải lắng nghe được sự thay đổi của trạng thái ứng dụng. Trong iOS 14, Apple cung cấp cho bạn ScenePhase để giúp bạn theo dõi trạng thái. Việc theo dõi này sẽ theo dõi từ môi tường. Do đó, bạn cần tạo thêm một biến môi trường cho Scene.

@Environment(\.scenePhase) private var scenePhase

Bạn đã có Scene rồi, giờ sử dụng thêm phương thức onChange(of:) để lắng nghe các giá trị từ biến môi trường.

struct TheNewAppApp: App {
    
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }.onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("App State : Background")
            case .inactive:
                print("App State : Inactive")
            case .active:
                print("App State : Active")
            @unknown default:
                print("App State : Unknown")
            }
        }
    }
}

Bạn sử dụng đoạn code trên để tiết kiệm thời gian. Sau đó hãy build ứng dụng lên các thiết bị iOS và xem kết quả.

SwiftUI App Life Cycle - tracking State

5. App Init

Với AppDelegate, function didFinishLaunchWithOptions  được xem là rất thần thánh. Nơi ứng dụng bắt đầu khởi tạo và là nơi để chứa hết những gì cần chạy lúc ban đầu. Và tương tự với SwiftUI App. Bạn chỉ cần nhẹ nhàng thêm function init huyền thoại vào là ổn.

struct TheNewAppApp: App {
    
    @Environment(\.scenePhase) private var scenePhase
    
    init() {
        // Bla bla bla
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }.onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("App State : Background")
            case .inactive:
                print("App State : Inactive")
            case .active:
                print("App State : Active")
            @unknown default:
                print("App State : Unknown")
            }
        }
    }
}

Còn muốn dùng làm gì thì tuỳ ý bạn nha!

6. Deeplink URLs

Phần tiếp theo, bạn cần là việc mở ứng dụng bằng một link URL. Không chỉ có mỗi việc mở ứng dụng mà chúng ta có thể làm nhiều thứ với nó. Như truyền thêm giá trị vào ứng dụng từ bên ngoài.

Giang hồ gọi là Deeplink.

Với AppDelegate, ta có thể handle nó thông qua phương thức sau application(_:open:options:). Còn với SwiftUI App ta cũng có phương thức tương tự.

WindowGroup {
            ContentView(name: name)
                .onOpenURL(perform: { url in
                    print(url.absoluteURL)
                })
        }.onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("App State : Background")
            case .inactive:
                print("App State : Inactive")
            case .active:
                print("App State : Active")
            @unknown default:
                print("App State : Unknown")
            }
        }

Với onOpenURL, sử dụng cho Scene top trong ứng dụng. Ta có thể đón nhận các url và khởi động app lên.

(Về setting cho Deeplink URL thì mình không trình bày ở bài viết này. Còn lại hình dưới đây là setting của mình.)

Để cho demo thêm sinh động ta sẽ implement thêm một chút nữa  cho em ContentView. Nội dung cập nhật như sau:

  • Với việc hiển thị tên dưới chữ Hello, world!.
  • Giá trị tên này sẽ truyền từ một link và mở bằng Safari.

Code của ContentView như sau:

struct ContentView: View {
    
    let name: String
    
    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
            Text((name != "") ? name : "---")
                .font(.title)
                .foregroundColor(Color.blue)
                .padding()
        }
    }
}

Bạn chuyển sang file App, thêm 1 extension nhằm đọc giá trị từ query string của url một cách nhanh chóng.

extension URL {
    func valueOf(_ queryParamaterName: String) -> String? {
        guard let url = URLComponents(string: self.absoluteString) else { return nil }
        return url.queryItems?.first(where: { $0.name == queryParamaterName })?.value
    }
}

Sau đó, bạn sẽ hoàn thiện struct App như sau:

struct TheNewAppApp: App {
    
    @Environment(\.scenePhase) private var scenePhase
    @State var name: String = ""
    
    init() {
        // Bla bla bla
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView(name: name)
                .onOpenURL(perform: { url in
                    name = url.valueOf("name") ?? ""
                    
                    print(url.absoluteURL)
                    print(name)
                })
        }.onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("App State : Background")
            case .inactive:
                print("App State : Inactive")
            case .active:
                print("App State : Active")
            @unknown default:
                print("App State : Unknown")
            }
        }
    }
}

Trong đó:

  • Tạo một thuộc tính @State là name, để lưu trữ giá trị và truyền cho ContentView
  • Tại onOpenURL , phân tích url và gián giá trị mới cho name.

Cú pháp mở app từ Safari với url như sau: (cài đặt link url theo hình ở trên)

fxapp://fxapp.com?name=FxStudio

Build ứng dụng và xem kết quả:

  • Safari sau khi gõ đường dẫn trên

  • Ứng dụng được gọi lại từ background

 

EZ Game!

7. Connect to AppDelegate

Mặc dù SwiftUI App có thể độc lập với UIKit. Nhưng khi bạn phát triển ứng dụng trên iOS thì mối quan hệ này vẫn còn vương vấn rất nhiều. Hiện nay, nhiều thư viện hay dịch vụ của bên thứ ba vẫn cần được khởi tạo tại AppDelegate. Hoặc bạn có thể tracking nhiều hơn với AppDelegate.

Do đó, SwiftUI cũng giúp cho bạn có thể nhận được hay triệu hồi các phương thức AppDelegate. Ta sẽ thực hiện theo các bước sau:

Bước 1: tạo lại file AppDelegate.swift

import UIKit

class MyAppDelegate: UIResponder, UIApplicationDelegate {
  
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
      
        print("AppDelegate > didFinishLaunchingWithOptions > called")
      
        return true
    }
}

Trong đó:

  • Triệu hồi UIKit trong file này
  • Kế thừa UIResponder
  • Implement protocol UIApplicationDelegate cho nó chắc
  • Thêm phương thức didFinishLaunchWithOptions
  • Không khai báo hay khởi tạo thêm các window & scene

Đặc biệt trong file không có từ khoá sau @UIApplicationMain hay @main . Vì đây là SwiftUI App, main đã được chỉ định ở nơi khác rồi.

Bước 2: Tạo adaptor từ file App. Thêm một thuộc tính như sau:

@UIApplicationDelegateAdaptor(MyAppDelegate.self) private var appDelegate

Khi ứng dụng được khởi tạo và chạy thì hàm didFinishLaunchWithOptions cũng sẽ được gọi chạy theo. Bạn build lại ứng dụng và cảm nhận kết quả nha!

 

Tạm kết

  • Tạo SwiftUI App với protocol App.
  • Sử dụng @main để chỉ định struct/class nào là bắt đầu ứng dụng,
  • Tracking được các trạng thái của app bằng SwiftUI App.
  • Sử dụng Deeplink vào trong SwiftUI App.
  • Kết nối ngược lại với AppDelegate Life cycle.

 

Okay! Tới đây, mình xin kết thúc bài viết này. Và nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.

  • Bạn có thể checkout code tại đây.
  • Bài viết tiếp theo tại đây.

Cảm ơn bạn đã đọc bài viết này!

FacebookTweetPinYummlyLinkedInPrintEmailShares44

Related Posts:

  • Make color App Flutter
    Make color App Flutter
  • Hello Multiplatform App - SwiftUI Notes #22
    Hello Multiplatform App - SwiftUI Notes #22
  • tvOS App - SwiftUI Notes #21
    tvOS App - SwiftUI Notes #21
  • SwiftUI - Phần 2 : Cơ bản về ứng dụng SwiftUI App
    SwiftUI - Phần 2 : Cơ bản về ứng dụng SwiftUI App
Tags: SwiftUI, SwiftUI Notes
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 api AppDistribution Asynchronous autolayout basic ios tutorial blog callback ci/cd closure collectionview combine concurrency CoreData Core Location crashlytics darkmode dart dart basic dart tour Declarative decoding delegate deploy fabric fastlane firebase flavor flutter GCD iOS mapview MVVM optional protocol rxswift Swift Swift 5.5 SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Raw String trong 10 phút
  • Dispatch Semaphore trong 10 phút
  • Tổng kết năm 2022
  • KeyPath trong 10 phút – Swift
  • Make color App Flutter
  • Ứng dụng Flutter đầu tiên
  • Cài đặt Flutter SDK & Hello world
  • Coding Conventions – người hùng hay kẻ tội đồ?
  • Giới thiệu về Flutter
  • Tìm hiểu về ngôn ngữ lập trình Dart

You may also like:

  • SwiftUI - Phần 2 : Cơ bản về ứng dụng SwiftUI App
    SwiftUI - Phần 2 : Cơ bản về ứng dụng SwiftUI App
  • SwiftUI - Phần 5 : Multiplatform App
    SwiftUI - Phần 5 : Multiplatform App
  • Tích hợp UIView (UIKit) vào SwiftUI Project - SwiftUI Notes…
    Tích hợp UIView (UIKit) vào SwiftUI Project - SwiftUI Notes #15
  • Multiplatform App : Extra Functions - SwiftUI Notes #24
    Multiplatform App : Extra Functions - SwiftUI Notes #24
  • Multiplatform App : Design Views - SwiftUI Notes #23
    Multiplatform App : Design Views - SwiftUI Notes #23

Archives

  • 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 (22)
  • Code (4)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (86)
  • RxSwift (37)
  • SwiftUI (76)
  • Tutorials (70)

Newsletter

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

    Copyright © 2023 Fx Studio - All rights reserved.

    Share this ArticleLike this article? Email it to a friend!

    Email sent!