Panduan Lengkap WebView Native di SwiftUI iOS 26: Cara Baru Menampilkan Konten Web

Pelajari cara menggunakan WebView native di SwiftUI iOS 26 dengan contoh kode lengkap. Mulai dari WebPage, tracking progress, eksekusi JavaScript, NavigationDeciding, hingga backward compatibility.

Pendahuluan: Akhirnya, WebView Native di SwiftUI!

Kalau kamu sudah cukup lama jadi iOS developer, pasti pernah merasakan betapa menderitanya menampilkan konten web di SwiftUI. Bertahun-tahun kita harus bikin wrapper UIViewRepresentable untuk membungkus WKWebView dari UIKit. Boilerplate-nya banyak, integrasi dengan state management SwiftUI itu ribet, dan kalau butuh fitur lanjutan kayak tracking progress atau eksekusi JavaScript — wah, tambah pusing lagi.

Jujur, saya sendiri sempat bikin helper class custom yang lumayan panjang cuma buat handle navigasi di WKWebView. Nggak fun sama sekali.

Nah, di iOS 26, Apple akhirnya menjawab doa kita. Mereka memperkenalkan WebView native yang dibangun dari nol khusus untuk SwiftUI. Bukan sekadar wrapper tipis di atas WKWebView — ini API yang beneran didesain untuk paradigma deklaratif SwiftUI dan memanfaatkan fitur-fitur modern Swift seperti Observable.

Dan yang paling bikin senang? Menampilkan halaman web sekarang bisa dilakukan dengan satu baris kode. Serius, satu baris.

Dalam panduan ini, kita akan bahas secara lengkap semua yang perlu kamu tahu tentang WebView dan WebPage di SwiftUI iOS 26:

  • WebView dasar — menampilkan web dengan satu baris kode
  • WebPage — kontrol penuh atas halaman web (judul, URL, progress, reload)
  • Tracking progress loading secara real-time
  • Eksekusi JavaScript dari Swift
  • Memuat HTML lokal tanpa server
  • NavigationDeciding — mengontrol navigasi dan memfilter URL
  • Konfigurasi WebPage — custom user agent dan lainnya
  • Integrasi dengan NavigationStack
  • Backward compatibility untuk iOS versi lama

Semuanya dilengkapi contoh kode yang bisa langsung kamu coba. Oke, langsung aja yuk!

Persyaratan dan Persiapan

Sebelum mulai ngoding, pastikan dulu environment kamu sudah siap:

  • Xcode 26 atau lebih baru
  • iOS 26 SDK (atau macOS Tahoe, visionOS 26)
  • Perangkat fisik atau Simulator yang menjalankan iOS 26+
  • Deployment target: iOS 26.0 minimum

Penting: API WebView native cuma tersedia di iOS 26 ke atas. Kalau aplikasimu perlu mendukung iOS versi lama, kamu tetap harus siapin fallback pakai UIViewRepresentable. Tenang, kita bahas caranya di bagian akhir artikel ini.

Oh iya, satu hal lagi — pastikan kamu selalu tambahkan import WebKit di file Swift yang menggunakan WebView. Tanpa import ini, Xcode nggak akan kenal tipe WebView dan WebPage. Kesalahan kecil tapi sering bikin bingung, terutama kalau baru pertama kali coba.

WebView Dasar: Satu Baris, Langsung Jalan

Oke, mari kita mulai dari yang paling sederhana. Untuk menampilkan halaman web di SwiftUI, sekarang kamu cukup tulis ini:

import SwiftUI
import WebKit

struct ContentView: View {
    var body: some View {
        WebView(url: URL(string: "https://www.apple.com"))
    }
}

Itu aja. Satu baris WebView(url:) dan kamu sudah punya browser mini di dalam aplikasi. Halaman web tampil lengkap dengan scrolling, link yang bisa diklik, video, dan JavaScript yang jalan — persis kayak di Safari.

Coba bandingkan dengan dulu, di mana kamu perlu bikin struct UIViewRepresentable, implement makeUIView, updateUIView, kadang pakai Coordinator juga. Sekarang? Satu baris. Beda banget rasanya.

Perhatikan bahwa WebView(url:) menerima optional URL. Jadi kalau URL-nya nil, WebView cuma tampilkan halaman kosong tanpa crash. Desain yang aman dan sangat idiomatic Swift.

Kapan Menggunakan WebView(url:)

Pendekatan ini cocok buat kasus-kasus sederhana:

  • Menampilkan halaman "Terms and Conditions" atau "Privacy Policy"
  • Menunjukkan halaman web statis tanpa perlu interaksi programatik
  • Prototyping cepat saat kamu cuma mau lihat apakah web content tampil dengan benar

Tapi untuk aplikasi production, kamu hampir pasti butuh kontrol lebih. Dan di situlah WebPage masuk.

WebPage: Pusat Kendali untuk Konten Web

WebPage adalah kelas Observable baru yang jadi "otak" di balik WebView. Ia mengelola state, navigasi, dan komunikasi dengan konten web. Kalau WebView itu layar TV, maka WebPage adalah remote control-nya.

Analoginya sederhana, tapi memang se-simpel itu konsepnya.

Membuat WebPage dan Menghubungkan ke WebView

import SwiftUI
import WebKit

struct BrowserView: View {
    @State private var page = WebPage()

    var body: some View {
        WebView(page)
            .onAppear {
                if let url = URL(string: "https://www.swift.org") {
                    page.load(URLRequest(url: url))
                }
            }
    }
}

Ada beberapa hal penting yang perlu kamu perhatikan di sini:

  • WebPage dideklarasikan sebagai @State karena ia objek yang di-track oleh SwiftUI
  • Kita pakai .onAppear untuk memuat URL pertama kali view muncul
  • page.load() menerima URLRequest, bukan URL langsung — ini sedikit berbeda dari WebView(url:)

Properti Penting WebPage

Karena WebPage conform ke Observable, semua propertinya otomatis memicu update UI di SwiftUI. Berikut properti-properti yang paling sering kamu pakai:

PropertiTipeDeskripsi
titleString?Judul halaman web saat ini
urlURL?URL halaman yang sedang ditampilkan
estimatedProgressDoubleProgress loading (0.0 sampai 1.0)
isLoadingBoolApakah halaman sedang dimuat
canGoBackBoolApakah bisa navigasi mundur

Semua properti ini bersifat reaktif. Artinya, saat pengguna berpindah halaman di WebView, properti-properti ini otomatis ter-update dan UI yang bergantung padanya langsung di-render ulang. Nggak perlu KVO, nggak perlu delegate, nggak perlu binding manual.

Clean banget, kan?

Metode Penting WebPage

MetodeDeskripsi
load(URLRequest)Memuat halaman dari URL
load(html:baseURL:)Memuat konten HTML string
reload()Memuat ulang halaman saat ini
stopLoading()Menghentikan proses loading
callJavaScript(_:)Mengeksekusi kode JavaScript (async throws)

Menampilkan Judul dan URL Halaman Secara Reaktif

Salah satu keuntungan terbesar dari WebPage yang Observable adalah kita bisa menampilkan informasi halaman secara real-time. Ini salah satu hal pertama yang saya coba waktu API-nya baru rilis, dan hasilnya langsung bikin kagum.

Berikut contoh lengkapnya:

import SwiftUI
import WebKit

struct SmartBrowserView: View {
    @State private var page = WebPage()

    var body: some View {
        VStack(spacing: 0) {
            // Header dengan judul halaman
            VStack(spacing: 4) {
                Text(page.title ?? "Memuat...")
                    .font(.headline)
                    .lineLimit(1)

                if let url = page.url {
                    Text(url.host ?? "")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
            .padding()

            // WebView
            WebView(page)
        }
        .onAppear {
            if let url = URL(string: "https://www.swift.org") {
                page.load(URLRequest(url: url))
            }
        }
    }
}

Perhatikan gimana page.title dan page.url langsung dipakai di Text view tanpa binding khusus. Saat pengguna klik link dan pindah halaman, judul dan URL di header otomatis berubah. Ini kekuatan Observable — simpel tapi powerful.

Tracking Progress Loading Secara Real-Time

Menampilkan progress bar saat halaman web sedang dimuat itu penting banget buat UX. Pengguna perlu tahu bahwa sesuatu sedang terjadi, bukan cuma layar putih kosong. Dengan WebPage, ini jadi gampang karena properti estimatedProgress sudah Observable:

import SwiftUI
import WebKit

struct ProgressBrowserView: View {
    @State private var page = WebPage()

    var body: some View {
        VStack(spacing: 0) {
            // Progress bar — hanya tampil saat loading
            if page.isLoading {
                ProgressView(value: page.estimatedProgress)
                    .progressViewStyle(.linear)
                    .tint(.blue)
            }

            // Toolbar
            HStack {
                Text(page.title ?? "Memuat...")
                    .font(.headline)
                    .lineLimit(1)

                Spacer()

                // Tombol reload/stop
                Button {
                    if page.isLoading {
                        page.stopLoading()
                    } else {
                        page.reload()
                    }
                } label: {
                    Image(systemName: page.isLoading ? "xmark" : "arrow.clockwise")
                }
            }
            .padding(.horizontal)
            .padding(.vertical, 8)

            WebView(page)
        }
        .onAppear {
            if let url = URL(string: "https://www.apple.com") {
                page.load(URLRequest(url: url))
            }
        }
    }
}

Kode di atas bikin pengalaman yang mirip browser sungguhan — ada progress bar linear di atas yang bergerak saat halaman dimuat, dan tombol yang berubah fungsi antara "stop" dan "reload" tergantung status loading.

Semua ini terjadi otomatis berkat reaktivitas WebPage. Nggak ada satu pun baris kode KVO atau delegate. Kalau kamu pernah pakai KVO di WKWebView dulu (dan saya yakin banyak yang pernah), pasti ngerasa ini upgrade yang signifikan.

Eksekusi JavaScript dari Swift

Nah, ini bagian yang menurut saya paling seru. Kemampuan menjalankan JavaScript di dalam WebView itu super berguna — kamu bisa ubah tampilan halaman, ekstrak data, atau bahkan kontrol navigasi dari kode Swift.

Metode callJavaScript(_:) bersifat async throws, jadi kamu perlu memanggilnya di dalam konteks async:

import SwiftUI
import WebKit

struct JavaScriptDemoView: View {
    @State private var page = WebPage()
    @State private var pageInfo = ""

    var body: some View {
        VStack(spacing: 0) {
            WebView(page)

            // Panel kontrol JavaScript
            VStack(spacing: 12) {
                Text(pageInfo)
                    .font(.caption)
                    .foregroundStyle(.secondary)

                HStack(spacing: 16) {
                    Button("Ambil Judul") {
                        Task {
                            do {
                                let result = try await page.callJavaScript(
                                    "document.title"
                                )
                                pageInfo = "Judul: \(result ?? "N/A")"
                            } catch {
                                pageInfo = "Error: \(error.localizedDescription)"
                            }
                        }
                    }

                    Button("Dark Mode") {
                        Task {
                            try? await page.callJavaScript(
                                "document.body.style.backgroundColor = '#1a1a2e'; document.body.style.color = '#eaeaea';"
                            )
                        }
                    }

                    Button("Scroll Atas") {
                        Task {
                            try? await page.callJavaScript(
                                "window.scrollTo({top: 0, behavior: 'smooth'});"
                            )
                        }
                    }
                }
                .buttonStyle(.bordered)
            }
            .padding()
        }
        .onAppear {
            if let url = URL(string: "https://www.swift.org") {
                page.load(URLRequest(url: url))
            }
        }
    }
}

Beberapa hal penting soal callJavaScript:

  • Ia mengembalikan Any?, jadi kamu perlu cast hasilnya sesuai kebutuhan
  • Karena async throws, selalu bungkus dalam Task { } kalau dipanggil dari konteks synchronous
  • Jangan lupa handle error — JavaScript yang salah sintaks bakal throw error
  • Untuk keamanan, gunakan parameter arguments saat meneruskan data dari Swift ke JavaScript, bukan string interpolation langsung (ini penting banget buat mencegah injection)

Memuat HTML Lokal

Kadang kamu nggak perlu memuat URL dari internet — cukup render HTML yang sudah disiapkan secara lokal. Ini berguna buat banyak hal, misalnya menampilkan konten rich text, preview email, atau halaman bantuan offline.

WebPage mendukung ini lewat metode load(html:baseURL:):

import SwiftUI
import WebKit

struct LocalHTMLView: View {
    @State private var page = WebPage()

    private let htmlContent = """
    <!DOCTYPE html>
    <html lang="id">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
            body {
                font-family: -apple-system, BlinkMacSystemFont, sans-serif;
                padding: 20px;
                color: #333;
                line-height: 1.6;
            }
            h1 { color: #007AFF; }
            .highlight {
                background: #F0F8FF;
                padding: 12px;
                border-radius: 8px;
                border-left: 4px solid #007AFF;
            }
        </style>
    </head>
    <body>
        <h1>Selamat Datang</h1>
        <p>Ini adalah konten HTML yang dimuat secara lokal.</p>
        <div class="highlight">
            <strong>Tips:</strong> Kamu bisa menggunakan CSS dan JavaScript
            lengkap di dalam HTML lokal ini.
        </div>
    </body>
    </html>
    """

    var body: some View {
        WebView(page)
            .onAppear {
                page.load(
                    html: htmlContent,
                    baseURL: URL(string: "about:blank")!
                )
            }
    }
}

Parameter baseURL menentukan URL dasar untuk resolusi path relatif di dalam HTML. Kalau HTML-mu nggak punya resource eksternal, pakai aja about:blank. Tapi kalau kamu perlu memuat gambar atau CSS dari bundle aplikasi, atur baseURL ke direktori bundle yang sesuai.

Mengontrol Navigasi dengan NavigationDeciding

Ini salah satu fitur yang (menurut saya) paling underrated dari WebPage baru. Protokol NavigationDeciding memungkinkan kamu memfilter navigasi — misalnya, cuma izinkan URL tertentu atau blokir link eksternal.

Kalau dulu pakai WKWebView, kamu harus implement WKNavigationDelegate yang lumayan verbose. Sekarang jauh lebih clean:

import SwiftUI
import WebKit

// Buat struct yang conform ke NavigationDeciding
struct DomainFilter: WebPage.NavigationDeciding {
    let allowedDomains: [String]

    func decidePolicy(
        for response: WebPage.NavigationResponse
    ) async -> WKNavigationResponsePolicy {
        guard let url = response.response.url,
              let host = url.host else {
            return .cancel
        }

        // Hanya izinkan domain yang ada di daftar
        if allowedDomains.contains(where: { host.contains($0) }) {
            return .allow
        } else {
            return .cancel
        }
    }
}

struct FilteredBrowserView: View {
    @State private var page: WebPage

    init() {
        let filter = DomainFilter(
            allowedDomains: ["apple.com", "swift.org", "developer.apple.com"]
        )
        _page = State(initialValue: WebPage(navigationDecider: filter))
    }

    var body: some View {
        WebView(page)
            .onAppear {
                if let url = URL(string: "https://developer.apple.com") {
                    page.load(URLRequest(url: url))
                }
            }
    }
}

Dengan NavigationDeciding, kamu punya kontrol penuh atas apa yang boleh dan nggak boleh diakses pengguna di dalam WebView. Ini berguna banget untuk:

  • Aplikasi anak-anak — membatasi akses cuma ke domain yang aman
  • In-app browser — mencegah pengguna keluar dari domain tertentu
  • Keamanan — memblokir redirect ke situs phishing atau berbahaya

Oh, dan metode decidePolicy itu bersifat async lho. Artinya kamu bahkan bisa melakukan validasi yang butuh network call sebelum memutuskan apakah navigasi diizinkan atau tidak. Fleksibel banget.

Konfigurasi WebPage

Kamu bisa kustomisasi perilaku WebPage menggunakan WebPage.Configuration. Satu hal yang perlu diingat: konfigurasi ini harus diset saat inisialisasi — nggak bisa diubah setelah WebPage dibuat.

import SwiftUI
import WebKit

struct ConfiguredBrowserView: View {
    @State private var page: WebPage = {
        var config = WebPage.Configuration()
        config.applicationNameForUserAgent = "MyApp/1.0"
        return WebPage(configuration: config)
    }()

    var body: some View {
        WebView(page)
            .onAppear {
                if let url = URL(string: "https://www.example.com") {
                    page.load(URLRequest(url: url))
                }
            }
    }
}

Opsi konfigurasi yang tersedia antara lain:

  • applicationNameForUserAgent — menambahkan nama aplikasi ke user agent string. Berguna kalau server backend-mu perlu mengenali request dari aplikasi iOS
  • Pengaturan pemutaran video — mengontrol perilaku playback video
  • Data detection otomatis — mendeteksi nomor telepon, alamat, dan data lainnya secara otomatis

Catatan penting: Sekali lagi, konfigurasi bersifat immutable setelah WebPage dibuat. Pastikan semua pengaturan sudah bener sebelum inisialisasi. Kalau salah set, kamu harus bikin instance WebPage baru.

Navigasi Back/Forward Manual

Tidak seperti browser lengkap, WebView native nggak menyediakan tombol back/forward bawaan. Kamu perlu buatnya sendiri. Ini sebetulnya masuk akal — Apple memberikan kebebasan penuh soal UI, nggak memaksakan desain tertentu.

Berikut cara implementasinya:

import SwiftUI
import WebKit

struct FullBrowserView: View {
    @State private var page = WebPage()
    @State private var urlText = "https://www.swift.org"

    var body: some View {
        VStack(spacing: 0) {
            // Address bar
            HStack {
                TextField("Masukkan URL", text: $urlText)
                    .textFieldStyle(.roundedBorder)
                    .autocapitalization(.none)
                    .keyboardType(.URL)
                    .onSubmit {
                        loadURL()
                    }

                Button("Buka") {
                    loadURL()
                }
                .buttonStyle(.borderedProminent)
            }
            .padding(.horizontal)
            .padding(.vertical, 8)

            // Progress bar
            if page.isLoading {
                ProgressView(value: page.estimatedProgress)
                    .progressViewStyle(.linear)
                    .tint(.blue)
            }

            // WebView
            WebView(page)

            // Navigation toolbar
            HStack(spacing: 24) {
                Button {
                    Task {
                        try? await page.callJavaScript("history.back()")
                    }
                } label: {
                    Image(systemName: "chevron.left")
                        .font(.title3)
                }

                Button {
                    Task {
                        try? await page.callJavaScript("history.forward()")
                    }
                } label: {
                    Image(systemName: "chevron.right")
                        .font(.title3)
                }

                Spacer()

                Button {
                    if page.isLoading {
                        page.stopLoading()
                    } else {
                        page.reload()
                    }
                } label: {
                    Image(systemName: page.isLoading
                        ? "xmark" : "arrow.clockwise")
                        .font(.title3)
                }
            }
            .padding()
            .background(.bar)
        }
        .onAppear {
            loadURL()
        }
    }

    private func loadURL() {
        var finalURL = urlText
        if !finalURL.hasPrefix("http://")
            && !finalURL.hasPrefix("https://") {
            finalURL = "https://" + finalURL
        }
        if let url = URL(string: finalURL) {
            page.load(URLRequest(url: url))
        }
    }
}

Contoh di atas membangun mini browser lengkap dengan address bar, progress bar, dan tombol navigasi. Navigasi back/forward di sini pakai callJavaScript("history.back()") dan callJavaScript("history.forward()") karena API native untuk goBack/goForward masih dalam tahap pengembangan.

Agak workaround memang, tapi works well untuk sekarang.

Integrasi dengan NavigationStack

Menggabungkan WebView dengan NavigationStack SwiftUI itu sangat natural berkat sifat Observable dari WebPage. Ini contoh yang menampilkan judul halaman web sebagai navigation title:

import SwiftUI
import WebKit

struct NavigationBrowserView: View {
    @State private var page = WebPage()

    var body: some View {
        NavigationStack {
            WebView(page)
                .navigationTitle(page.title ?? "Browser")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .topBarTrailing) {
                        HStack {
                            if page.isLoading {
                                ProgressView()
                            }

                            Button {
                                page.reload()
                            } label: {
                                Image(systemName: "arrow.clockwise")
                            }
                        }
                    }
                }
        }
        .onAppear {
            if let url = URL(string: "https://developer.apple.com/swift/") {
                page.load(URLRequest(url: url))
            }
        }
    }
}

Navigation title otomatis berubah mengikuti judul halaman web yang sedang ditampilkan. Ini contoh sempurna bagaimana WebPage yang Observable terintegrasi mulus dengan komponen SwiftUI lainnya. Nggak perlu notification, nggak perlu delegate — just works.

Mendeteksi Perubahan URL

Terkadang kamu perlu bereaksi ketika pengguna berpindah ke URL tertentu — misalnya buat membuka link eksternal di Safari atau menangani deep link. Caranya gampang, tinggal pakai .onChange(of:) pada properti page.url:

import SwiftUI
import WebKit

struct URLMonitorView: View {
    @State private var page = WebPage()
    @Environment(\.openURL) private var openURL

    var body: some View {
        WebView(page)
            .onChange(of: page.url) { _, newURL in
                guard let url = newURL,
                      let host = url.host else { return }

                // Buka link eksternal di Safari
                if !host.contains("swift.org") {
                    page.stopLoading()
                    openURL(url)
                }
            }
            .onAppear {
                if let url = URL(string: "https://www.swift.org") {
                    page.load(URLRequest(url: url))
                }
            }
    }
}

Teknik ini berguna banget buat bikin "walled garden" browser — pengguna bisa bernavigasi bebas di dalam domain tertentu, tapi link ke domain lain otomatis dibuka di Safari. Saya sering pakai pola ini di aplikasi yang menampilkan konten dari CMS.

Backward Compatibility: Mendukung iOS Versi Lama

Realitanya, nggak semua pengguna langsung update ke iOS terbaru. Kalau aplikasimu perlu mendukung versi iOS yang lebih lama (dan kemungkinan besar memang perlu), kamu harus siapin dua implementasi:

import SwiftUI
import WebKit

// WebView legacy untuk iOS < 26
struct LegacyWebView: UIViewRepresentable {
    let url: URL

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.load(URLRequest(url: url))
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {}
}

// View utama dengan backward compatibility
struct CompatibleBrowserView: View {
    let url: URL

    var body: some View {
        if #available(iOS 26, *) {
            WebView(url: url)
        } else {
            LegacyWebView(url: url)
        }
    }
}

Pendekatan ini pakai #available check sehingga compiler bisa mastiin API yang benar digunakan di setiap versi iOS. Di iOS 26+, pengguna dapet WebView native yang lebih performant dan terintegrasi. Di versi lama, mereka tetap bisa akses konten web lewat wrapper UIViewRepresentable.

Memang agak repot maintain dua implementasi, tapi ini pendekatan standar di ekosistem Apple. Dan seiring waktu, kamu bisa naikkan minimum deployment target dan buang kode legacy-nya.

Tips dan Best Practices

Setelah cukup banyak bereksperimen dengan WebView native di iOS 26, berikut beberapa tips yang semoga bisa membantu kamu menghindari jebakan yang sama:

1. Selalu Gunakan WebPage untuk Aplikasi Production

WebView(url:) memang simpel, tapi untuk aplikasi sungguhan kamu hampir selalu butuh WebPage. Kemampuan tracking progress, menampilkan judul, dan mengontrol navigasi itu esensial buat UX yang baik. Trust me on this one.

2. Hapus Background Sebelum Menambahkan WebView

Kalau view parent-mu punya background warna solid, pastikan WebView nggak tertutup. Gunakan .ignoresSafeArea() kalau kamu mau WebView memenuhi seluruh layar, tapi perhatikan bahwa ada laporan bug layout dengan modifier ini di beberapa versi beta iOS 26.

3. Handle Error JavaScript dengan Baik

Karena callJavaScript bersifat throws, selalu wrap dalam do-catch. JavaScript yang gagal bisa terjadi karena berbagai alasan — halaman belum selesai dimuat, DOM element belum ada, atau sintaks yang salah. Jangan pakai try? di production code kecuali memang nggak butuh feedback error-nya.

4. Perhatikan Performa di Perangkat Lama

Meskipun WebView native lebih efisien dari wrapper UIViewRepresentable, situs web yang berat tetap bisa bikin masalah memori dan performa di perangkat lama. Pertimbangkan untuk memblokir loading resource yang nggak perlu lewat JavaScript atau NavigationDeciding.

5. Konfigurasi Harus Diset di Awal

WebPage.Configuration bersifat immutable setelah WebPage dibuat. Jangan coba mengubah konfigurasi setelah inisialisasi — nggak bakal berpengaruh. Plan ahead.

WebView vs WKWebView: Kapan Masih Perlu UIKit?

Meskipun WebView native sudah sangat capable, ada beberapa skenario di mana kamu mungkin masih perlu pakai WKWebView lewat UIViewRepresentable:

SkenarioPendekatan
Tampilan web sederhanaWebView native (iOS 26+)
Tracking progress dan judulWebPage + WebView native
Eksekusi JavaScriptWebPage + callJavaScript
User scripts (content scripts)WKWebView (UIKit)
Deep konfigurasi WKWebViewConfigurationWKWebView (UIKit)
Support iOS < 26UIViewRepresentable fallback

Intinya, untuk sebagian besar kasus penggunaan, WebView native udah lebih dari cukup. Kamu cuma perlu kembali ke UIKit kalau butuh konfigurasi yang sangat spesifik yang belum disupport oleh API baru.

FAQ

Apakah SwiftUI sudah punya WebView bawaan?

Ya, mulai dari iOS 26 (diperkenalkan di WWDC 2025), SwiftUI punya komponen WebView native yang tersedia lewat import WebKit. Sebelum iOS 26, developer harus bikin wrapper UIViewRepresentable untuk WKWebView secara manual.

Apa perbedaan antara WebView dan WebPage di SwiftUI?

WebView adalah komponen tampilan (View) yang menampilkan konten web di layar. WebPage adalah model Observable yang mengelola state dan kontrol — seperti judul halaman, URL, progress loading, dan eksekusi JavaScript. Untuk kasus sederhana, pakai WebView(url:). Untuk kontrol penuh, buat instance WebPage dan hubungkan ke WebView(page).

Bagaimana cara menjalankan JavaScript di WebView SwiftUI?

Gunakan metode callJavaScript(_:) pada objek WebPage. Metode ini bersifat async throws, jadi kamu perlu panggilnya di dalam Task { } atau konteks async lainnya. Contoh: try await page.callJavaScript("document.title").

Apakah WebView native iOS 26 bisa digunakan di iOS versi lama?

Tidak bisa. WebView native cuma tersedia di iOS 26 ke atas. Untuk mendukung versi iOS lama, gunakan #available(iOS 26, *) check dan sediakan fallback menggunakan WKWebView yang dibungkus UIViewRepresentable.

Apakah WebView native lebih baik daripada WKWebView via UIViewRepresentable?

Untuk sebagian besar kasus, iya. WebView native terintegrasi jauh lebih baik dengan SwiftUI — propertinya Observable sehingga UI otomatis reactive, nggak perlu boilerplate UIViewRepresentable, dan kodenya jauh lebih bersih. Tapi untuk konfigurasi sangat spesifik seperti user scripts atau deep WKWebViewConfiguration, kamu mungkin masih perlu pendekatan UIKit.

Tentang Penulis Editorial Team

Our team of expert writers and editors.