SwiftUI 100% 웹뷰 구현하기

이정훈·2025년 6월 12일
0

SwiftUI

목록 보기
7/8
post-thumbnail

드디어 나옴 🎉

SwiftUI가 2019년 등장 이후, 만 6년만에 SwiftUI 네이티브 웹뷰 API를 지원하기 시작했다. 감격스러워서 눈물이 나올 지경이다.

물론, 아쉽게도 모든 버전에서 사용 가능한 기능은 아니다. iOS 26 이상부터 사용 가능하다.

이전까지 어떻게 사용해 왔나❓

이전까지는 SwiftUI 용 웹뷰를 지원하지 않았기 때문에, 어쩔 수 없이 UIKit에서 제공하는 WKWebViewUIViewRepresentable를 활용하여 간접적으로 SwiftUI에서 웹뷰를 구현할 수 있었다.

아래는 UIKit에서 제공하는 WKWebView를 활용하여 웹뷰를 구현하는 간단한 예시이다.

WKWebView + SwiftUI 예시

import SwiftUI
import WebKit

struct BaseWebView: UIViewRepresentable {
    private let webView = WKWebView()
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }
    
    func makeUIView(context: Context) -> some UIView {
        webView.load(URLRequest(url: URL(string: "https://www.google.com")!))
        return webView
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        private let parent: BaseWebView
        
        init(parent: BaseWebView) {
            self.parent = parent
        }
    }
}

이 좋은걸 애플만 알고 있었다니

이번에는 iOS 26부터 추가된 SwiftUI 전용 WebView를 활용하여 웹뷰를 구현하면 아래와 같이 코드를 작성할 수 있다.

import SwiftUI
import WebKit

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

UIKitWKWebView을 사용할 때 보다 단 5줄이면 웹뷰 구현을 끝낼 수 있어 생산성을 매우 높일 수 있을 것이라 생각한다.

여기서 끝이 아니다

이번에는 WKNavigationDelegate에서 제공 했었던 다양한 웹뷰 기능을 활용해 보자.

초기 세팅은 아래와 같은데, 먼저 WebPage 인스턴스를 관찰 가능한 형태로 선언한다.

import Combine
import WebKit

@MainActor
@Observable
final class ContentViewModel {
    let page: WebPage = WebPage()
    
    func load() {
        page.load(URLRequest(url: URL(string: "https://www.google.com")!))
    }
}

그리고 View 쪽에서 WebView의 생성자 전달인자로 WebPage 인스턴스를 전달한다.

import SwiftUI
import WebKit

struct ContentView: View {
    @Environment(ContentViewModel.self) private var viewModel
    
    var body: some View {
        WebView(viewModel.page)
            .onAppear {
                viewModel.load()
            }
    }
}

이제 WebPage에서 제공하는 다양한 데이터를 활용하여 기존 WKNavigationDelegate에서 제공하는 다양한 웹뷰 기능을 구현할 수 있다. 아래에서 간단하게 몇가지 기능을 소개한다.

로딩 바 구현

WebPageestimatedProgress는 현재 로딩 중인 페이지의 progress 상태를 Double 값으로 제공한다. 그리고 isLoading은 웹 페이지의 로딩 여부를 Bool 값으로 제공한다.

두 프로퍼티를 활용하여 SwiftUIProgressView와 함께 사용하면 웹뷰의 로딩 바를 구현할 수 있다.

VStack(spacing: 0) {
    ProgressView(value: viewModel.page.estimatedProgress)
        .opacity(viewModel.page.isLoading ? 1 : 0)
            
    WebView(viewModel.page)
        .onAppear {
            viewModel.load()
         }
}

javascript injection

WebPage에서 제공하는 javascript 인젝션을 활용하면 웹 페이지의 데이터를 가져오거나 데이터를 런타임에 동적으로 변경할 수 있다.

예를 들어, 아래와 같이 callJavaScript(_:) 메서드를 활용하면 웹 페이지 내의 롱터치 기능을 비활성화하는 JavaScript 코드를 실행할 수 있다.

import Combine
import WebKit

@MainActor
@Observable
final class ContentViewModel {
    let page: WebPage = WebPage()
    
    func injectScript() {
        Task {
            do {
                try await page.callJavaScript(
                    """
                    document.documentElement.style.webkitUserSelect='none'
                    """
                )
            } catch {
                print(error)
            }
        }
    }
}
profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글