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 December 20, 2019

Basic iOS tutorial : Project Template

iOS & Swift

Contents

  • Chuẩn bị
  • 1. Tạo project
  • 2. Cài đặt CococaPods
    • 2.1. Cài đặt CocoaPods cho máy
    • 2.2. Tạo Podfile
    • 2.3. Pod install
  • 3. Cấu trúc của Project
    • 3.1. AppDelegate
    • 3.2. Define
    • 3.3. Controllers
    • 3.4. Views
      • 3.4.1. Custom View
      • 3.4.2. Cells
    • 3.5. Models
      • 3.5.1. Object & Class
      • 3.5.2. Managers
      • 3.5.3. Service & Core
    • 3.6. Extension
    • 3.7. Framework & Libraries
    • 3.8. Resources
  • Tạm kết

Chào bạn đến với series Lập trình iOS cho mọi người. Bài viết hôm nay sẽ khá là đơn giản về mặt lý thuyết. Mục đích chính là chuẩn bị giúp cho bạn những gì cần thiết cho 1 project iOS.

Chắc bạn không cần phải chuẩn bị kiến thức gì nhiều và bây giờ …

Bắt đầu thôi!

Chuẩn bị

  • MacOS 10.14.4
  • Xcode 11.0
  • Swift 5.1

1. Tạo project

Việc học iOS càng tiến sâu vào thì độ phức tạp của project Xcode lại càng tăng thêm. Mọi thứ không còn đơn giản như ngày mới bắt đầu, với một View Controller cân cả thế giới. Để có thể quản lý được cả project, nhất khi team dự án của bạn có nhiều người cùng làm. Tất nhiên, chúng ta không thể bỏ tất cả mọi thứ vào cùng 1 thư mục …

Nhiều vấn đề như vậy, thì yêu cầu phải có một mẫu hướng dẫn chung, ít nhất là trong phạm vi team dự án của bạn. Để đảm bảo 2 yếu đố chính:

Cấu trúc và mô hình.

  • Cấu trúc là hệ thống các thư mục có sự phân cấp. Và sử dụng theo mục đích khác nhau. Trong các thư mục đó chứa mã nguồn của project
  • Mô hình là mẫu thiết kế mà project của bản sẽ sử dụng chính. Các mô hình hay sử dụng hiện nay như sau: MVC, MVVM, MVP, MV*, VIPER

Vâng, rất nhiều ý nghĩa nhân văn ở đây. Chúng ta sẽ giải quyết chúng ở các phần sau. Còn bây giờ thì bạn hãy

Tạo một iOS project bằng Xcode.

Phần này mình đã mô tả rất cụ thể ở link sau:

    • Bắt đầu iOS Project không sử dụng Storyboard với Xcode 11 và Swift 5.1

Bạn làm theo hướng dẫn và mình sẽ tiến sang phần tiếp theo. Cũng khá là quan trọng.

Lưu ý: Việc không sử dụng Storyboard trong iOS Project thì cũng có trình bày ở link trên rồi.

Bạn nên xoá hết các dòng comment code trong các file của project, cho code mình sạch đẹp hơn. Ví dụ trong file ScreenDelegate.swift

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        
        // ... code đây nha
        
        self.window = window
        window.makeKeyAndVisible()
        
    }

    func sceneDidDisconnect(_ scene: UIScene) {}

    func sceneDidBecomeActive(_ scene: UIScene) {}

    func sceneWillResignActive(_ scene: UIScene) {}

    func sceneWillEnterForeground(_ scene: UIScene) {}

    func sceneDidEnterBackground(_ scene: UIScene) {}
}

2. Cài đặt CococaPods

Cocoapods là chương trình giúp quản lý các bộ thư viện được được sử dụng trong ứng dụng mobile được phát triển dựa trên khung làm việc Cocoa.

Thông thường một ứng dụng phần mềm sẽ cần phải sử dụng nhiều bộ thư viện khác nhau và ứng dụng như vậy sẽ phụ thuộc vào các bộ thư viện này. Chính vì vậy Cocoapod còn được gọi là dependencies manager hay phần mềm quản lý các bộ thư viện phụ thuộc của ứng dụng.

Cocoa chính là framwork chính của MAC OS. Với iOS thì framwork chính là CocoaTouch.

2.1. Cài đặt CocoaPods cho máy

Cocoadpods được viết sử dụng ngôn ngữ Ruby và nó có thể cài đặt giống như một RubyGem. Trên MAC OS, mở Terminal và bạn sử dụng câu lệnh sau để cài đặt Cocoapods.

sudo gem install cocoapods

2.2. Tạo Podfile

  • Di chuyển tới thư mục của project của bạn trên máy tính
cd <đường dẫn tới thư mục project>
  • Gõ lệnh tạo Podfile
pod init

Kết quả là sẽ có một file tên là Podfile được tạo ra. Và ở trong thư mục project của bạn.

Podfile là một tập tin đặc biệt được Cocoapods sử dụng để xác định danh sách các bộ thư viên sẽ được cài đặt để sử dụng trong ứng dụng.

2.3. Pod install

Tiếp tục thì bạn gõ tiếp lệnh cài đặt Pod cho project trong Terminal.

pod install

Kết quả như sau:

Vì Podfile của mình chưa có gì hết nên cũng chưa có thư viện nào thêm vào project. Bạn sẽ thấy sau khi gõ lệnh install thì có một file *.xcworkspace được tạo ra. Và bạn kích đúp mở file đó lên.

Bạn sẽ thấy trong workspace bao gồm 2 project:

  • Project của bạn
  • Project là Pods của CocoaPods tạo ra

Bạn kích vào file Podfile. Và thêm các thư viện cần thiết vào. Ví dụ: cho thư viện kinh điển Almofire.

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'ProjectTemplate' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for ProjectTemplate
  pod 'Alamofire', '~> 5.0.0-rc.3'

end

Sau đó bạn lại quay lại Terminal và gõ lại lệnh install, để cài đặt các thư viện mới thêm vào Podfile.

Trong hình trên bạn cũng thấy thư viện Almofire đã được cài đặt. Bạn có thể tham khảo thêm về cú pháp của Podfile ở đây.

Mỗi lần thay đổi, thêm mới hoặc xoá đi các thư viện trong Podfile thì bạn nên chạy lại lệnh pod install.

Nếu như mới clone project thì cùng nên chạy lại lệnh pod install.

Nếu như thư viện bạn quá cũ hoặc có vấn đề gì thì hãy chạy lệnh pod update.

3. Cấu trúc của Project

Phần này chỉ mang tính chất tạo thư mục và gom các file mã nguồn về đúng nơi, theo đúng chức năng mà nó đảm nhận. Chúng ta tạo các thư mục như hình sau.

3.1. AppDelegate

Bao gồm 2 file:

  • AppDelegate.swift
    • Để im không đụng chạm gì tới nó. Thời đại mới rồi và nó mất dần đi tầm ảnh hưởng.
    • Chú ý từ khoá @UIApplicationMain ở trong file đó. Mang ý nghĩa xác định file AppDelegate nào sẽ chạy lúc khởi động ứng dụng.
  • ScreneDelegate.swift
    • Vài trò khá lớn. Có thể điều phối giao diện ở mức cao nhất.
    • Chúng ta tạo thêm 1 singleton cho class này. Vì đối tượng của nó là duy nhất và được tạo sẵn rồi. Nên chúng ta chỉ trỏ tới chứ không tạo mới thêm.
static func shared() -> SceneDelegate {
        let scene = UIApplication.shared.connectedScenes.first
        return (scene?.delegate as? SceneDelegate)!
    }

3.2. Define

Chứa các khai báo dùng cho toàn bộ project. Bạn tham khảo hình sau tạo các file tương ứng

Ở trên chỉ tham khảo những cái cần sử dụng. Đối với bạn, nếu muốn thêm gì thì có thể thêm tuỳ ý. Về code thì như sau:

  • App.swift
import Foundation

struct App {
    struct Key {}
    
    struct Text {}
    
    struct Color {}
}
  • App.Key.swift
import Foundation

extension App.Key {
    static var authenToken = "....."
    
    static var admobID = "....."
    static var bannerID = "....."
}
  • App.Text.swift
import Foundation

extension App.Text {
    //Information app
    static var appName = "Project Template"
    
    //...
}
  • App.Color.swift
import Foundation
import UIKit

extension App.Color {
    static var mainColor = UIColor.lightGray
    static var textColor = UIColor.black
    static var titleColor = UIColor.blue
    
    //....
}

Tất cả chúng đều khai báo là static và struct. Để sử dụng thì bạn có thể tham khảo như sau:

let name = App.Text.appName

let color = App.Color.mainColor

3.3. Controllers

Thư mục này sẽ chứa các View Controller của project. Tham khảo hình sau

Có một thư mục là Base. Tai đây chính là các super class của các Controller sử dụng trong project.

  • BaseViewController.swift
    • Kế thừa từ UIViewController
    • Các View Controller khác sẽ kế thừa lại nó.
    • Chứa các function xử lý chung cho tất cả các View Controller con.
      • setupUI xử lý các UI lúc khởi tạo View Controller
      • setupDataxử lý phần Data, load Data, fetch Data ….
import UIKit

class BaseViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setupData()
        setupUI()
    }
    
    func setupData() {}
    
    func setupUI() {}

}
  • BaseNavigationController.swift
    • Tương tự như BaseViewController, nhưng kế thừa trực tiếp từ UINavigationController
    • Nếu không làm gì thì bạn có thể để im hoặc xoá đi
import UIKit

class BaseNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}
  • BaseTabBarController.swift
    • Kế thừa từ UITabBarController và cũng tương tự 2 em trên
import UIKit

class BaseTabBarController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

}

Lưu ý không tạo file *.xib cho các Base class này. Vì chúng chỉ mang tính chất là super class. Không có thể hiện của mình. Và dùng kế thừa lại cho các class con.

Sử dụng như sau:

  • HomeViewController.swift
    • Kế thừa lại BaseViewController
    • Yêu cầu cài đặt 2 function cần thiết: setupData & setupUI
    • Vì khi khởi tạo xong thì function viewDidLoadđược chạy. Nó lại gọi super.viewDidLoad(), thì ở BaseViewController sẽ gọi 2 function setupData và setupUI. Vì đã override lại chúng rồi, nên 2 function ở lớp con sẽ được.
import UIKit

class HomeViewController: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func setupUI() {
        title = App.Text.titleHome
    }
    
    override func setupData() {
        
    }

}
  • Xét root của Window
    • Thay vì dùng UINavigationController, chúng ta dùng lớp Base của nó vừa mới tạo ra.
let vc = HomeViewController()
let navi = BaseNavigationController(rootViewController: vc)

window.rootViewController = navi

Tuỳ thuộc vào dự án của bạn có bao nhiêu màn hình. Thì bạn sẽ tạo ra thêm như vậy.

  • ViewModel
    • Nó sẽ ở cùng thư mục với View / View Controller của bạn.
    • Mục đích cho dễ tìm kiếm
    • Sử dụng hậu tố ViewModel cho đặt tên các class này.

Tham khảo ViewModel cho HomeViewController và HomeCell

3.4. Views

Phần này sẽ chứa các class View phục vụ cho cả project. Chúng không phải là View trong MVC hay MVVM. Không thuộc quản chế của bất cứ class hay Controller nào. Ta có thể chia làm 3 loại sau:

3.4.1. Custom View

Ta sẽ sử dụng rất nhiều các Custom View trong dự án. Ví dụ tham khảo qua hình sau:

Chú ý việc sử dụng hậu tố View , cho cách đặt tên file. Để có thể nhận biết nhanh chúng là một custom view.

3.4.2. Cells

Chính là các cell của TableView & CollectionView. Để cho dễ tìm thì chúng ta sẽ để chúng ở gần với View Controller của nó.

Sử dụng hậu tố Cell để phân biệt chúng với các View khác.

3.4.3. Common Control

Nếu bạn cần phải tạo ra các Control dùng chung cho toàn bộ project thì hay tạo ra nó. Ví dụ cho việc sử dụng các Button và Lable chung.

Ví dụ code

  • Button.swift
import UIKit

class Button: UIButton {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

}
  • Label.swift
import UIKit

class Label: UILabel {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */

}

3.5. Models

Phần này sẽ có rất nhiều thứ, nhiều class, model, file managers, các bộ core … Nhưng ta có thể chia thành 3 nhóm chính:

  • Objects
  • Manages
  • Core

Tham khảo hình sau:

3.5.1. Object & Class

Là thư mục chứa các class object. Là các lớp đối tượng dữ liệu, phục vụ cho nhiều màn hình trong project.

Ví dụ như:

  • User.swift
import Foundation

final class User {
    var name: String
    var age: Int
    var gender: Bool
    
    init(name: String, age: Int, gender: Bool) {
        self.name = name
        self.age = age
        self.gender = gender
    }
}
  • Music.swift
import Foundation
import UIKit

final class Music {
    var id: String
    var artistName: String
    var releaseDate: String
    var name: String
    var artworkUrl100: String
    var thumbnailImage: UIImage?
    
    init(json: JSON) {
        self.id = json["id"] as! String
        self.artistName = json["artistName"] as! String
        self.releaseDate = json["releaseDate"] as! String
        self.name = json["name"] as! String
        self.artworkUrl100 = json["artworkUrl100"] as! String
    }
}

Ở trên chỉ là ví dụ mang tính chất tham khảo, nên bạn tuỳ ý thêm vào hay bớt ra theo những gì bạn muốn. Cần chú ý:

Từ khoá final cho việc khai báo class. Để xác định nó không thể kế thừa. Điều này làm tăng tốc độ build của Swift.

3.5.2. Managers

Thư mục Managers sẽ chứa các class liên quan tới việc tương tác tập trung của một thư viện, hay 1 class khác, hay 1 thao tác với hệ thống…. Theo ví dụ ở hình trên thì:

  • DataManager.swift : xử lý việc tương tác với UserDefault
import Foundation

class DataManager {
    
    //singleton
    private static var sharedDataManager: DataManager = {
        let dataManager = DataManager()
        return dataManager
    }()
    
    class func shared() -> DataManager {
        return sharedDataManager
    }
    
    // init
    private init() {}
    
    //open database
    func read() -> (String, String) {
        let email = UserDefaults.standard.string(forKey: "email") ?? ""
        let password = UserDefaults.standard.string(forKey: "password") ?? ""
        return (email, password)
    }
    
    //save database
    func save(email: String, password: String) {
        UserDefaults.standard.set(email, forKey: "email")
        UserDefaults.standard.set(password, forKey: "password")
        UserDefaults.standard.synchronize()
    }
}
  • FileManager.swift : làm việc với file
  • LocationManager.swift : làm việc với thư viện CoreLocation
  • AudioManager.swift : làm việc với các file âm thanh

Mình chỉ ví dụ cho 1 file thôi và cũng là mang tính chất tham khảo. Nhưng bạn cần chú ý các điểm sau cho các class Manager:

  • Thường sẽ sử dùng chung và phục vụ nhiều ViewController hay ViewModel
  • Sử dụng Singleton Pattern là chính
  • Mang tính bao đóng lại các thao tác, chứ không viết thêm nhiều các chức năng mới.
  • Đồng nhất trong việc sử dụng call back cho phù hợp:

3.5.3. Service & Core

Phần này như đã trình bày ở bài Core API. Nó mang tính chất lớn hơn phần Manager nhiều, nhưng chưa đủ để thành một framework. Có tính dùng đi dùng lại trong nhiều project. Nên

  • Phần Login Model : thường sẽ bất biến trong nhiều project
  • Phần Business Model : sẽ thay đổi đối với từng project khác nhau

Qua hình trên, phần Core phục vụ cho tương tác với API được add vào thư mục Models. Và thường sẽ có ít bộ Core được thêm vào nên chúng ta không cần dùng tiền tố Core để đặt tên cho thư mục.

3.6. Extension

Đây là thư mục chứa phần mở rộng cho các class có sẵn và các class của hệ thống.

Các file này không phải là class. Mà là các extension của các class. Mục đích là thêm các function mà không phải kế thừa lại class đó. Mục đích để phục vụ cho project, tránh việc phải viết đi viết lại nhiều lần.

Ví dụ:

  • Data.Ext.swift
    • Biết Data thành JSON
import Foundation

typealias JSON = [String: Any]

extension Data {
    func toJSON() -> JSON {
        var json: [String: Any] = [:]
        do {
            if let jsonObj = try JSONSerialization.jsonObject(with: self, options: .mutableContainers) as? JSON {
                json = jsonObj
            }
        } catch {
            print("JSON casting error")
        }
        return json
    }
}
  • Color.Ext.swift
    • Thêm hàm khởi tạo 1 UIColor từ hex string
import UIKit

extension UIColor {
    convenience init(hexString: String) {
        let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int = UInt64()
        Scanner(string: hex).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            (a, r, g, b) = (255, 0, 0, 0)
        }
        self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
    }
}
  • String.Ext.swift
    • trim string
    • encode & decode
    • base64
import Foundation

enum Process {
    case encode
    case decode
}

extension String {

    var trimmed: String {
        return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
    }

    func base64(_ method: Process) -> String? {
        switch method {
        case .encode:
            guard let data = data(using: .utf8) else { return nil }
            return data.base64EncodedString()
        case .decode:
            guard let data = Data(base64Encoded: self) else { return nil }
            return String(data: data, encoding: .utf8)
        }
    }
}

extension String {

    /// Initializes an NSURL object with a provided URL string. (read-only)
    var url: URL? {
        return URL(string: self)
    }

    /// The host, conforming to RFC 1808. (read-only)
    var host: String {
        if let url = url, let host = url.host {
            return host
        }
        return ""
    }
}

Muốn thêm gì thêm thì bạn hãy thêm vào. Tuy nhiên, bạn đừng lạm dụng nó quá nhiều. Vì nhiều function hay extension không cần thiết thì cũng không cần tạo ra.

3.7. Framework & Libraries

Là các thư viện từ bên ngoài được thêm vào project. Chúng ta có 2 cách thêm:

  • Thông qua CocoaPod:
    • Nhanh gọn lẹ, không cần suy nghĩ nhiều
    • Tuy nhiên mỗi lầm thay đổi Podfile thì phải chạy lại lệnh pod install
    • Đồng nhất môi trường dev giữa các thành viên
  • Thông qua việc thêm file trực tiếp vào project.
    • Phải thêm file bằng tay. Thêm vào thư mục Libs
    • Nhanh nhưng không gọn lẹ được
    • Khó khăn nếu thư viện bắt bạn phải config nhiều thứ trong settings
    • Có thể custom một cách dễ dàng, trực tiếp edit code của thư viện

Ngoài ra, có thêm một số dịch vụ tương tự như CocoaPods:

  • Carthage
  • Swift Package Manager

Bạn có thể cài cùng lúc 3 dịch vụ trên vào chung project vẫn không sao. Mỗi cái có ưu và nhược riêng.

Khi thêm thư viện bạn cần chú ý các vấn đề sau:

  • Bản quyền của thư viện
  • Độ phổ biến của thư viện
  • Thời gian update hay còn được support hay không
  • Version của thư viện tương thích với môi trường của project hay không

Nên đọc README của thư viện trước khi sử dụng.

Ví dụ cho Almofire

3.8. Resources

Phần này sẽ chứa các tài nguyên cho project sử dụng. Bao gồm các file như:

  • Hình ảnh
  • Âm thanh
  • Video
  • Font chữ

Vì giai đoạn gần đây thì file Assets.xcassets của Xcode đã rất mạnh mẽ. Support cho bạn rất nhiều, như:

  • Images
  • Splash
  • Scale image
  • Vector & Render
  • Apple Device
  • Text
  • ARKit
  • GameKit
  • icon app
  • Stickers app
  • Size image (1x, 2x, 3x)
  • Light & Dark Mode
  • …

Nên bạn nên tận dụng nó tối đa nhất có thể. Lúc nào không sử dụng được nữa thì hãy thêm file trực tiếp vào project của bạn.

Cuối cùng, toàn bộ bài viết mang tính chất tham khảo. Bạn hãy tạo ra một template riêng của bạn. Xem lại toàn bộ cây thư mực của project và có thể checkout mã nguồn tại đây. Chúc bạn thành công!

Tạm kết

  • Các thành phần cần thiết trong project
  • Ý nghĩa các thành phần
  • Một số quy tắc áp dụng như: đặt tên class, đối tượng, hậu tố, tiền tố …

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

FacebookTweetPinYummlyLinkedInPrintEmailShares18

Related Posts:

  • RxCocoa Basic - Working with multi UI Control
    RxCocoa Basic - Working with multi UI Control
  • RxCocoa Basic – Binding Observables
    RxCocoa Basic – Binding Observables
  • RxCocoa Basic – Merge Observables Input
    RxCocoa Basic – Merge Observables Input
  • Sử dụng Custom UIView vào SwiftUI Project - SwiftUI Notes #16
    Sử dụng Custom UIView vào SwiftUI Project - SwiftUI Notes…
Tags: basic ios tutorial, iOS
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:

  • Basic Grid Layout - SwiftUI Notes #52
    Basic Grid Layout - SwiftUI Notes #52
  • Sử dụng Custom UIView vào SwiftUI Project - SwiftUI Notes…
    Sử dụng Custom UIView vào SwiftUI Project - SwiftUI Notes #16
  • RxCocoa Basic – Extend UIKit
    RxCocoa Basic – Extend UIKit
  • Basic TextField - SwiftUI Notes #27
    Basic TextField - SwiftUI Notes #27
  • 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

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!