WebView
WebView๋ ํ๋ ์์ํฌ์ ๋ด์ฅ๋ ์น ๋ธ๋ผ์ฐ์ ์ปดํฌ๋ํธ๋ก View์ ํํ๋ก ์ฑ์ ์๋ฒ ๋ฉํ๋ ๊ฒ์ ๋งํฉ๋๋ค. ์๋ฅผ ๋ค์ด, API๋ฅผ ํตํด์ ๋ฐ์ URL์ ํธ์ถ์ ํด๋ณด๋ฉด ์๋ต ๊ฐ์ผ๋ก JSON, XML ๊ฐ์ ๋ฐ์ดํฐ ํฌ๋งท์ด ์๋๋ผ HTML๋ก ๋ค์ด์ต๋๋ค. ๋ง์ฝ ์ด๋ ๊ฒ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๊ฒ ๋๋ฉด ํ์ฑ์ ํด์ค ์ ์์ต๋๋ค.
๊ทธ๋์ ์ด๋ฌํ HTML ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํด์ ์นํ์ด์ง๋ก ๋ณด์ฌ์ฃผ๋ ํ๋ ์์ํฌ๊ฐ WebView์
๋๋ค.
์ฆ, WebView๋ ์ฑ ๋ด์ ์น ํ์ด์ง๋ฅผ ๋ฃ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
WebView๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ต๋๋ค.
UIWebView
UIWebView๋ ios 2.0์ ์ถ์๋์์ผ๋ฉฐ ์น ์ฝํ ์ธ ๋ฅผ ์ฑ ๋ด๋ถ์์ ๋ ๋๋ง์ ํ ์ ์์ต๋๋ค.
์ฑ๋ฅ๊ณผ ๋ณด์ ๋ฌธ์ ๋ก ์ธํด iOS 12์์ Deprecated๋์ด iOS 13๋ถํฐ WKWebView๋ก ๋์ฒด๋์์ต๋๋ค.
WKWebView
ios 8.0์ด์์ WebKit ๊ธฐ๋ฐ ์น๋ทฐ์ ๋๋ค. ๊ธฐ์กด์ UIWebView๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ด ์ต์ ํ๋์์ต๋๋ค.
๋ํ, ์น ์ฝํ ์ธ ๋ฅผ ๋ณ๋์ ํ๋ก์ธ์ค์์ ์คํํ๊ธฐ ๋๋ฌธ์ ์์ ์ ์ ๋๋ค. ์ด ๋ง์ ์ฆ์จ, ์น ํ์ด์ง๋์ ์ฑ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ณ๋๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ์น ํ์ด์ง์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๋ฌด๋ฆฌ ํฌ๋๋ผ๋ ์ฑ์ด ์ฃฝ์ง ์๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
์ด์ธ์๋ JavaScript ์คํ ์๋๊ฐ ๋น ๋ฅด๋ฉฐ ์ฟ ํค๋ ์บ์, ์ธ์ ๋ฑ ์น ๋ธ๋ผ์ฐ์ง ๊ธฐ๋ฅ์ ๋ค๋ฃฐ ์ ์์ด ์ฑ ๋ด๋ถ์์ ์น์ ์์ ํ ์ปจํธ๋กคํด์ผ ํ ๋ ์ ํฉํฉ๋๋ค.
SFSafariView
SFSafariView ๋ ios 9.0๋ถํฐ ์ฌ์ฉ๋์์ผ๋ฉฐ, Safari ๊ธฐ๋ฐ์ ์น๋ทฐ์ ๋๋ค. ์ฆ, ์ฑ ๋ด์์ ์ฌํ๋ฆฌ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋์ฐ๋ ๋ฐฉ์์ ๋๋ค. ํ์ง๋ง ์ฌํ๋ฆฌ ๋ธ๋ผ์ฐ์ ๋ฅผ ๋์ฐ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ ์น ์ฝํ ์ธ ๋ฅผ ์ง์ ์ปจํธ๋กคํ ์๋ ์์ต๋๋ค.
SFSafariView๋ ๋ก๊ทธ์ธ ์ ์ง, Safari ์ฟ ํค ๋ฐ ๋ฐ์ดํฐ ๋ฑ ๊ณต์ ๋ฑ ๋ค์ํ ์ด์ ์ด ์์ต๋๋ค.
WKWebView๊ฐ ๋จ์ํ ์น ํ์ด์ง ํ๋๋ง์ ๋ณด์ฌ์ค๋ค๋ฉด, SFSafariView๋ ์ฌํ๋ฆฌ์ ๊ธฐ๋ฅ์ ์ด์ฉํ ์๊ฐ ์์ด ๋ค์ํ ๋์์ด ๊ฐ๋ฅํด์ง๋๋ค.
ASWebAuthenticationSession
iOS 12 ์ด์์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ OAuth ์ธ์ฆ(๋ก๊ทธ์ธ)์ ํนํ๋ WebView ์ ๋๋ค.
์ฌ์ฉ์์ ๊ธฐ์กด ์ฌํ๋ฆฌ ๋ก๊ทธ์ธ ์ธ์ ์ ํ์ฉํ ์ ์์ด ์ฑ๊ณผ ์ฌํ๋ฆฌ ๊ฐ ์ํํ ๋ก๊ทธ์ธ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค. ๋ณด์ ์ธก๋ฉด์์ SFSafariViewController๋ณด๋ค ๋ ๊ฐ์ ๋์์ผ๋ฉฐ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ๋๋ ์ฑ๊ณผ ์ฟ ๊ธฐ ๋ฑ ๋ฐ์ดํฐ ๊ณต์ ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
์ด ์ค์์๋ ์์ฃผ ์ฌ์ฉ๋๋ WKWebView๋ฅผ ์ฌ์ฉํด ๊ฐ๋จํ ์น๋ทฐ๋ฅผ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
WKWebView๋ WebKit ๊ธฐ๋ฐ์ ์น๋ทฐ์ด๊ธฐ ๋๋ฌธ์
์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋จผ์ WebKit์ ํ๋ก์ ํธ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
ํ๋ก์ ํธ ์ค์ ํ์ผ์์ Build Phases > Link Binary With Libraries๋ก ์ด๋ํด +๋ฅผ ๋๋ฌ WebKit.framework๋ฅผ ์ถ๊ฐํฉ๋๋ค.
๋จผ์ ์คํ ๋ฆฌ๋ณด๋๋ ์๋์ ๊ฐ์ด ๊ตฌ์ฑํ์ต๋๋ค.
ViewController๋ Navigation Controller์ ์ฐ๊ฒฐ๋์ด ์๊ณ ,
๊ฒ์์ด ์ ๋ ฅ์ ์ํด ์คํ๋ทฐ๋ก ๊ฐ์ผ TextField, Button์ ๋ฐฐ์นํ์ต๋๋ค.
ViewController์ WebViewController ๋ชจ๋ ์คํ ๋ฆฌ๋ณด๋์ ์ปดํฌ๋ํธ ์ฐ๊ฒฐ๋ง ํด๋ ์ํ์ ๋๋ค.
ViewController.swift
tappedSearch Action ํจ์์๋ ์ ๋ ฅํ ๊ฒ์์ด์ ํจ๊ป WebViewController๋ก ์ด๋ํ๋ ๋์์ ๊ตฌํํ ์์ ์ ๋๋ค.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tvSearch: UITextField!/** ๊ฒ์์ด ํ
์คํธํ๋ */
/** life cycle */
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tappedSearch(_ sender: Any) {
}
}
๋จผ์ ๊ฒ์์ด๋ฅผ ์
๋ ฅํ ๋ค ๊ฒ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ์น๋ทฐ๋ก ์ด๋ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ฒ ์ต๋๋ค.
ViewController์ tappedSearch ํจ์์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
let text: String = tvSearch.text!
text ๋ณ์์ ์ ๋ ฅ๋ฐ์ ๊ฒ์์ด๋ฅผ ๋ฃ์ด, WebViewController๋ก ์ ๋ฌํ ์์ ์ ๋๋ค.
์ด์ด์ NavigationController์ ์ฌ์ฉํ์ฌ WebViewController๋ก ์ด๋ํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
if let navigationController = self.navigationController {
if !(navigationController.topViewController?.description.contains("WebViewController"))! {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
viewController.search = text
viewController.url = "https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&"
navigationController.pushViewController(viewController, animated: true)
}
}
๊ฒ์์ด์ url์ WebViewController์ ํ๋กํผํฐ๋ฅผ ํตํด ์ ๋ฌํฉ๋๋ค.
viewController.search = text
viewController.url = "https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&"
๋ค์ด๋ฒ์ ํน์ ํค์๋๋ฅผ ์
๋ ฅํด ๊ฒ์ํ๋ฉด url์ด `https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&query=๊ฒ์์ด` ์ ๊ฐ์ต๋๋ค.
์์ ๊ฐ์ด query๋ฅผ ์ฌ์ฉํ์ฌ, ๊ฒ์์ด๋ฅผ ์
๋ ฅํ๋ฉด ๋ค์ด๋ฒ์ ๊ฒฐ๊ณผ ํ๋ฉด์ ๋์์ค ์์ ์
๋๋ค.
WebViewController.swift
๋จผ์ WebKit๋ฅผ import ํ๊ณ , ์น๋ทฐ๋ฅผ ๋ด์ ํ๋กํผํฐ๋ฅผ ์ ์ธํฉ๋๋ค.
import UIKit
import WebKit
class WebViewController: UIViewController {
@IBOutlet weak var webViewGroup: UIView!/** ๋ฐฐ๊ฒฝ ๋ทฐ */
private var webView: WKWebView!/** ์น๋ทฐ */
var search: String!/** ๊ฒ์์ด */
var url: String!/** url */
/** life cycle */
override func viewDidLoad() {
super.viewDidLoad()
/** ๋ค๋น๊ฒ์ด์
๋ฐ ํ์ดํ */
self.navigationItem.title = search
}
}
์ด webView๋ฅผ ํตํด ์น๋ทฐ๋ฅผ ๊ทธ๋ฆฌ๊ณ , ๋ฏธ๋ฆฌ ๊ทธ๋ ค๋ ๋ทฐ์ธ webViewGroup์ ์ถ๊ฐํ ์์ ์
๋๋ค.
search, url ํ๋กํผํฐ์๋ ViewController๋ก ๋ถํฐ ๊ฐ์ ์ ๋ฌ๋ฐ์ ์ ์ฅํฉ๋๋ค.
๋จผ์ Delegate๋ฅผ ์ถ๊ฐํฉ๋๋ค.
webView.uiDelegate = self
webView.navigationDelegate = self
extension WebViewController: WKNavigationDelegate {
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
print("\(navigationAction.request.url?.absoluteString ?? "")" )
decisionHandler(.allow)
}
}
extension WebViewController: WKUIDelegate {
public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
}
}
extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
}
}
decidePolicyFor ํจ์๋ ์ ํจํ ๋๋ฉ์ธ์ธ์ง ์ฒดํฌํฉ๋๋ค.
์์ add()ํจ์๋ฅผ ์ฌ์ฉํด ๋ฉ์์ง๋ฅผ ๋ฑ๋กํ๋ค๋ฉด, WKScriptMessageHandler ํด๋์ค๋ฅผ ํตํด javaScript(์ฆ, ์น ํ์ด์ง)๋ก ๋ถํฐ ๋ฉ์์ง๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
์ด์ด์ WebView ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
viewDidLoad()์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํฉ๋๋ค.
WKPreferences๋ ์น ๋ทฐ์ ๋ํ ๊ธฐ๋ณธ ์์ฑ์ ์บก์ํํ ํด๋์ค๋ก, java๋ javaScript ์ค์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
let preferences = WKPreferences()
/** javaScript ์ฌ์ฉ ์ค์ */
preferences.javaScriptEnabled = true
/** ์๋์ผ๋ก javaScript๋ฅผ ํตํด ์ ์ฐฝ ์ด๊ธฐ ์ค์ */
preferences.javaScriptCanOpenWindowsAutomatically = true
WKUserContentController๋ ์น๋ทฐ์ javaScript ๊ฐ์ ์ํธ์์ฉ์ ๊ด๋ฆฌํ๋ ํด๋์ค์
๋๋ค.
javaScript์์ ์ฑ์ผ๋ก ๋ฉ์์ง๋ฅผ ์ ๋ฌํ ๋๋ add() ํจ์๋ฅผ ์ฌ์ฉํด ๋ฉ์์ง์ ์ด๋ฆ์ ์ค์ ํฉ๋๋ค.
let contentController = WKUserContentController()
/** ์ฌ์ฉํ ๋ฉ์์ง ๋ฑ๋ก */
contentController.add(self, name: "bridge")
WKWebViewConfiguration๋ ์น๋ทฐ๊ฐ ๋งจ ์ฒ์ ์ด๊ธฐํ๋ ๋ ํธ์ถ๋๋ฉฐ, ์น๋ทฐ๊ฐ ์์ฑ๋ ์ดํ์๋ ์ด ํด๋์ค๋ฅผ ํตํด ์์ฑ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. ์ด ์์ฑ์ ์ฌ์ฉํด WebView ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
let configuration = WKWebViewConfiguration()
/** preference, contentController ์ค์ */
configuration.preferences = preferences
configuration.userContentController = contentController
webView = WKWebView(frame: self.view.bounds, configuration: configuration)
๋ค์๊ณผ ๊ฐ์ด URLQueryItem() ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์น๋ทฐ์ ๋ํ๋ผ url์ ์ค์ ํฉ๋๋ค.
var components = URLComponents(string: url)!
components.queryItems = [ URLQueryItem(name: "query", value: search) ]
๋ง์ง๋ง์ผ๋ก ์์ฑํ webView๋ฅผ ์ ์ฉํ ํ, requset ๊ฐ์ฒด๋ฅผ ํตํด ์น ํ์ด์ง๋ฅผ ๋ก๋ํฉ๋๋ค.
webViewGroup.addSubview(webView)
setAutoLayout(from: webView, to: webViewGroup)
let request = URLRequest(url: components.url!)
webView.load(request)
AutoLayout์ ๋ฐ๋ก ํจ์๋ฅผ ๊ตฌํํด ์ค์ ํ์ต๋๋ค.
/** auto leyout ์ค์ */
public func setAutoLayout(from: UIView, to: UIView) {
from.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.init(item: from, attribute: .leading, relatedBy: .equal, toItem: to, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .trailing, relatedBy: .equal, toItem: to, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .top, relatedBy: .equal, toItem: to, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .bottom, relatedBy: .equal, toItem: to, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
view.layoutIfNeeded()
}
์๋๋ ๊ฒฐ๊ณผ ํ๋ฉด์ ๋๋ค.
์ ์ฒด ์ฝ๋์
๋๋ค.
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tvSearch: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
/** ์น ๋ทฐ๋ก ์ด๋ */
@IBAction func tappedSearch(_ sender: Any) {
let text: String = tvSearch.text!
if let navigationController = self.navigationController {
if !(navigationController.topViewController?.description.contains("WebViewController"))! {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
viewController.search = text
viewController.url = "https://m.search.naver.com/search.naver?sm=mtp_hty.top&where=m&"
navigationController.pushViewController(viewController, animated: true)
}
}
}
}
WebViewController.swift
import UIKit
import WebKit
class WebViewController: UIViewController {
@IBOutlet weak var webViewGroup: UIView!/** ๋ฐฐ๊ฒฝ ๋ทฐ */
private var webView: WKWebView!/** ์น ๋ทฐ */
var search: String!/** ๊ฒ์์ด */
var url: String!/** url */
/** life cycle */
override func viewDidLoad() {
super.viewDidLoad()
/** ๋ค๋น๊ฒ์ด์
๋ฐ ํ์ดํ */
self.navigationItem.title = search
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = true
let contentController = WKUserContentController()
contentController.add(self, name: "bridge")
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
configuration.userContentController = contentController
webView = WKWebView(frame: self.view.bounds, configuration: configuration)
var components = URLComponents(string: url)!
components.queryItems = [ URLQueryItem(name: "query", value: search) ]
let request = URLRequest(url: components.url!)
webView.uiDelegate = self
webView.navigationDelegate = self
webViewGroup.addSubview(webView)
setAutoLayout(from: webView, to: webViewGroup)
webView.load(request)
webView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseIn, animations: {
self.webView.alpha = 1
}) { _ in
}
}
/** auto leyout ์ค์ */
public func setAutoLayout(from: UIView, to: UIView) {
from.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.init(item: from, attribute: .leading, relatedBy: .equal, toItem: to, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .trailing, relatedBy: .equal, toItem: to, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .top, relatedBy: .equal, toItem: to, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint.init(item: from, attribute: .bottom, relatedBy: .equal, toItem: to, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
view.layoutIfNeeded()
}
}
extension WebViewController: WKNavigationDelegate {
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
print("\(navigationAction.request.url?.absoluteString ?? "")" )
decisionHandler(.allow)
}
}
extension WebViewController: WKUIDelegate {
public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
}
}
extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
}
}
์๋ ๋งํฌ์์๋ ์ ์ฒด ์ฝ๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
https://github.com/hoojeong-dev/Toy-Project-iOS/tree/main/WebView
Toy-Project-iOS/WebView at main · hoojeong-dev/Toy-Project-iOS
Contribute to hoojeong-dev/Toy-Project-iOS development by creating an account on GitHub.
github.com
'๐ป Programming > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[iOS] Request Methods๋ฅผ ๊ณ๋ค์ธ Alamofire ๊ฐ๋จ ์ฌ์ฉ๋ฒ (0) | 2025.02.27 |
---|