[iOS] WebView javaScript 함수 호출
앱 개발을 하면서 웹뷰를 활용하는 경우가 많음.
네이티브의 장점도 있지만, 웹뷰가 가진 장점도 있어서 각 특성을 구분하여 성격에 맞게 조합해서 사용하는 것이 좋음
히스토리
- 2021.10.25 15:35
- 초기 포스팅 작성
- 2024.11.17 14:49
- iOS 네이티브 환경 코드 최신화
- 가독성 개선
목차
- 웹 환경 설정
- iOS 환경 설정
웹 환경 설정
웹 개발자가 이미 존재한다면 생략해도 되는데, 웹 개발자가 없을 경우 테스트 환경 구축을 위해 네이티브 개발자를 위한 가이드라인.
웹 이미 있다면 건너뛰기
- VSCode 설치
- node.js 설치
Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
nodejs.org
- 위의 사이트에 접속하면 아래 이미지 처럼 사이트가 나오는데, iOS 개발자는 주로 맥북을 사용할 것이므로 macOS 가이드라인에 따라 설치
- 안정적이고, 신뢰도 높은거 선택했으나, 최신해도 상관 없음
- 개인적으로는 예전에 jdk 버전을 올리면서 돌아가는 문제가 생겼던 적이 있어서, 무조건 최신을 따라가기 보다 안정적인거 선택하는거 좋아함.

- 설치가 잘 되었는지 확인하고 터미널로 작업하기
- VSCode를 열어서 상단 메뉴바에 새 터미널 클릭


- VSCode 내부의 터미널에 아래 커맨드 입력하면 정상적으로 설치되었다는 v~ 가 나옴
node --version
npm --version (node.js를 설치하면서 함께 설치된다.)

정상적으로 설치된 것을 확인했다면 터미널에 다음과 아래의 코드를 작성
// 명령어
npm install -g nodemon
// 권한 관련 오류가 날 경우
npm install -g express-generator
sudo npm install -g express-generator
- 위 작업 수행 후 폴더를 열어서 내가 원하는 프로젝트 파일명을 작성하고 프로젝트 열기

폴더를 만들었다면 터미널의 시작 위치가 자기가 만든 폴더명의 위치로 바뀜, 우리가 아까 설치한 express 패키지를 사용하여 터미널
만약 터미널이 닫혀있다면 새 터미널 열어주기
express --ejs 폴더명
(예시) 만약 내가 폴더 열기를 통해 폴더명을 TestExpress로 만들었다면
express --ejs TestExpress 를 터미널에 입력해준다.
이 과정이 끝나면 아래 이미지처럼 파일이 생김

views - index.ejs 파일을 일단 보자.
- index.ejs에 작성해줄 코드
<!DOCTYPE html>
<html>
<head>
<!-- <title><%= title %></title> -->
<link rel='stylesheet' href='/stylesheets/style.css' />
<script type="text/javascript" charset="UTF-8">
function callNative() {
try {
webkit.messageHandlers.callbackHandler.postMessage("MessageBody");
print("callNative");
} catch(err) {
alert(err);
}
}
function redHeader() {
alert('redHeader() CALL');
document.querySelector('h1').style.color = "red";
}
function funcName() {
return "OK";
}
</script>
</head>
<body>
<h1>IOS용 메시지</h1>
<br><input type="button" onclick= 'callNative()' value="네이티브 함수 호출"/>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>
- 웹에서 보기 위해 실행하는 과정
터미널에 둘중 하나 입력
nodemon ./bin/www
또는
nodemon start
그 이후에 크롬 브라우저에서
http://localhost:3000/ 입력해주기.

위처럼 사이트가 나타나면 성공
iOS 환경설정
여기 과정부터는 Xcode에서 Swift 언어로 진행
- UI 확인을 위한 웹뷰 세팅
- 해당 예제에서는 스토리보드로 했는데, SwiftUI나 코드기반 UI 사용해도 무방함.
- 웹뷰를 넣고 레이아웃 잡아주기

- ViewController 코드
import UIKit
import WebKit
final class ViewController: UIViewController {
/// 웹의 js랑 통신할 컨텐트 컨트롤러
let contentController = WKUserContentController()
/// 스토리보드 기반 웹뷰에서 사용 불가능.
let configuration = WKWebViewConfiguration()
private func loadWebView() {
guard let url = URL(string: "http://localhost:3000") else { return }
let urlReuqest = URLRequest(url: url)
// !!!: - native -> webview(js) call 초기 세팅시에 가능한 환경설정으로 source부분에 함수 대신 HTML직접 삽입 가능
let userScript = WKUserScript(
source: "redHeader()",
injectionTime: .atDocumentEnd,
forMainFrameOnly: true
)
contentController.addUserScript(userScript)
// !!!: - webview(js) -> native call으로 name의 값을 지정하여, js에서 webkit.messageHandlers.NAME.postMessage("");와 연동되는 것, userContentController함수에서 처리
contentController.add(self, name: "callbackHandler")
/// 컨텐츠 컨트롤러 세팅
configuration.userContentController = contentController
// 웹뷰 로드
webView.load(urlReuqest)
}
// MARK: - Initialize
init() {
super.init(nibName: nil, bundle: nil)
configureUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configureUI()
}
// MARK: - UIComponents
/// 웹뷰 객체
private lazy var webView: WKWebView = {
$0.uiDelegate = self
$0.navigationDelegate = self
return $0
}(WKWebView())
/// 웹뷰가 로드될 때 필요한 인디케이터
private let activityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
private func configureUI() {
view.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
])
view.addSubview(activityIndicator)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
extension ViewController: WKNavigationDelegate,
WKUIDelegate,
WKScriptMessageHandler {
/// 웹뷰가 로드가 끝난 시점에 호출
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// !!!: - native -> webview(js) call을 통해 page에 js를 주입하는 방법
webView.evaluateJavaScript("javascript:redHeader()") { message, error in
print(message, error?.localizedDescription)
}
}
/// 웹뷰에서 이벤트 떨어지면 여기로 옴
///
/// 메시지에서 name으로 판단
/// name의 경우 `enum`을 활용해 규칙을 정하는 것이 좋음
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "callbackHandler":
print(message.body)
showsAlert()
default:
print("정의되지 않은 메시지")
}
}
private func showsAlert() {
let alert = UIAlertController(title: "Alert", message: "Hello, World!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
뷰 컨트롤러에 대한 전체 코드.
- 아주 간단한 기능들만 담았는데, 해당 원리를 기반으로 자세한 부분을 따로 확인해보면 좋음
결과물


- 결과물을 보면 redHeader() call의 호출되고, iOS용 메시지가 빨간색 글씨로 바뀐 것을 확인할 수 있다.
(참고)
https://zetal.tistory.com/entry/WKUserContentController
WKUserContentController 뽀개기
WKUserContentController 뽀개기 이번 포스팅에서는 WKWebView 기능 중에 하나인 WKUserContentController 기능을 살펴보겠습니다. WKWebView기능 중에 좋은 기능이 있습니다. 기존 UIWebView에서는 지원이 안됐던..
zetal.tistory.com
https://ios-development.tistory.com/701
[iOS - swift] 2. WKWebView 사용 방법 (웹뷰, 쿠키, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate)
1. WKWebView 개념1 (UIWebView, AJAX, XHR, 캐시, 쿠키) 2. WKWebView 사용 방법 (웹뷰, 쿠키, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate) WKWebView를 사용하기 전 알아야할 기본 개..
ios-development.tistory.com
loadView와 viewDidLoad 차이에 대한 질문입니다. - 야곰닷넷
안녕하세요. Swift를 공부한지 시간이 흐르니 스토리보드와 인터페이스 빌더로 UI를 구성하는 거 보다 코드만으로 View를 그리는 […]
yagom.net
'project > 개발 업무' 카테고리의 다른 글
[XCode 15.0 beta] Preview Macro Bug (0) | 2023.06.08 |
---|---|
Lottie 리소스 문제로 앱이 초기화되는 현상 (0) | 2022.07.12 |
iOS RxDelegateProxy 만들어보기 #2 (StarScream + RxProxy) (0) | 2022.01.12 |
iOS 앱 이름 변경하기 (1) | 2021.11.09 |
iOS 공유하기 LPLinkMetadata (UIKit, SwiftUI) 디자인패턴 적용 (0) | 2021.06.07 |