SlideShare a Scribd company logo
안정민
동적 데이터을 대응하는 코드 작성하기
Plugin 패턴을 활용하기
App
외부
App
외부
- Server
- App
- Web
- Apple Push Service
- Socket
API
App Scheme
WebKit JS
Push Noti
fi
cation
Socket
• 외부 서비스와 앱 간의 통신은 다양함
• 외부 서비스와 앱 간에는 약속한 데이터를 전달
• 전달하고 받을 데이터의 구조화
• API는 URL, Parameter, Response 등 송수신에 필요한 데이터 구조화 작업
• App Scheme, Javascript Handler 등에서 전달받은 데이터의 처리는 구조화가 되
어 있지 않음
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// 메시지의 이름과 body 추출
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String
else { return }
// Action에 따라 처리하는 switch 문
switch action {
case "loading": loading(body: messageBody)
case "openCard": openCard(body: messageBody)
case "payment": payment(body: messageBody)
case "log": log(body: messageBody)
default: break
}
}
// loading Action을 처리하는 함수
func loading(body: [String: Any]) {
guard
let value = body["show"] as? Bool
else { return }
switch value {
case true: showLoading()
case false: hideLoading()
}
}
// payment Action을 처리하는 함수
func payment(body: [String: Any]) {
guard
let id = body["paymentId"] as? String,
let info = body["paymentInfo"] as? [String: String]
else { return }
cardPayment(paymentId: id, paymentInfo: info)
}
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
// 메시지의 이름과 body 추출
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String
else { return }
// Action에 따라 처리하는 switch 문
switch action {
case "loading": loading(body: messageBody)
case "openCard": openCard(body: messageBody)
case "payment": payment(body: messageBody)
case "log": log(body: messageBody)
default: break
}
}
// loading Action을 처리하는 함수
func loading(body: [String: Any]) {
guard
let value = body["show"] as? Bool
else { return }
switch value {
case true: showLoading()
case false: hideLoading()
}
}
// payment Action을 처리하는 함수
func payment(body: [String: Any]) {
guard
let id = body["paymentId"] as? String,
let info = body["paymentInfo"] as? [String: String]
else { return }
cardPayment(paymentId: id, paymentInfo: info)
}
struct WKScriptMessageActionHandler {
let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void
}
class WebViewManager {
private let actionHandlers: [String: WKScriptMessageActionHandler]
var webView: WKWebView?
init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) {
self.actionHandlers = actionHandlers
}
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
// 메시지의 이름과 body 추출
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String
else { return }
actionHandlers[action]?.closure(messageBody, webView)
}
}
let loadingHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let value = body["show"] as? Bool
else { return }
switch value {
case true: showLoading()
case false: hideLoading()
}
}
let paymentHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let id = body["paymentId"] as? String,
let info = body["paymentInfo"] as? [String: String]
else { return }
cardPayment(paymentId: id, paymentInfo: info)
}
WebViewManager(actionHandlers: ["loading": loadingHandler,
"payment": paymentHandler])
• 모든 도메인과의 결합이 된 만능 WebView가 만들어지지 않도록 작업 필요
• 웹페이지에서 받은 ActionHandler를 직접 제어문을 통해 다루지 않아야함
• WebView를 사용하는 서비스, 도메인에서 Action과 Handler를 주입
• WebView는 WebView로서의 역할을 제한해야 함.
구조화
struct WKScriptMessageActionHandler {
let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void
}
class WebViewManager {
private let actionHandlers: [String: WKScriptMessageActionHandler]
var webView: WKWebView?
init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) {
self.actionHandlers = actionHandlers
}
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
// 메시지의 이름과 body 추출
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String
else { return }
actionHandlers[action]?.closure(messageBody, webView)
}
}
let loadingHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let value = body["show"] as? Bool
else { return }
switch value {
case true: showLoading()
case false: hideLoading()
}
}
let paymentHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let id = body["paymentId"] as? String,
let info = body["paymentInfo"] as? [String: String]
else { return }
cardPayment(paymentId: id, paymentInfo: info)
}
WebViewManager(actionHandlers: ["loading": loadingHandler,
"payment": paymentHandler])
struct WKScriptMessageActionHandler {
let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void
}
class WebViewManager {
private let actionHandlers: [String: WKScriptMessageActionHandler]
var webView: WKWebView?
init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) {
self.actionHandlers = actionHandlers
}
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
// 메시지의 이름과 body 추출
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String
else { return }
actionHandlers[action]?.closure(messageBody, webView)
}
}
let loadingHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let value = body["show"] as? Bool
else { return }
switch value {
case true: showLoading()
case false: hideLoading()
}
}
let paymentHandler = WKScriptMessageActionHandler {
[weak self] body, webview in
guard
let self,
let id = body["paymentId"] as? String,
let info = body["paymentInfo"] as? [String: String]
else { return }
cardPayment(paymentId: id, paymentInfo: info)
}
WebViewManager(actionHandlers: ["loading": loadingHandler,
"payment": paymentHandler])
protocol JSInterfacePluggable {
var action: String { get }
func callAsAction(_ message: [String: Any], with: WKWebView)
}
extension JSInterfaceSupervisor {
/// Loads a single plugin into the supervisor.
func loadPlugin(_ plugin: JSInterfacePluggable) {
let action = plugin.action
guard loadedPlugins[action] == nil else {
assertionFailure("(action) action already exists. Please
check the plugin.")
return
}
loadedPlugins[action] = plugin
}
}
extension JSInterfaceSupervisor {
/// Resolves an action and calls the corresponding plugin with a message and web view.
func resolve(
_ action: String,
message: [String: Any],
with webView: WKWebView) {
guard
let plugin = loadedPlugins[action], plugin.action == action
else {
assertionFailure("Failed to resolve (action): Action is not
loaded. Please ensure the plugin is correctly loaded.")
return
}
plugin.callAsAction(message, with: webView)
}
}
protocol JSInterfacePluggable {
var action: String { get }
func callAsAction(_ message: [String: Any], with: WKWebView)
}
/// Supervisor class responsible for loading and managing JS plugins.
class JSInterfaceSupervisor {
var loadedPlugins = [String: JSInterfacePluggable]()
init() {}
}
extension JSInterfaceSupervisor {
/// Loads a single plugin into the supervisor.
func loadPlugin(_ plugin: JSInterfacePluggable) {
let action = plugin.action
guard loadedPlugins[action] == nil else {
assertionFailure("(action) action already exists. Please
check the plugin.")
return
}
loadedPlugins[action] = plugin
}
}
extension JSInterfaceSupervisor {
/// Resolves an action and calls the corresponding plugin with a message and web view.
func resolve(
_ action: String,
message: [String: Any],
with webView: WKWebView) {
guard
let plugin = loadedPlugins[action], plugin.action == action
else {
assertionFailure("Failed to resolve (action): Action is not
loaded. Please ensure the plugin is correctly loaded.")
return
}
plugin.callAsAction(message, with: webView)
}
}
protocol JSInterfacePluggable {
var action: String { get }
func callAsAction(_ message: [String: Any], with: WKWebView)
}
/// Supervisor class responsible for loading and managing JS plugins.
class JSInterfaceSupervisor {
var loadedPlugins = [String: JSInterfacePluggable]()
init() {}
}
private let supervisor = JSInterfaceSupervisor()
// WKScriptMessageHandler 프로토콜 메서드 구현
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
guard
message.name == "actionHandler",
let messageBody = message.body as? [String: Any],
let action = messageBody["action"] as? String,
let webView
else { return }
supervisor.resolve(action, message: messageBody, with: webView)
}
class LoadingJSPlugin: JSInterfacePluggable {
let action = "loading"
func callAsAction(
_ message: [String: Any],
with webView: WKWebView) {
guard
let result = Parser(message)
else { return }
closure?(result.info, webView)
}
func set(_ closure: @escaping (Info, WKWebView) -> Void) {
self.closure = closure
}
private var closure: ((Info, WKWebView) -> Void)?
}
extension LoadingJSPlugin {
struct Info {
let uuid: String
let isShow: Bool
}
}
private extension LoadingJSPlugin {
struct Parser {
let info: Info
init?(_ dictonary: [String: Any]) {
guard
let uuid = dictonary["uuid"] as? String,
let body = dictonary["body"] as? [String: Any],
let isShow = body["isShow"] as? Bool
else { return nil }
info = .init(uuid: uuid, isShow: isShow)
}
}
}
class PaymentJSPlugin: JSInterfacePluggable {
let action = "payment"
func callAsAction(
_ message: [String: Any],
with webView: WKWebView) {
guard
let result = Parser(message)
else { return }
closure?(result.info, webView)
}
func set(_ closure: @escaping (Info, WKWebView) -> Void) {
self.closure = closure
}
private var closure: ((Info, WKWebView) -> Void)?
}
extension PaymentJSPlugin {
struct Info {
let uuid: String
let paymentAmount: Int
let paymentTransactionId: String
let paymentId: String
let paymentGoodsName: String
}
}
private extension PaymentJSPlugin {
struct Parser {
let info: Info
init?(_ dictonary: [String: Any]) {
guard
let uuid = dictonary["uuid"] as? String,
let body = dictonary["body"] as? [String: Any],
let paymentAmount = body["amount"] as? Int,
let paymentTransactionId = body["transactionId"] as? String,
let paymentId = body["paymentId"] as? String,
let paymentGoodsName = body["paymentGoodsName"] as? String
else { return nil }
info = .init(
uuid: uuid,
paymentAmount: paymentAmount,
paymentTransactionId: paymentTransactionId,
paymentId: paymentId,
paymentGoodsName: paymentGoodsName
)
}
}
}
class ViewController: UIViewController {
private let webViewManager = WebViewManager()
override func viewDidLoad() {
super.viewDidLoad()
initPlugins()
}
private func initPlugins() {
let loadingPlugin = LoadingJSPlugin()
let paymentPlugin = PaymentJSPlugin()
loadingPlugin.set { info, webView in
print("LoadingPlugin :", info)
}
paymentPlugin.set { info, webView in
print("PaymentJSPlugin :", info)
}
webViewManager.set(plugins: [loadingPlugin, paymentPlugin])
}
}
• Action Handler를 분석하고, 직접 다루게 되면 만능 WebView가 탄생
• Plugin으로 Action을 구조화하며, WebView에서 등록된 Plugin을 호출하여 해당
Action의 책임을 가지지 않도록 함.
• 각 Plugin은 Action, 데이터 유효성 검증을 구현하여 Plugin을 검증할 수 있으며, Plugin
이 호출되었을 때의 응답값을 예상할 수 있음
• Plugin 방식은 웹페이지의 Javascript, 앱 푸시, 챗의 데이터, 웹 소켓 등의 동적 데이터
를 유연하게 대응할 수 있음
데모
QnA

More Related Content

Similar to 20240516_동적_데이터을_대응하는_코드_작성하기.pdf

Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for Joomla
Luke Summerfield
 
JavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
JavaScript APIs - The Web is the Platform - MozCamp, Buenos AiresJavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
JavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
Robert Nyman
 
Pengenalan blaast platform sdk
Pengenalan blaast platform sdkPengenalan blaast platform sdk
Pengenalan blaast platform sdk
Arief Bayu Purwanto
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, ChileJavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
Robert Nyman
 
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
PROIDEA
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
JavaScript APIs - The Web is the Platform - MDN Hack Day, MontevideoJavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
Robert Nyman
 
ASP.NET MVC Internals
ASP.NET MVC InternalsASP.NET MVC Internals
ASP.NET MVC Internals
Vitaly Baum
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run
Frederik De Bruyne
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
Tse-Ching Ho
 
Academy PRO: Push notifications. Denis Beketsky
Academy PRO: Push notifications. Denis BeketskyAcademy PRO: Push notifications. Denis Beketsky
Academy PRO: Push notifications. Denis Beketsky
Binary Studio
 
Android+ax+app+wcf
Android+ax+app+wcfAndroid+ax+app+wcf
Android+ax+app+wcf
Aravindharamanan S
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
Macoscope
 
AJAX
AJAXAJAX
AJAX
AJAXAJAX
Spring ws
Spring wsSpring ws
Spring ws
Souleymane MATO
 
Android ax app wcf
Android ax app wcfAndroid ax app wcf
Android ax app wcf
Aravindharamanan S
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Framework
vhazrati
 
Overview Of Lift Framework
Overview Of Lift FrameworkOverview Of Lift Framework
Overview Of Lift Framework
Xebia IT Architects
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web Framework
IndicThreads
 
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos AiresJavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
Robert Nyman
 

Similar to 20240516_동적_데이터을_대응하는_코드_작성하기.pdf (20)

Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for Joomla
 
JavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
JavaScript APIs - The Web is the Platform - MozCamp, Buenos AiresJavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
JavaScript APIs - The Web is the Platform - MozCamp, Buenos Aires
 
Pengenalan blaast platform sdk
Pengenalan blaast platform sdkPengenalan blaast platform sdk
Pengenalan blaast platform sdk
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, ChileJavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
JavaScript APIs - The Web is the Platform - MDN Hack Day, Santiago, Chile
 
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
4Developers 2018: Real-time capabilities in ASP.NET Core web applications (To...
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
JavaScript APIs - The Web is the Platform - MDN Hack Day, MontevideoJavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Montevideo
 
ASP.NET MVC Internals
ASP.NET MVC InternalsASP.NET MVC Internals
ASP.NET MVC Internals
 
#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run#win8aca : How and when metro style apps run
#win8aca : How and when metro style apps run
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
 
Academy PRO: Push notifications. Denis Beketsky
Academy PRO: Push notifications. Denis BeketskyAcademy PRO: Push notifications. Denis Beketsky
Academy PRO: Push notifications. Denis Beketsky
 
Android+ax+app+wcf
Android+ax+app+wcfAndroid+ax+app+wcf
Android+ax+app+wcf
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
 
AJAX
AJAXAJAX
AJAX
 
AJAX
AJAXAJAX
AJAX
 
Spring ws
Spring wsSpring ws
Spring ws
 
Android ax app wcf
Android ax app wcfAndroid ax app wcf
Android ax app wcf
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Framework
 
Overview Of Lift Framework
Overview Of Lift FrameworkOverview Of Lift Framework
Overview Of Lift Framework
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web Framework
 
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos AiresJavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
JavaScript APIs - The Web is the Platform - MDN Hack Day - Buenos Aires
 

More from 정민 안

20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
정민 안
 
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가? Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
정민 안
 
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
정민 안
 
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdfInjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
정민 안
 
iOS Modular Architecture with Tuist
iOS Modular Architecture with TuistiOS Modular Architecture with Tuist
iOS Modular Architecture with Tuist
정민 안
 
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
정민 안
 
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
정민 안
 
Letusgo 2019 Summer - StringInterpolation and SwiftUI
Letusgo 2019 Summer - StringInterpolation and SwiftUILetusgo 2019 Summer - StringInterpolation and SwiftUI
Letusgo 2019 Summer - StringInterpolation and SwiftUI
정민 안
 
A Framework Driven Development
A Framework Driven DevelopmentA Framework Driven Development
A Framework Driven Development
정민 안
 
Debugging with xcode, lldb and chisel
Debugging with xcode, lldb and chiselDebugging with xcode, lldb and chisel
Debugging with xcode, lldb and chisel
정민 안
 
fastlane을 이용하여 iOS/Mac 앱 관리하기
fastlane을 이용하여 iOS/Mac 앱 관리하기fastlane을 이용하여 iOS/Mac 앱 관리하기
fastlane을 이용하여 iOS/Mac 앱 관리하기
정민 안
 
Introduce fastlane
Introduce fastlaneIntroduce fastlane
Introduce fastlane
정민 안
 
Git lecture
Git lectureGit lecture
Git lecture
정민 안
 
형상관리 발표자료 안정민
형상관리 발표자료 안정민형상관리 발표자료 안정민
형상관리 발표자료 안정민
정민 안
 

More from 정민 안 (14)

20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
20240325 TuistNight 모듈로 나누면 알아두면 좋을 3가지 팁
 
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가? Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
Let'Swift 2023 iOS 애플리케이션 개발 생산성 고찰
- 정시 퇴근을 위해 우리는 어떻게 해야할 것인가?
 
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
20221131_레츠스위프트_2022_iOS개발에서_알아두면_좋은것들.pdf
 
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdfInjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
 
iOS Modular Architecture with Tuist
iOS Modular Architecture with TuistiOS Modular Architecture with Tuist
iOS Modular Architecture with Tuist
 
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
DI Container를 이용하여 레거시와 모듈화를 동시에 잡기
 
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
MVC, MVVM, ReactorKit, VIPER를 거쳐 RIB 정착기
 
Letusgo 2019 Summer - StringInterpolation and SwiftUI
Letusgo 2019 Summer - StringInterpolation and SwiftUILetusgo 2019 Summer - StringInterpolation and SwiftUI
Letusgo 2019 Summer - StringInterpolation and SwiftUI
 
A Framework Driven Development
A Framework Driven DevelopmentA Framework Driven Development
A Framework Driven Development
 
Debugging with xcode, lldb and chisel
Debugging with xcode, lldb and chiselDebugging with xcode, lldb and chisel
Debugging with xcode, lldb and chisel
 
fastlane을 이용하여 iOS/Mac 앱 관리하기
fastlane을 이용하여 iOS/Mac 앱 관리하기fastlane을 이용하여 iOS/Mac 앱 관리하기
fastlane을 이용하여 iOS/Mac 앱 관리하기
 
Introduce fastlane
Introduce fastlaneIntroduce fastlane
Introduce fastlane
 
Git lecture
Git lectureGit lecture
Git lecture
 
형상관리 발표자료 안정민
형상관리 발표자료 안정민형상관리 발표자료 안정민
형상관리 발표자료 안정민
 

Recently uploaded

Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
Severalnines
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
Remote DBA Services
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
ICS
 
What’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete RoadmapWhat’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete Roadmap
Envertis Software Solutions
 
ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.
Maitrey Patel
 
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
safelyiotech
 
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptxOperational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
sandeepmenon62
 
What is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdfWhat is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdf
kalichargn70th171
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Paul Brebner
 
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptxMigration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
ervikas4
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
kalichargn70th171
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
gapen1
 
Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...
Paul Brebner
 
How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
ToXSL Technologies
 
Microservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we workMicroservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we work
Sven Peters
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
Grant Fritchey
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
XfilesPro
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
Peter Muessig
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
Alina Yurenko
 

Recently uploaded (20)

Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
 
Oracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptxOracle Database 19c New Features for DBAs and Developers.pptx
Oracle Database 19c New Features for DBAs and Developers.pptx
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
 
What’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete RoadmapWhat’s New in Odoo 17 – A Complete Roadmap
What’s New in Odoo 17 – A Complete Roadmap
 
ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.ACE - Team 24 Wrapup event at ahmedabad.
ACE - Team 24 Wrapup event at ahmedabad.
 
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
 
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptxOperational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
Operational ease MuleSoft and Salesforce Service Cloud Solution v1.0.pptx
 
What is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdfWhat is Continuous Testing in DevOps - A Definitive Guide.pdf
What is Continuous Testing in DevOps - A Definitive Guide.pdf
 
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
Why Apache Kafka Clusters Are Like Galaxies (And Other Cosmic Kafka Quandarie...
 
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptxMigration From CH 1.0 to CH 2.0 and  Mule 4.6 & Java 17 Upgrade.pptx
Migration From CH 1.0 to CH 2.0 and Mule 4.6 & Java 17 Upgrade.pptx
 
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
A Comprehensive Guide on Implementing Real-World Mobile Testing Strategies fo...
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
 
Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...Superpower Your Apache Kafka Applications Development with Complementary Open...
Superpower Your Apache Kafka Applications Development with Complementary Open...
 
How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
 
Microservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we workMicroservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we work
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
 
UI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design SystemUI5con 2024 - Bring Your Own Design System
UI5con 2024 - Bring Your Own Design System
 
All you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVMAll you need to know about Spring Boot and GraalVM
All you need to know about Spring Boot and GraalVM
 

20240516_동적_데이터을_대응하는_코드_작성하기.pdf

  • 1. 안정민 동적 데이터을 대응하는 코드 작성하기 Plugin 패턴을 활용하기
  • 3. App 외부 - Server - App - Web - Apple Push Service - Socket API App Scheme WebKit JS Push Noti fi cation Socket
  • 4. • 외부 서비스와 앱 간의 통신은 다양함 • 외부 서비스와 앱 간에는 약속한 데이터를 전달 • 전달하고 받을 데이터의 구조화 • API는 URL, Parameter, Response 등 송수신에 필요한 데이터 구조화 작업 • App Scheme, Javascript Handler 등에서 전달받은 데이터의 처리는 구조화가 되 어 있지 않음
  • 5. // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // 메시지의 이름과 body 추출 guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String else { return } // Action에 따라 처리하는 switch 문 switch action { case "loading": loading(body: messageBody) case "openCard": openCard(body: messageBody) case "payment": payment(body: messageBody) case "log": log(body: messageBody) default: break } } // loading Action을 처리하는 함수 func loading(body: [String: Any]) { guard let value = body["show"] as? Bool else { return } switch value { case true: showLoading() case false: hideLoading() } } // payment Action을 처리하는 함수 func payment(body: [String: Any]) { guard let id = body["paymentId"] as? String, let info = body["paymentInfo"] as? [String: String] else { return } cardPayment(paymentId: id, paymentInfo: info) }
  • 6. // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { // 메시지의 이름과 body 추출 guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String else { return } // Action에 따라 처리하는 switch 문 switch action { case "loading": loading(body: messageBody) case "openCard": openCard(body: messageBody) case "payment": payment(body: messageBody) case "log": log(body: messageBody) default: break } } // loading Action을 처리하는 함수 func loading(body: [String: Any]) { guard let value = body["show"] as? Bool else { return } switch value { case true: showLoading() case false: hideLoading() } } // payment Action을 처리하는 함수 func payment(body: [String: Any]) { guard let id = body["paymentId"] as? String, let info = body["paymentInfo"] as? [String: String] else { return } cardPayment(paymentId: id, paymentInfo: info) }
  • 7. struct WKScriptMessageActionHandler { let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void } class WebViewManager { private let actionHandlers: [String: WKScriptMessageActionHandler] var webView: WKWebView? init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) { self.actionHandlers = actionHandlers } // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { // 메시지의 이름과 body 추출 guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String else { return } actionHandlers[action]?.closure(messageBody, webView) } } let loadingHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let value = body["show"] as? Bool else { return } switch value { case true: showLoading() case false: hideLoading() } } let paymentHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let id = body["paymentId"] as? String, let info = body["paymentInfo"] as? [String: String] else { return } cardPayment(paymentId: id, paymentInfo: info) } WebViewManager(actionHandlers: ["loading": loadingHandler, "payment": paymentHandler])
  • 8. • 모든 도메인과의 결합이 된 만능 WebView가 만들어지지 않도록 작업 필요 • 웹페이지에서 받은 ActionHandler를 직접 제어문을 통해 다루지 않아야함 • WebView를 사용하는 서비스, 도메인에서 Action과 Handler를 주입 • WebView는 WebView로서의 역할을 제한해야 함.
  • 10. struct WKScriptMessageActionHandler { let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void } class WebViewManager { private let actionHandlers: [String: WKScriptMessageActionHandler] var webView: WKWebView? init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) { self.actionHandlers = actionHandlers } // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { // 메시지의 이름과 body 추출 guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String else { return } actionHandlers[action]?.closure(messageBody, webView) } } let loadingHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let value = body["show"] as? Bool else { return } switch value { case true: showLoading() case false: hideLoading() } } let paymentHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let id = body["paymentId"] as? String, let info = body["paymentInfo"] as? [String: String] else { return } cardPayment(paymentId: id, paymentInfo: info) } WebViewManager(actionHandlers: ["loading": loadingHandler, "payment": paymentHandler])
  • 11. struct WKScriptMessageActionHandler { let closure: (_ body: [String: Any], _ webView: WKWebView?) -> Void } class WebViewManager { private let actionHandlers: [String: WKScriptMessageActionHandler] var webView: WKWebView? init(actionHandlers: [String: WKScriptMessageActionHandler] = [:]) { self.actionHandlers = actionHandlers } // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { // 메시지의 이름과 body 추출 guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String else { return } actionHandlers[action]?.closure(messageBody, webView) } } let loadingHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let value = body["show"] as? Bool else { return } switch value { case true: showLoading() case false: hideLoading() } } let paymentHandler = WKScriptMessageActionHandler { [weak self] body, webview in guard let self, let id = body["paymentId"] as? String, let info = body["paymentInfo"] as? [String: String] else { return } cardPayment(paymentId: id, paymentInfo: info) } WebViewManager(actionHandlers: ["loading": loadingHandler, "payment": paymentHandler])
  • 12. protocol JSInterfacePluggable { var action: String { get } func callAsAction(_ message: [String: Any], with: WKWebView) }
  • 13. extension JSInterfaceSupervisor { /// Loads a single plugin into the supervisor. func loadPlugin(_ plugin: JSInterfacePluggable) { let action = plugin.action guard loadedPlugins[action] == nil else { assertionFailure("(action) action already exists. Please check the plugin.") return } loadedPlugins[action] = plugin } } extension JSInterfaceSupervisor { /// Resolves an action and calls the corresponding plugin with a message and web view. func resolve( _ action: String, message: [String: Any], with webView: WKWebView) { guard let plugin = loadedPlugins[action], plugin.action == action else { assertionFailure("Failed to resolve (action): Action is not loaded. Please ensure the plugin is correctly loaded.") return } plugin.callAsAction(message, with: webView) } } protocol JSInterfacePluggable { var action: String { get } func callAsAction(_ message: [String: Any], with: WKWebView) } /// Supervisor class responsible for loading and managing JS plugins. class JSInterfaceSupervisor { var loadedPlugins = [String: JSInterfacePluggable]() init() {} }
  • 14. extension JSInterfaceSupervisor { /// Loads a single plugin into the supervisor. func loadPlugin(_ plugin: JSInterfacePluggable) { let action = plugin.action guard loadedPlugins[action] == nil else { assertionFailure("(action) action already exists. Please check the plugin.") return } loadedPlugins[action] = plugin } } extension JSInterfaceSupervisor { /// Resolves an action and calls the corresponding plugin with a message and web view. func resolve( _ action: String, message: [String: Any], with webView: WKWebView) { guard let plugin = loadedPlugins[action], plugin.action == action else { assertionFailure("Failed to resolve (action): Action is not loaded. Please ensure the plugin is correctly loaded.") return } plugin.callAsAction(message, with: webView) } } protocol JSInterfacePluggable { var action: String { get } func callAsAction(_ message: [String: Any], with: WKWebView) } /// Supervisor class responsible for loading and managing JS plugins. class JSInterfaceSupervisor { var loadedPlugins = [String: JSInterfacePluggable]() init() {} } private let supervisor = JSInterfaceSupervisor() // WKScriptMessageHandler 프로토콜 메서드 구현 func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { guard message.name == "actionHandler", let messageBody = message.body as? [String: Any], let action = messageBody["action"] as? String, let webView else { return } supervisor.resolve(action, message: messageBody, with: webView) }
  • 15. class LoadingJSPlugin: JSInterfacePluggable { let action = "loading" func callAsAction( _ message: [String: Any], with webView: WKWebView) { guard let result = Parser(message) else { return } closure?(result.info, webView) } func set(_ closure: @escaping (Info, WKWebView) -> Void) { self.closure = closure } private var closure: ((Info, WKWebView) -> Void)? } extension LoadingJSPlugin { struct Info { let uuid: String let isShow: Bool } } private extension LoadingJSPlugin { struct Parser { let info: Info init?(_ dictonary: [String: Any]) { guard let uuid = dictonary["uuid"] as? String, let body = dictonary["body"] as? [String: Any], let isShow = body["isShow"] as? Bool else { return nil } info = .init(uuid: uuid, isShow: isShow) } } }
  • 16. class PaymentJSPlugin: JSInterfacePluggable { let action = "payment" func callAsAction( _ message: [String: Any], with webView: WKWebView) { guard let result = Parser(message) else { return } closure?(result.info, webView) } func set(_ closure: @escaping (Info, WKWebView) -> Void) { self.closure = closure } private var closure: ((Info, WKWebView) -> Void)? } extension PaymentJSPlugin { struct Info { let uuid: String let paymentAmount: Int let paymentTransactionId: String let paymentId: String let paymentGoodsName: String } } private extension PaymentJSPlugin { struct Parser { let info: Info init?(_ dictonary: [String: Any]) { guard let uuid = dictonary["uuid"] as? String, let body = dictonary["body"] as? [String: Any], let paymentAmount = body["amount"] as? Int, let paymentTransactionId = body["transactionId"] as? String, let paymentId = body["paymentId"] as? String, let paymentGoodsName = body["paymentGoodsName"] as? String else { return nil } info = .init( uuid: uuid, paymentAmount: paymentAmount, paymentTransactionId: paymentTransactionId, paymentId: paymentId, paymentGoodsName: paymentGoodsName ) } } }
  • 17. class ViewController: UIViewController { private let webViewManager = WebViewManager() override func viewDidLoad() { super.viewDidLoad() initPlugins() } private func initPlugins() { let loadingPlugin = LoadingJSPlugin() let paymentPlugin = PaymentJSPlugin() loadingPlugin.set { info, webView in print("LoadingPlugin :", info) } paymentPlugin.set { info, webView in print("PaymentJSPlugin :", info) } webViewManager.set(plugins: [loadingPlugin, paymentPlugin]) } }
  • 18. • Action Handler를 분석하고, 직접 다루게 되면 만능 WebView가 탄생 • Plugin으로 Action을 구조화하며, WebView에서 등록된 Plugin을 호출하여 해당 Action의 책임을 가지지 않도록 함. • 각 Plugin은 Action, 데이터 유효성 검증을 구현하여 Plugin을 검증할 수 있으며, Plugin 이 호출되었을 때의 응답값을 예상할 수 있음 • Plugin 방식은 웹페이지의 Javascript, 앱 푸시, 챗의 데이터, 웹 소켓 등의 동적 데이터 를 유연하게 대응할 수 있음
  • 20. QnA