๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŽ iOS/๐Ÿ”ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

[iOS/Swift] WKWebKit ์•ฝ๊ด€ ์ƒ์„ธ ํŽ˜์ด์ง€ ์ „ํ™˜ํ•˜์—ฌ ๋„์šฐ๊ธฐ

by MINT09 2024. 7. 7.

์šฐ๋ฆฌ๋Š” ๋…ธ์…˜ ์›นํŽ˜์ด์ง€๋กœ ๋‘ ๊ฐœ์˜ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. (<๊ด‘๊ณ  ๋ฐ ๋งˆ์ผ€ํŒ… ์ˆ˜์‹ ์— ๋™์˜> ํŽ˜์ด์ง€๋Š” ํ˜„์žฌ ์ œ์™ธ๋˜์—ˆ๋‹ค.)

๊ฐ™์€ ํ˜•์‹์˜ ๋ทฐ์—, title๊ณผ WebView์˜ ๋งํฌ๋งŒ ๋ฐ”๋€๋‹ค. ๋‹ค๋งŒ ์—ฌ๊ธฐ์„œ <๋™์˜> ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ, ๋ฐ”๊นฅ์˜ ์•ฝ๊ด€ ์„ ํƒ ํŽ˜์ด์ง€์—์„œ๋„ ํ•ด๋‹น <๋™์˜> ํ‘œ์‹œ๊ฐ€ ๋ฐ˜์˜๋˜์–ด์•ผ ํ–ˆ๋‹ค. ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€, ์œ„์˜ 3๊ฐœ ํŽ˜์ด์ง€๊ฐ€ ์ „๋ถ€ ๊ฐ™์€ ๋ทฐ๋ชจ๋ธ์„ ๊ณต์œ ํ•˜์—ฌ ์ƒํƒœ ๊ฐ’๋“ค์„ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

WKWebView ์‚ฌ์šฉ

WebView์˜ ๊ตฌํ˜„ ๋ฐฉ์‹์—์„œ๋Š” 3๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. 

1. WKWebKit

2. open safari

3. SFSafariViewController

๋‚˜๋Š” ์ด ์ค‘์—์„œ๋„ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ์„ ์ปค์Šคํ…€ํ•˜๊ณ , ๋”ฐ๋กœ ์›น์—์„œ ์ž‘์—…ํ•  ๊ฒƒ ์—†์ด ์•ฑ ๋‚ด์—์„œ ๋ณด์—ฌ์ฃผ๊ธฐ๋งŒ ํ•  ๊ฒƒ์ด๊ธฐ์— WKWebKit์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. ๋…ธ์…˜์œผ๋กœ ์•ฝ๊ด€์„ ์ •์˜ํ–ˆ๋Š”๋ฐ, ์•ฝ๊ด€์˜ ๋‚ด์šฉ์ด ์ˆ˜์ •๋˜์—ˆ์„ ๋•Œ ๊ตณ์ด ์•ฑ์„ ์—…๋ฐ์ดํŠธ ํ•˜์ง€ ์•Š๋”๋ผ๋„ ์ˆ˜์ •๋œ ๋‚ด์šฉ์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋„๋ก html๊ณผ ์›น๋งํฌ ์ค‘์—์„œ๋Š” ์›น๋งํฌ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

import UIKit
import WebKit
import Combine

class TermsDetailViewController: UIViewController {
	//๋ณด์—ฌ์ค„ ์›น๋ทฐ
    private let webView: WKWebView = {
        let configuration = WKWebViewConfiguration()
        let view = WKWebView(frame: .zero, configuration: configuration)
        view.translatesAutoresizingMaskIntoConstraints = false
        
        return view
    }()
    
    let agreeButton = NextButton(title: "๋™์˜", backgroundColor: .roomeMain, tintColor: .white)
    let viewModel: TermsAgreeViewModel
    var cancellable = Set<AnyCancellable>()
    
    init(viewModel: TermsAgreeViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        configureAgreeButton()
        configureWebView()
        bind()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        //๋ทฐ๊ฐ€ ๋ณด์ผ ๋•Œ๋งˆ๋‹ค webViewLoading update
        loadWebView()
    }
    
    func bind() {
        agreeButton.publisher(for: .touchUpInside)
            .eraseToAnyPublisher()
            .sink { [weak self] _ in
                self?.viewModel.input.handleDetail.send()
                self?.dismiss(animated: true)
            }
            .store(in: &cancellable)
    }
    
    private func configureAgreeButton() {
        view.addSubview(agreeButton)
        
        NSLayoutConstraint.activate([
            agreeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            agreeButton.heightAnchor.constraint(equalToConstant: 50),
            agreeButton.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
            agreeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])
    }
    
    //์›น๋ทฐ ์œ„์น˜ ์žก๊ธฐ
    private func configureWebView() {
        view.addSubview(webView)
        
        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
            webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: agreeButton.topAnchor, constant: -8)
        ])
    }
    
    private func loadWebView() {
        //์›น ๋งํฌ ๋„์šฐ๊ธฐ
        let link = viewModel.detailState.link
        guard let url = URL(string: link) else {
            return
        }
        let request = URLRequest(url: url)
        
        webView.load(request)
    }
}

titleLabel, closeButton๊ณผ ๊ฐ™์€ ๋ถ€๋ถ„์€ ์ฝ”๋“œ์—์„œ ์ œ์™ธํ–ˆ๋‹ค. (๋กœ์ง๊ณผ๋Š” ์ƒ๊ด€ ์—†๋Š” ์ฝ”๋“œ๋‹ˆ๊นŒ)

ํด๋ฆญ ํ•  ๋•Œ ๋ทฐ๋ชจ๋ธ ์ƒํƒœ ๋ณ€ํ™”

TermsDetailViewController๋กœ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— TermsAgreeViewController์—์„œ viewModel์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•œ ํ›„ TermsDetailViewController๋ฅผ ๋ถ€๋ฅด๋Š” ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ํ•ด๋‹น ViewController๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

serviceAgreeButton.tappedDetailButtonPublisher()
    .sink { [weak self] _ in
        self?.viewModel.detailState = .service
        let detailView = DIContainer.shared.resolve(TermsDetailViewController.self)
        detailView.modalPresentationStyle = .fullScreen
        self?.view.window?.rootViewController?.present(detailView, animated: true)
    }.store(in: &cancellable)
        
personalInformationAgreeButton.tappedDetailButtonPublisher()
    .sink { [weak self] _ in
        self?.viewModel.detailState = .personal
        let detailView = DIContainer.shared.resolve(TermsDetailViewController.self)
        detailView.modalPresentationStyle = .fullScreen
        self?.view.window?.rootViewController?.present(detailView, animated: true)
    }.store(in: &cancellable)

 

https://github.com/Prography-RoomE/roome_ios/pull/39

 

[FEAT] ์•ฝ๊ด€ ๋™์˜ ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌํ˜„ by mint3382 · Pull Request #39 · Prography-RoomE/roome_ios

๐Ÿ“š ์ž‘์—… ์„ค๋ช… ์ด์šฉ์•ฝ๊ด€ ์ƒ์„ธ ๋…ธ์…˜ ํŽ˜์ด์ง€ ์›น๋ทฐ๋กœ ๋„์›Œ์„œ ๊ตฌํ˜„ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๋™์˜ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์•ฝ๊ด€ ๋™์˜ํ•œ ๊ฒƒ์œผ๋กœ ์ด์šฉ์•ฝ๊ด€ ํŽ˜์ด์ง€์˜ ๋ฒ„ํŠผ ์ƒํƒœ๋„ ๊ฐ™์ด ๋ณ€๋™ ๐Ÿ”ฅ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… 1๏ธโƒฃ. WebView

github.com

https://apps.apple.com/kr/app/roome/id6503616766

 

‎roome

‎๋ฐฉ ํƒˆ์ถœ ํ”„๋กœํ•„ ์ œ์ž‘ ์„œ๋น„์Šค ๋ฃจ๋ฏธ๋ฅผ ํ†ตํ•ด ๋‚˜์˜ ์„ฑํ–ฅ์„ ๋น ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด ๋ณด์„ธ์š”. ๋ฃจ๋ฏธ๋Š” ๋ฐฉ ํƒˆ์ถœ์„ ์ข‹์•„ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ๋‚˜์˜ ์ทจํ–ฅ์„ ๋‹ด์€ ํ”„๋กœํ•„์„ ์ด๋ฏธ์ง€ 1์žฅ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค

apps.apple.com