[WKWebView] Can't find variable: webkit, undefined is not an object(evaluating 'window.webkit.messageHandlers) error 해결 방법
1. 문제 상황
하이브리드 앱 프로젝트를 진행하던 도중, WKwebview 에서 보여주는 web js 파일에서 native 앱으로 DB 관련한 파일을 전송해야 하는 작업을 해야 할 일이 생겼다.
정말 iOS에 엄청난 생초보로서 처음 해보는 부분이었지만, stackoverflow와 블로그의 도움을 받아 쓱쓱 해가는 중이고, 나름대로 와 이렇게 체계적으로 잘 만들어져있구나! 끄덕끄덕 하면서 기능을 추가해나갔다.
가장 깔끔하게 정리되어 있는 블로그이니 이 기능 구현 해보고싶은 분들은 따라해봐도 좋을 것 같다ㅎㅎ
https://oingbong.tistory.com/225
WKWebview를 이용한 Javascript, Swift 양방향 통신
아래 작성된 방법은 WebView 가 로드될 때 실행되는 것을 기준으로 하였으며 네이티브 앱에서 버튼을 누르거나 특정 동작을 할 때 Javascript 로 값을 전달하고 싶은 경우에는 아래 블로그에 나온 방
oingbong.tistory.com
내가 구현 해놓은 함수는 다음과 같다. 리팩토링을 하지 않고 막 짜고 있어서 코드가 매우 더럽다..;;
일단 컨트롤러의 viewDidLoad 함수는 다음과 같다.
override func viewDidLoad() {
super.viewDidLoad()
let contentController = WKUserContentController()
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: self.view.frame, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
// [start] 웹뷰 load
self.view.addSubview(webView)
let myURL = URL(string: "http://localhost:1234")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
// [end] 웹뷰 load
// [start] 당겨서 새로고침
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
webView.scrollView.addSubview(refreshControl)
// [end] 당겨서 새로고침
// [start] Birdge 등록
contentController.add(self, name: "getFollowStatus")
webConfiguration.userContentController = contentController
// [end] Birdge 등록
}
호출된 Bridge를 처리하는 extension 컨트롤러 함수이다.
// [start] 호출된 Bridge 처리
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "getFollowStatus":
print(message.body)
default:
break
}
}
}
// [end] 호출된 Bridge 처리
값을 보내는 JS 코드는 다음과 같다.
// 팔로우 언팔로우 버튼을 클릭했을 때.
const clickFollow = (btnId, value, btnUrl) => {
if (!token) {
location.href = '/section'
} else {
axios({
method: 'post',
url: btnUrl,
data: {
userId: currentUserID,
followValue: value,
}
}).then((res) => {
///////////////////// 값을 보내는 test ///////////////////
var message = {"aaa":"bbbbbbbbbbbbbbbb"}
webkit.messageHandlers.getFollowStatus.postMessage(message);
////////////////////////////////////////////////////////
if (btnUrl === 'follow')
alert('팔로우 되었습니다.');
else
alert('언팔로우 되었습니다.');
section_display_change(btnId);
}).catch(error => {
alert(error.message);
});
}
};
빌드도 잘 되고 돌려보며 내가 원하는 값이 찍히나? 테스트를 해보는데 다음과 같은 오류가 뜬다. 위는 'window.webkit' 으로 값을 넘겼을 때이고, 아래는 'webkit' 으로 값을 넘겼을 때이다.
이런 오류들이 찍히면서 원하는 값이 넘어오지 않았다.
혹시 내가 빼먹은게 있나 다른 사람들의 코드들을 참조하고, 에러코드 관련해서 stackoverflow 등 수십개의 문서를 찾아봤는데, 아무리 봐도 빼먹은 것도 없었고 딱히 문제가 생길 만한 여지가 보이지 않았다.
아직 JS에서 Native로 무언가를 보낸다는게 처음이었기 떄문에
"아... 내가 애초에 안되는걸 하고 있나? 하이브리드 앱은 원래 이렇게 만드는게 아닌가? 하는 의문까지 들었다.."
2. 해결
다른 기능을 사용할 수 있으면 빨리 다른 방법으로 기능을 구현했겠지만, 대체할 수 없는 필수적인 기능이었기 때문에 계속 방법을 찾았고 결국 방법을 찾았다!!! 무표정으로 계속 해보다가 print 찍히는 거 보고 엄청 놀랐다..
아마 stackoverflow로 해결하지 못한 대부분의 사람들은 이 방법으로 해결할 수 있을 것이다.
문제는 'viewDidLoad 함수'에 있었다.
override func viewDidLoad() {
super.viewDidLoad()
let contentController = WKUserContentController()
let webConfiguration = WKWebViewConfiguration()
// [start] Birdge 등록
contentController.add(self, name: "getFollowStatus")
webConfiguration.userContentController = contentController
// [end] Birdge 등록
webView = WKWebView(frame: self.view.frame, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
// [start] 웹뷰 load
let myURL = URL(string: "http://localhost:1234")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
self.view.addSubview(webView)
// [end] 웹뷰 load
// [start] 당겨서 새로고침
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
webView.scrollView.addSubview(refreshControl)
// [end] 당겨서 새로고침
}
위 코드와 차이점이 보이는가? 순서에 집중해서 보면 차이점이 한눈에 들어올 것이다.
바로 Bridge를 등록하고 이를 configuration에 등록하는 작업을,
Birdge가 등록된 configuration을 사용해서 WKWebview를 생성하기 이전에 해야 한다는 것이다.
너무도 당연한건데... 놓치고 있었다!!
이 방법 뿐만 아니라 WKWebview의 환경설정을 수정하고 나중에 "적용" 시킬 수 있어도 문제는 없을 것 같다.
하지만 나는 둘다 하지 않았지...
이렇게 순서만 수정해주니 길고 길게 고민했던 error를 드디어 잡아낼 수 있게 되었다.
이제 빨리 다른 작업해야지!!!