diskusi.tech (beta) Community

loading...

Bikin List View dari Enum Pakai Swift Generics

nicohyperjump profile image Nico Prananta ・3 min read

Semenjak Apple meluncurkan SwiftUI di September 2019, saya sangat menikmati membuat aplikasi iOS menggunakan SwiftUI. Salah satu alasannya adalah karena SwiftUI sifatnya deklaratif seperti menggunakan React. Ditambah lagi, banyak fitur dari bahasa pemrograman Swift yang saya sukai, seperti Enumerations, Protocols, Generics, dan banyak lagi.

Di sesi pertama Swift Study Group Indonesia, saya menceritakan tentang temuan saya menggunakan Enums dan Generics di SwiftUI. Lebih tepatnya saya memaparkan bagaimana cara membuat List View dari Enums.

Misalnya, saya mempunyai sebuah Enum bernama MugiwaraPirates (Ada yang fans One Piece juga??). Di dalam enum ini, kita mendefinisikan anggota dari Mugiwara Pirates seperti Luffy, Zoro, Sanji dan seterusnya. Lalu dari enum ini, saya ingin bisa menampilkan List view menggunakan SwiftUI secara mudah.

Alt Text

Untuk bisa melakukan ini, enum MugiwaraPirates harus memenuhi protokol CaseIterable agar kita bisa mendapatkan semua opsi di dalam enum tersebut.

struct ListView: View {

    var title: String = ""
    @Binding var selectedItem: MugiwaraPirates?

    var body: some View {
        NavigationView {
            List {
                ForEach(MugiwaraPirates.allCases, id: \.rawValue) { option in
                    Button(action: {
                        selectedItem = option
                    }, label: {
                        HStack {
                            Text(option.description)
                            Spacer()
                            if selectedItem != nil && selectedItem! == option {
                                Image(systemName: "checkmark")
                            }
                        }
                    })
                }
            }.navigationBarTitle(title)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ListView(title: "Mugiwara Pirates", selectedItem: .constant(nil))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Di kode di atas, saya mengiterasi semua case dari enum MugiwaraPirates menggunakan MugiwaraPirates.allCases dan untuk setiap case saya membuat sebuah Button yang isinya nama case dan gambar checkmark bila case tersebut terpilih. ListView komponen sudah berjalan sesuai yang saya inginkan.

Namun bagaimana bila saya ingin menggunakan ListView tersebut untuk Enum lain? Misalnya enum PiratesBounty seperti berikut.

enum PiratesBounty: Int, CaseIterable, CustomStringConvertible {
    case luffy = 1500000000
    case blackbeard = 2247600000
    case shanks = 4048900000
    case bigMom = 4388000000
    case kaido = 4611100000
    case whitebeard = 5046000000
    case roger = 5564800000

    var name: String {
        switch self {
        case .luffy:
            return "Luffy"
        case .blackbeard:
            return "Blackbeard"
        case .shanks:
            return "Shanks"
        case .bigMom:
            return "Big Mom"
        case .kaido:
            return "Kaido"
        case .whitebeard:
            return "White Beard"
        case .roger:
            return "Gol D. Roger"
        }
    }

    var description: String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        let value = formatter.string(from: NSNumber(value: self.rawValue))!

        return "\(self.name): \(value) Berries"
    }
}
Enter fullscreen mode Exit fullscreen mode

Inilah saatnya kita menggunakan Generics. Generics di Swift berguna untuk mengurangi duplikasi di kode kita. Dengan Generics kita bisa membuat ListView agar dapat digunakan untuk berbagai macam Enum. Berikut hasil temuan saya.

struct EnumListView<Enum: RawRepresentable &
                        CaseIterable &
                        CustomStringConvertible> : View where
    Enum.RawValue: Hashable,
    Enum.AllCases: RandomAccessCollection {

    var title: String = ""
    @Binding var selectedItem: Enum?

    var body: some View {
        NavigationView {
            List {
                ForEach(Enum.allCases, id: \.rawValue) { (status) in
                    Button(action: {
                        selectedItem = Enum(rawValue: status.rawValue)!
                    }, label: {
                        HStack {
                            Text(status.description)
                            Spacer()
                            if selectedItem != nil && selectedItem! == status {
                                Image(systemName: "checkmark")
                            }
                        }
                    })
                }
            }
            .navigationBarTitle(LocalizedStringKey(title))
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Untuk menggunakan EnumListView tersebut, enum yang ingin ditampilkan harus memenuhi protokol RawRepresentable, CaseIterable, dan CustomStringConvertible. RawRepresentable diperlukan karena EnumListView memerlukan semacam id untuk mengiterasi casesnya dan di implementasi saya menggunakan rawValue sebagai ID. CaseIterable diperlukan untuk mengiterasi semua case di enum. CustomStringConvertible diperlukan agar kita dapat menampilkan text di setiap baris di list.

Untuk menggunakan EnumListView tersebut, silakan ikuti contoh di bawah ini.

static var previews: some View {
       Group {
           EnumListView<PiratesBounty>(title: "Bounties", selectedItem: .constant(.luffy))
           EnumListView<MugiwaraPirates>(title: "Mugiwara Pirates", selectedItem: .constant(.luffy))
       }
}
Enter fullscreen mode Exit fullscreen mode

Demo Xcode project di atas dapat diunduh dari sini.

Jika kamu tertarik untuk belajar bareng Swift sama developer2 iOS Indonesia, ayo ikutan sesi kedua Swift Study Group Indonesia!

Discussion

pic
Editor guide