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 April 21, 2021

Swift Package – SwiftUI Notes #17

iOS & Swift . SwiftUI . Tutorials

Contents

  • 1. Game Demo
    • 1.1. Game Play
    • 1.2. Cấu trúc
    • 1.3. Game State
  • 2. Create Swift Package
    • 2.1. Create
    • 2.2. Add files
    • 2.3. Config Code
    • 2.4. Version
    • 2.5. Linking
  • 3. Import Swift Package
  • 4. Build iOS App
  • Tạm kết

Chào bạn đến với Fx Studio. Chúng ta lại sang phần mới trong hành trình SwiftUI với series SwiftUI Notes. Đây cũng là bài viết đầu tiên trong phần thứ tư nói về Hệ sinh thái Apple. Và chủ đề của bài viết là Swift Package.

Bạn có thể xem lại 3 phần trước của series tại đây:

    • Làm quen với SwiftUI
    • Cơ bản về ứng dụng SwiftUI App
    • Tích hợp SwiftUI và UIKit

Nhiệm vụ đầu tiên của chúng ta cần phải làm trước khi thực hiện việc đưa một source code lên các thiết bị & nền tảng khác nhau. Đó là biến các phần dùng chung thành một thư viện duy nhất. Đảm bảo được tính thống nhất về mặt logic trong tất cả các nền tảng.

Apple cũng đã giúp bạn với Swift Package Manager. Nó cũng khá tương đồng với CocoaPod. Tuy nhiên, ta sẽ sử dụng Swift Package trong phạm vi local và lưu trữ tại máy tính. Có thể bạn sẽ thấy thích nó sau khi hoàn thành các bài trong phần mới này của series.

Và nếu như mọi việc đã ổn rồi, thì …

Bắt đầu thôi!

1. Game Demo

Thay vì cung cấp cho bạn các version OS & tools, mình sẽ trình bày phần chuẩn bị bằng một game đơn giản. Bạn có thể xem qua ở hình sau:

swift package

1.1. Game Play

Game Play thì khá là EZ Game (thậm chí mang màu sắc ngu ngốc nữa, ahihi).

  • Khi nhấn Play Button, Game sẽ được chạy
  • Nhiệm vụ của bạn là Tap vào màn hình
  • Nếu con số bạn Tap là 00 thì bạn là người chiến thắng
  • Ngược lại bạn sẽ thua
  • Bạn có thể chơi lại khi kích vào Play again

Game có tên là Tappy 00. Hy vọng nó giúp bạn bớt căng thẳng trong những giờ làm việc mệt mỏi.

1.2. Cấu trúc

Ta có cấu trúc thư mục của Project Game như sau:

  • ContentView là một SwiftUI View. Đó là màn hình chính của game chúng ta.
struct ContentView: View {
    var body: some View {
        GameView(width: .infinity)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

  • GameView là View cài đặt cho Game Play. Việc xử lý logic và các trạng thái cho game được thực hiện trong cùng một file

Code khởi tạo GameView cho bất cứ màn hình nào như sau:

GameView(width: 300.0)

 

1.3. Game State

Đây là một thành phần quan trọng của Game. Các màn hình game như Loading, Running, GameOver … sẽ được quản lý bằng Game State. Ta có enum khai báo các trạng thái game như sau:

enum GameState {
    case loading
    case running
    case gameover
    case winner
}

Vì chúng ta đang sử dụng SwiftUI và phong cách code của ta là Declarative Programming. Nên tất cả phải được khai báo ngay từ đầu. Do đó, với một ứng dụng mà thay đổi liên tục các View để phù hợp với các trạng thái khác nhau. Thì sự lựa chọn cho việc khai báo view là

@ViewBuilder

Ta có function sau để quyết định View nào sẽ xuất hiện trọng ứng dụng.

@ViewBuilder func makeStatusView() -> some View {
        switch gameState {
        case .loading:
            // View for Loading
            
        case .running:
            // View for Running
            
        case .gameover, .winner:
            // View for end game
        }
    }

Vì kiểu trả về là some View nên bạn hoàn toàn yên tâm mà sử dụng nó vào trong body GameView.

Qua trên, mình đã trình bày cấu trúc chính của Game Demo đơn giản này rồi. Hi vọng giúp dễ hiểu hoặc giúp bạn có thể tự thiết kế một game cho riêng bạn. Và bây giờ, chúng ta sang phần chính nào.

2. Create Swift Package

Swift Packager Manager, ra mắt cùng Swift 3.0 & được giới thiệu là một công cụ giúp quản lý việc phân phối mã nguồn, giúp cho việc chia sẻ và dùng lại code được dễ dàng.

Tuy nhiên, ta sử dụng Swift Package đơn giản mà thôi. Dễ hình dung thì ta sẽ tạo ra một thư viện nhỏ. Thư viện này sẽ được import vào các project demo trong phần này.

2.1. Create

Đầu tiên, bạn hãy dùng Xcode và tạo một Swift Package trước. Lựa chọn Menu > New > Swift Package.

Swift Package

Tiếp tục, bạn cần điền các thông tin cần thiết như sau:

Chú ý, các phần được tô màu đỏ.

  • Bạn cần phải tạo thêm một thư mục mới để lưu trữ thư viện mới này
  • Bạn chọn Add to và Group cho project demo ở trên. Giúp bạn có thể chỉnh sửa một cách nhanh chóng

Sau khi thêm thành công thì Swift Package mới đó sẽ được tự động thêm vào Project. Ta xem cấu trúc file của project lúc này sẽ như sau:

Swift Package

Trong đó:

  • Package.swift là nới chứa định nghĩa của Swift Package và các tham số cần thiết (như target, version …)
  • Thư mục Sources chứa mã nguồn của thư viện
  • File Game.swift là file define đầu tiên của thư viện

2.2. Add files

Sang phần tiếp theo, chúng ta sẽ thêm các file mã nguồn cần thiết vào thư viện. Theo cấu trúc trên ta sẽ có thư mục Game là nơi chứa mã nguồn của thư viện. Bạn muốn thêm file nào vào thì hãy copy chúng vào thư mục Game đó.

Mình tiến hành kéo thả file GameView.swift từ project demo vào Swift Package.

2.3. Config Code

Phần này rất quan trọng. Khi bạn code thường sẽ không chú ý tới các access control trong Swift. Vì mặc định khi bạn khai báo 1 class thì mức truy cập là internal. Điều này có nghĩa là các class/struct khác cùng module với sẽ có thể truy cập tới được và có thể kế thừa struct/class đó.

Tuy nhiên, khi bạn sử dụng Swift Package thì lúc nào đã là một module khác rồi. Với quyền truy cập mặc định là internal thì bên ngoài (lúc này là code ở Project) sẽ không thể nào tìm thấy được các class/struct trong Swift Package.

Do đó, bạn cần phải thêm từ khoá public vào những class/struct/enum và properties nào mà bạn muốn bên ngoài nhìn thấy được. Ví dụ mình sẽ chỉnh sửa lại như sau:

public enum GameState {
    case loading
    case running
    case gameover
    case winner
}

public struct GameView: View {
  
    public var width: CGFloat

    let mainColor = Color(.white)
    @State var gameState: GameState = .loading
    @State var backgroundColor = Color(.darkGray)
    @State var status = "Tap to Play!"
    @State var isStarGame = false
    @State var count = 10
    @State var timer = Timer.publish(every: 0.01, on: .main, in: .common)
    @State private var timerSubscription: Cancellable?
        
    public init(width: CGFloat) {
        //...
    }
    
    public var body: some View {
       // ...
    }
}

Cái nào thực sự cần thiết thì bạn hãy để ở chế độ public nha. Chúng ta sang phần tiếp theo nào.

2.4. Version

Giúp cho việc build package thì bạn cần phải cung cấp thêm các version của các platforms mà bạn muốn sử dụng Swift Package này. Nếu bạn bỏ qua thì Xcode sẽ báo lỗi. Bạn sẽ thêm các từ khoá @available vào trước câu lệnh/khai báo …. mà bạn sử dụng. Xcode sẽ hiểu là hệ điều hành mới nhất.

Khá là rườm rà!

Đơn giản hơn, bạn mở file Package.swift lên và thêm tham số cho platforms vào.

platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],

Cho bạn dễ nhìn thì sau đây là toàn bộ file Package.

import PackageDescription

let package = Package(
    name: "Game",
    platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Game",
            targets: ["Game"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "Game",
            dependencies: []),
        .testTarget(
            name: "GameTests",
            dependencies: ["Game"]),
    ]
)

Chắc phải làm một bài viết đầy đủ về Swift Package. Ahuhu!

2.5. Linking

Bạn chú ý tại file Package.swift có phần define về product . Đây là phần định nghĩ những gì mà bạn có thể thấy được từ Project khi sử dụng thư viện.

products: [
        .library(
            name: "Game",
            targets: ["Game"]),
    ],

Tiếp theo, bạn sẽ tiến hành import thư viện vào Project. Bạn theo các bước sau:

  1. Mở Project của bạn lên
  2. Tại Target của Project, bạn truy cập phần General
  3. Bạn tới phần Frameworks, Libraries and Embedded Content , kích nút +
  4. Chọn Package tại Workspace/Game/ Game

Swift Package

Nếu Project có nhiều target và target nào bạn muốn sử dụng Swift Package thì hãy thực hiện lại các thao tác liên kết trên cho từng target.

Như vậy, bạn đã hoàn thành việc tạo thư viện và liên kết nó vào Project của bạn rồi.

3. Import Swift Package

Cuối cùng, bạn muốn sử dụng Swift Package tại đâu trong Project của bạn, thì hãy import nó vào tại vị trí đó. Xem ví dụ cho thư viện vừa tạo của chúng ta.

import Game

Tuy nhiên, bạn hãy bấm Command + B để build lại Project. Nếu Xcode báo lỗi và không build được. Thì sẽ gỡ rối theo một trong các cách như sau hoặc tất cả cũng đc.

  1. Product > Clean (nhấn Shift + Command + K)
  2. Product > Clean Build Folder (nhấn Shift + Option + Command + K)
  3. Xoá thư mục Derived Data của project. Mở bằng cách truy cập Xcode Menu > Preferences > Locations.

Mọi thứ đã okay thì bạn tiến hành sử dụng các class/struct trong thư viện đó. Với Project demo của chúng ta, mình có sử dụng thư viện tại ContentView. Code tham khảo của nó sẽ như thế này

import SwiftUI
import Game

struct ContentView: View {
    var body: some View {
        GameView(width: UIScreen.screenWidth <= UIScreen.screenHeight ? UIScreen.screenWidth : UIScreen.screenHeight)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension UIScreen{
   static let screenWidth = UIScreen.main.bounds.size.width
   static let screenHeight = UIScreen.main.bounds.size.height
   static let screenSize = UIScreen.main.bounds.size
}

 

4. Build iOS App

Sau khi đã fix hết các lỗi phát sinh. Bạn tiến hành build lại project để xác nhận lần cuối là thư viện của chúng ta đã hoạt động ổn hay không.

Bạn chọn lại Schema & Target để build. Kết quả build thành công lên Simulator là thành công nhoé.

Chúng ta sẽ tiếp tục sử dụng thư viện này cho các bài viết sau và trên các nền tảng khác của Apple.

Tạm kết

  • Tạo thư viện đơn giản ở local với Swift Package
  • Cấu hình cho Swift Package
  • Liên kết Package với Project mà bạn muốn sử dụng
  • Import vào code và sử dụng trong project

 

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!

FacebookTweetPinYummlyLinkedInPrintEmailShares10

Related Posts:

  • KeyPath
    KeyPath trong 10 phút - Swift
  • feature_bg_swiftui_4
    Regular Expression (Regex) trong Swift
  • feature_bg_swift_04
    [Swift 6.2] Raw Identifiers - Đặt tên hàm có dấu…
  • feature_bg_swift_10
    Swift Optional trong 10 phút
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 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:

  • Swift Optional trong 10 phút
    feature_bg_swift_10
  • KeyPath trong 10 phút - Swift
    KeyPath
  • Complete Concurrency với Swift 6
    feature_bg_swift_04
  • Giới thiệu MapKit trên SwiftUI
    feature_bg_swiftui_7
  • Lập trình hướng giao thức (POP) với Swift
    POP

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.