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

Declaring Data – SwiftUI Notes #12

SwiftUI

Contents

  • Chuẩn bị
  • 1. Declaring Data dependencies
    • Data access = dependency
    • Single source of truth
  • 2. Property wrappers
    • @State
    • @Binding
    • @ObservedObject
    • @EnvironmentObject
  • 3. Value Type
    • 3.1. Basic Type
    • 3.2. Struct
    • 3.3. Binding
  • 4. Reference type
    • 4.1. Protocol ObservableObject
    • 4.2. @StateObject
    • 4.3. @ObservedObject
  • 5. Environment
    • 5.1. Setup
    • 5.2. Implement
    • 5.3. Over and Over
  • Tóm tắt các Property Wrappers
  • Tạm kết

Chào bạn đến với Fx Studio. Chúng ta đã lang thang khá nhiều trong thế giới SwiftUI rồi. Chủ đề lần này đề cập tới phần dữ liệu được sử dụng cho ứng dụng của SwiftUI. Nó được gọi là Declaring Data.

Vì đây là một bài viết có liên quan tới Declarative App với SwiftUI. Nếu bạn chưa biết về Declarative App thì hãy đọc các bài viết sau đây trước:

    • Declarative app development
    • Declaring View

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

Bắt đầu thôi!

Chuẩn bị

Về môi trường cho demo, bạn có thể tham khảo như sau:

    • Xcode 12
    • Swift 5.3
    • SwiftUI 2.0

Vẫn là những yêu cầu cơ bản cho version các tools mà ta cần sử dụng. Bạn có thể checkout code tại đây:

    • Toàn bộ repo: https://github.com/fx-studio/swiftui-notes

Về demo cho bài viết này, ta sẽ cần tạo mới một project với giao diện cực kì đơn giản. Chủ yếu tập trung vào luồng dữ liệu hoạt động như thế nào trong project với SwiftUI.

1. Declaring Data dependencies

Với SwiftUI, các ứng dụng của chúng ta đang phát triển thuộc thể loại Declarative App. Do đó, về luồng dữ liệu hoạt động trong App cũng phải thuộc trường phái Declarative Programming. Bạn phải dần quên đi cách truyền/gán dữ liệu theo truyền thống. Và nếu như bạn đã biết về Reactive Programming thì đó là một lợi thế.

À, nếu bạn chưa biết về 2 thư viện Reactive Programming cho Swift hay iOS, thì có thể tham khảo các bài viết sau:

    • Combine
    • RxSwift

Còn với SwiftUI, ta có 2 nguyên tắc hướng dẫn cách để quản lý luồng dữ liệu như sau:

Data access = dependency

  • Việc đọc dữ liệu sẽ đến từ View của bạn và sẽ tạo ra mối quan hệ phụ thuộc giữa View & Data
  • Mọi View được khởi tạo hay khai báo, thì sẽ đi kèm với các dữ liệu phục thuộc vào nó. Nó có thể là các input hay state của View
  • Khi dữ liệu phụ thuộc thay đổi, thì giao diện hiển thị của View sẽ tự động thay đổi theo

Single source of truth

  • Được xem là nguồn sự thật chân lý (mình gọi tên cho vui ngen). Nơi sẽ chứa dữ liệu của 1 View nào đó.
  • Dữ liệu của 1 View là nguồn dữ liệu mà của riêng nó hoặc từ một View khác bên ngoài.
  • Bất kể nằm ở đâu thì nguồn này là duy nhất.
  • Nguồn sẽ quản lý việc cập nhật lại giá trị của dữ liệu. Và nguồn sẽ gây ảnh hưởng lên 1 hoặc nhiều View mà có sự liên kết với nguồn này.

2. Property wrappers

Cái tên của Property wrapper cũng đã mang nhiều ý nghĩa, Property wrapper là vỏ bọc của Property. Về cơ bản, Property wrapper là một cấu trúc dữ liệu, nó sẽ đóng gói Property, và bổ xung thêm một vài chức năng cho Property đó. Property wrapper có thể là dạng struct, class hoặc enum. Trước Swift 5.1, Swift đã có nhiều builtin wrappers như lazy, @NSCopying, và SwiftUI cũng có nhiều builtin wrappers như @State, @Binding,… Từ Swift 5.1 trở đi, chúng ta có thể viết thêm các custom Property wrapper để phục vụ mục đích của mình

Ở trên là định nghĩa sơ lượt nhất về Property wrappers. Ta sẽ đề cập tới nó, vì SwiftUI sẽ sử dụng rất nhiều và thường xuyên các wrappers này trong code. Và phần được dùng nhiều nhất chính là Declaring Data này. Ta sẽ đi qua lần lượt các wrappers như sau.

@State

  • Các biến/thuộc tính tạo với wrapper @State sẽ thuộc chính View đó.
  • Đây là một biến lưu trữ dữ liệu. Nên bạn cần cấp phát giá trị ban đầu khi khai báo loại biến này.
  • Nó chỉ dùng cho các giá trị kiểu value type đơn giản hay cơ bản. Như: Int, Float, Double, String …
  • Khuyến cáo, nên sử dụng private cho nó.

@Binding

  • @Binding được sử dụng để khai báo một biến/thuộc tính, mà có sự phụ thuộc vào một biến @State từ một View khác.
  • Sử dụng từ khoá $ để gán giá trị cho biến Binding của View vào lúc View được khởi tạo.
  • Đây là một biến tham chiếu đến dữ liệu. Do đó, bạn không cần cấp phát giá trị khởi tạo ban đầu cho nó.
  • Tham chiếu này cho phép bạn có thể thay đổi giá trị của nguồn sự thật chân lý từ bất cứ View nào mà có cụ ràng buộc dữ liệu với nguồn.

@ObservedObject

  • Dùng để khai báo một biến với kiểu dữ liệu tham chiếu (class).
  • Kiểu của biến (class type) phải implement giao thức ObservableObject
  • Nó sẽ yêu cầu implement các thuộc tính objectWillChange để có thể phát dữ liệu thay đổi của nó đi. Từ khoá sẽ là @Published cho thuộc tính đó.

@EnvironmentObject

  • Khai báo sự phụ thuộc vào một số dữ liệu được chia sẽ (share data) cho toàn project.
  • Giống như Singleton
  • Là cách đơn giản nhất mà bạn có thể truyền dữ liệu đi bất cứ nơi nào. Mà không cần phải quan tâm tới việc truyền/gán/khởi tạo hay bắt buộc phải dùng chúng.

3. Value Type

3.1. Basic Type

Với kiểu dữ liệu tham trị (value type) cơ bản, chúng ta hay quen sử dụng như là Int, Float, Double … Ta sẽ dùng @State để khai báo dữ liệu phụ thuộc cho View của mình. Bạn tham khảo code ví dụ sau:

struct ContentView: View {
    @State var count: Int = 0
    
    var body: some View {
        Button(action: {
            count += 1
        }, label: {
            Text("Tap me!\n\(count)")
                .font(.title)
                .foregroundColor(Color.white)
                .multilineTextAlignment(.center)
        })
        .padding()
        .background(Color(.blue))
    }
}

Ta khai báo thuộc tính count của ContentView với từ khoá @State. Tại đây, phần UI phụ thuộc vào đó chính là Text. Nó sẽ hiển thị giá trị của thuộc tính count đó. Quan trọng nhất là mọi thứ sẽ tự động cập nhật với nhau giữa View & Data.

Hãy bấm Live Preview và xem Text có cập nhật lại giá trị từ count hay không nha!

3.2. Struct

Ngoài ra, còn một kiểu dữ liệu tham trị nữa, đó là struct. Ta sẽ khai báo một Struct cơ bản. Nó được tạo thành từ các kiểu dữ liệu đơn giản như ở trên. Ta khai báo thêm một Struct như sau:

struct Score {
    var count: Int = 0
}

Bạn hãy cập nhật lại thuộc tính count được khai báo ở trên, với kiểu giá trị mới là Score.

@State var item: Score = Score()

Bạn thay đổi lại ContentView sao cho phù hợp với kiểu dữ liệu mới cho thuộc tính item. Tham khảo đoạn code sau:

struct ContentView: View {
    @State var item: Score = Score()
    
    var body: some View {
            Button(action: {
                item.count += 1
            }, label: {
                Text("Tap me!\n\(item.count)")
                    .font(.title)
                    .foregroundColor(Color.white)
                    .multilineTextAlignment(.center)
            })
            .padding()
            .background(Color(.blue))
        }
}

Bạn hãy bấm Live Preview lại và test như ở trên. Giải thích đơn giản như sau:

Chúng ta sẽ dùng .count và thay đổi giá trị của nó. Mặc dù, bạn thay đổi giá trị thuộc tính của một đối tượng Struct. Tuy nhiên, vì nó kiểu dữ liệu giá trị. Nên đối tượng không cập nhật lại giá trị cho thuộc tính, thay vào đó một vùng nhớ mới tạo ra. Với giá trị thuộc tính mới được cập nhật. Quan trọng cơ chế này giống như việc bạn sử dụng các kiểu dữ liệu cơ bản.

Như vậy với Value Type thì ta vẫn dùng tốt với Declaring Data bằng SwiftUI.

3.3. Binding

Thêm một chút nữa để kết thúc phần Value Type của Declaring Data này, là việc truyền dữ liệu đi qua các View. Hay còn nói cách khác, là chúng ta sẽ sử dụng tới

Single source of truth

Công việc của ta sẽ tạo thêm 1 View, mình chọn cái tên hơi chuối là StateView. Bạn tham khảo code của nó như sau:

struct StateView: View {
    @Binding var item: Score
    
    var body: some View {
        Button(action: {
            item.count += 1
        }, label: {
            Text("Tap me!\n\(item.count)")
                .font(.title)
                .foregroundColor(Color.white)
                .multilineTextAlignment(.center)
        })
        .padding()
        .background(Color(.blue))
    }
}

Trong đó:

  • Bạn clone lại code ContentView ở trên, đưa nó vào StateView
  • Thay từ khoá @State bằng @Binding cho thuộc tính item
  • Ý nghĩa các View vẫn giống ở trên.

Bạn quay về ContentView, thay đổi lại một chút cho body của nó như sau:

struct ContentView: View {
    @State var item: Score = Score()
    
    var body: some View {
        StateView(item: $item)
    }
}

Chú ý việc khởi tạo StateView, cần:

  • Truyền giá trị cho thuộc tính của StateView là item
  • Thêm từ khoá $ để liên kết với nguồn dữ liệu chân lý item

Bạn bấm Live Preview và cảm nhận kết quả nha!

Declaring Data

4. Reference type

Về kiểu dữ liệu, bên cạnh kiểu dữ liệu tham trị (value type) thì chúng ta còn kiểu dữ liệu tham chiếu (reference type). Với kiểu này, việc áp dụng 2 nguyên tắc của Declaring Data sẽ hơi phức tạp hơn một chút. Bạn cần thêm một chút kiến thức của Combine vào đây thì mới dễ hiểu hơn. Và mình xin phép trình bày cơ bản cách sử dụng Reference Type trong SwiftUI.

4.1. Protocol ObservableObject

Với một đối tượng là kiểu tham chiếu, thì nó sẽ trỏ tới một vùng nhờ nào đó. Đối với cách tiếp cận Declarative App, chúng ta sẽ quan tâm tới việc thay đổi giá trị & giá trị thay đổi của vùng nhớ đó.

Để bắt đầu, ta tạo một class mới có tên là User. Code ví dụ như sau:

class User: ObservableObject {
    @Published var name: String
    @Published var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

Trong đó:

  • Bạn cần phải implement protocol ObservableObject. Có như vậy, đối tượng của chúng ta mới có thể lắng nghe được từ bên ngoài.
  • Tiếp theo, bạn xác định thuộc tính nào cho phép phát dữ liệu đi, thì hãy khai báo thêm với từ khoá @Published
  • Việc phát dữ liệu đi sẽ xảy ra trước khi vùng nhớ của thuộc tính bị thay đổi. Chính là objectWillChange

4.2. @StateObject

Bạn tạo một View mới, đặt tên gì cũng được. Mình đặt tên là DemoReferenceView và code ví dụ như sau:

struct DemoReferenceView: View {
    
    @StateObject var user = User(name: "Fx Studio", age: 18)
    
    var body: some View {
        VStack {
            Text("\(user.age) years old")
                .font(.title)
                .foregroundColor(Color.blue)
                .padding()
            UserView(user: user)
                .frame(width: 100, height: 175)
                .padding()
            HStack{
                Button(action: {
                    user.age += 1
                }, label: {
                    Text("+")
                        .font(.title)
                        .foregroundColor(Color.red)
                        .padding()
                        .background(Color(.lightGray))
                })
                Button(action: {
                    user.age -= 1
                }, label: {
                    Text("-")
                        .font(.title)
                        .foregroundColor(Color.red)
                        .padding()
                        .background(Color(.lightGray))
                })
            }
        }
    }
}

Bạn tạm thời chưa cần quan tâm tới UserView. Trong đoạn code trên thì ta chú ý 2 điều.

  • Thuộc tính user với khai báo @StateObject.
@StateObject var user = User(name: "Fx Studio", age: 18)

Nó cũng tương tự như @State. Nhưng lần này bạn có quyền dùng với các thuộc tính là kiểu tham chiếu và kiểu dữ liệu phức tạp hơn. Nó biến thuộc tính hay biến đó thành nguồn sự thật chân lý. Là nơi cho các View ràng buộc dữ liệu vào.

  • Từ khoá $ , ta không cần tới nó trong trường hợp này. Vì lẽ bản thân của thuộc tính đã là tham chiếu rồi.
UserView(user: user)

Ngoài ra, trong các action của Button, ta sẽ thay đổi giá trị thuộc tính age của đối tượng user. Mục đích xem nó có ảnh hưởng tới hiển thị của UserView hay không.

4.3. @ObservedObject

Thay vì dùng @Binding để liên kết với dữ liệu phụ thuộc ở một View khác, ta sử dụng từ khoá @ObservedObject. Cũng tương tự như với Binding, lần này xác nhận thuộc tính của bạn sẽ lắng nghe sự thay đổi từ nguồn sự thật chân lý. Và có một số chú ý sau:

  • Dùng đối với kiểu dữ liệu tham chiếu và kiểu dự liệu custom
  • Chỉ nhận được giá trị thay đổi của những thuộc tính nào có khai báo @Published
  • Không cần gán giá trị ban đầu cho lúc khởi tạo thuộc tính
  • Khi thay đổi giá trị từ một View, nó sẽ ảnh hưởng tới tất cả các View mà có ràng buộc với dữ liệu đối tượng đó

Ta sẽ tiến hành cài đặt nó vào trong UserView. Bạn hãy tạo file SwiftUI mới có tên là UserView. Code ví dụ như sau:

struct UserView: View {
    
    @ObservedObject var user: User
    
    var body: some View {
        VStack {
            Image(systemName: "person.crop.square")
                .resizable()
                .foregroundColor(.blue)
            Text(user.name)
                .fontWeight(.bold)
                .multilineTextAlignment(.center)
            Text("\(user.age)")
                .fontWeight(.bold)
                .multilineTextAlignment(.center)
            Button(action: {
                user.age += 1
            }) {
                Text("Tap me!")
            }
        }
    }
}

Ta cài đặt @ObservedObject cho thuộc tính user của UserView. Các View con của nó sẽ ràng buộc dữ liệu với các thuộc tính trong đối tượng user. Cuối cùng, action của Button sẽ thay đổi giá trị của thuộc tính. Mục đích của ta là xem phạm vi ảnh hưởng của nó tới nhiều View.

Bạn hãy bấm Live Preview và kiểm tra kết quả.

Declaring Data

Bạn hãy bình tĩnh và test cẩn thận nha. Phần này dễ sai lắm và dễ cấu lắm. Ahihi!

5. Environment

Cuối cùng trong Declaring Data, bạn sẽ đến phần tổ chức và truyền dữ liệu đi khắp nơi trong project SwiftUI (share data). Đó là các biến/thuộc tính với khai báo là @EnvironmentObject. Thao tác này của chúng ta hay còn được gọi là tự tạo biến môi trường của riêng mình.

Chúng ta sẽ tiếp tục demo theo các bước, để có thể hiểu hơn về chúng nó.

5.1. Setup

Bắt đầu, bạn sẽ cài đặt cho việc sử dụng tới các kiểu dữ liệu cho biến môi trường mới này. Mình sẽ tạo 1 class với tên là GameSetting. Đặc điểm của đối tượng này sẽ là duy nhất trong cả project. Mình tin chắc nếu vào bài toán thực tế, bạn cũng chỉ cần 1 đối tượng duy nhất mà thôi.

Bạn xem code ví dụ sau:

class GameSettings: ObservableObject {
    @Published var score = 0
}

À, khai báo class này thì cũng giống y với việc sử dụng tham chiếu trong Declaring Data. Đọc lại mục trên nếu bạn quên nha. Tiếp theo, bạn tạo một View đơn giản. Mình gọi nó là ScoreView và code ví dụ của nó như sau:

struct ScoreView: View {
    @EnvironmentObject var settings: GameSettings
    
    var body: some View {
        Text("Score: \(settings.score)")
    }
}

Trong đó:

  • Từ khoá @EnvironmentObject dùng để khai báo một thuộc tính là biến môi trường của một View
  • Không cần gán giá trị ban đầu cho thuộc tính đó
  • Mọi thứ bạn cài đặt vẫn giống như mọi khi

5.2. Implement

Việc cài đặt & sử dụng biến môi trường mới như sau. Trước hết, ta sẽ tạo ra một View ban đầu, để là nơi khởi tạo các View và giá trị cần thiết. Mình đặt tên là GameView và code ví dụ như sau:

struct GameView: View {
    @StateObject var settings = GameSettings()

        var body: some View {
            NavigationView {
                VStack {
                    Text("Score: \(settings.score)")
                        .font(.title)
                    
                    Button(action: {
                        settings.score += 1
                    }, label: {
                        Text("Increase Score")
                            .padding()
                    })

                    NavigationLink(destination: ScoreView()) {
                        Text("Show Detail View")
                            .font(.title)
                    }
                }
                .frame(height: 200)
            }
            .environmentObject(settings)
        }
}

Theo nguyên tắc về nguồn sự thật chân lý, cho dù là một biến môi trường thì nó vẫn phải có và chỉ có duy nhất một nguồn mà thôi. Cũng vì là kiểu dữ liệu của GameSettings là tham chiếu (khai báo class), nên ta sử dụng từ khoá @StateObject cho thuộc tính settings của View.

@StateObject var settings = GameSettings()

Bạn vẫn sử dụng biến như bình thường và sử dụng ràng buộc dữ liệu với các view con của nó. Nhưng điểm đặc biệt, bạn chú ý tới việc khởi tạo của ScoreView(). Chúng ta không cần phải truyền đối số vào hàm khởi tạo của nó. Và cách đơn giản là bạn chỉ cần gọi tới modifier environmentObject(setting) là ổn.

Quá đơn giản và hiệu quả.

Bạn bấm Live Preview để kiểm tra kết quả nha.

  • Ở GameView bấm tăng score vài lần

  • Vào màn hình ScoreView xem giá trị score có đúng như ở bên ngoài không

5.3. Over and Over

Để khai thác thêm tính hiệu quả của @EnvironmentObject này. Bạn thử thay đổi một chút code của ScoreView như sau.

struct ScoreView: View {
    @EnvironmentObject var settings: GameSettings
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Score: \(settings.score)")
                    .font(.title)
                
                Button(action: {
                    settings.score += 1
                }, label: {
                    Text("Increase Score")
                        .padding()
                })

                NavigationLink(destination: ScoreView()) {
                    Text("Show Detail View")
                        .font(.title)
                }
            }
            .frame(height: 200)
        }
        .environmentObject(settings)
    }
}

Thực chất, bạn copy lại code ở GameView sang ScoreView thôi à.

  • Nhưng điểm cần chú ý, đó là bạn lại gọi tới một ScoreView trong ScoreView.
  • Nhưng điểm quan trọng, là bạn truyền giá trị của biến môi trường xuyên qua rất nhiều View.

Mục đích chính vẫn là xem việc sử dụng và cập nhật lại giá trị của biến sẽ ảnh hưởng như thế nào tới toàn bộ các View. Bạn hãy bấm Live Preview và tận hưởng kết quả nào!

  • Bạn vào lại ScoreView lần từ nhất và bắt đầu tại GameView mà sau khi nhấn tăng score vài lần

  • Sau vài lần thử kích để vào ScoreView và kết quả hiển thị như hình dưới.

Declaring Data

Giá trị của biến môi trường đã truyền qua các View một cách đồng nhất. Okay, mọi View đều cập nhật đúng giá trị. Và bất cứ View nào cũng thay đổi được giá trị của biến môi trường. Ưu điểm chính của biến môi trường này là đơn giản và tiện lợi.

 

Tóm tắt các Property Wrappers

  • @State Sử dụng có các thuộc tính của View và kiểu dữ liệu đơn giản (Int, Float, String …). Nên sử dụng private cho khai báo này.
  • @Binding dùng để khai báo các thuộc tính mà ràng buộc dữ liệu với thuộc tính ở một View khác. Chính là nơi khai báo @State. Và gán bằng từ khoá $
  • Sử dụng khai báo @StateObject dành cho các thuộc tính với kiểu dữ liệu tham chiếu để biến nó thành nguồn sự thật chân lý. Và giá trị của nó sẽ ảnh hưởng tới các View mà ràng buộc dữ liệu với nó.
  • Class khai báo với @StateObject thì cần phải implement Protocol ObservableObject. Xác nhận rằng đối tượng của class sẽ lắng nghe được từ bên ngoài.
  • Bạn muốn thuộc tính nào của class ObservableObject có thể lắng nghe được từ bên ngoài. Thì khai báo thêm @Published cho thuộc tính đó. Và khi thay đổi giá trị của đối tượng thì sẽ ảnh hưởng tới các View ràng buộc với chính nó.
  • @ObservedObject dùng để khai báo thuộc tính của View, mà thuộc tính đó sẽ liên kết với nguồn sự thật chân lý (với kiểu tham chiếu) từ một View bên ngoài. Nơi mà biến/thuộc tính được khai báo với @StateObject.
  • @EnvironmentObject tạo ra một biến/thuộc tính là biến môi trường. Đây là cách đơn giản và hiệu quả nhất để truyền dữ liệu đi qua nhiều View. Nó tương tự Singleton truyền thống. Và bạn không cần truyền đối số khi khởi tạo View. Đây là nguồn dữ liệu chia sẽ (share data) trong Declaring Data.

Tạm kết

Quả thật là đau não với đám Declaring Data này. Nhưng qua bài viết này, bạn có thể nắm được những điểm sau:

  • Hai nguyên tắt cơ bản của Declaring Data
  • Phân loại các kiểu dữ liệu được sử dụng trong SwiftUI App
  • Các liên kết dữ liệu giữa các View với nhau
  • Tạo được các dữ liệu chia sẽ 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!

FacebookTweetPinYummlyLinkedInPrintEmailShares8

Related Posts:

  • RxSwift vs. UIKit – Working with Cache Data
    RxSwift vs. UIKit – Working with Cache Data
  • Thread safety & data race - Swift
    Thread safety & data race - Swift
  • SwiftUI - Phần 7 : State & Data Flow
    SwiftUI - Phần 7 : State & Data Flow
  • Tổng quan về State & Data Flow - SwiftUI Notes #32
    Tổng quan về State & Data Flow - SwiftUI Notes #32
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:

  • MainActor và điều gì xảy ra với Data trên Main Thread
    MainActor và điều gì xảy ra với Data trên Main Thread
  • SwiftUI - Phần 7 : State & Data Flow
    SwiftUI - Phần 7 : State & Data Flow
  • Thread safety & data race - Swift
    Thread safety & data race - Swift
  • RxCocoa Basic – Display Data from API
    RxCocoa Basic – Display Data from API
  • RxCocoa Basic - Display Data
    RxCocoa Basic - Display Data

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!