Skip to content

Instantly share code, notes, and snippets.

@skyofdwarf
Last active November 4, 2022 04:26
Show Gist options
  • Save skyofdwarf/d76cf51ff79356010cfaa627fe62c6b3 to your computer and use it in GitHub Desktop.
Save skyofdwarf/d76cf51ff79356010cfaa627fe62c6b3 to your computer and use it in GitHub Desktop.
WKWebView 스크립트메시지 함수 분리를 위한 프로토콜
//
// DynamicDispatch.swift
// DynamicDispatch
//
// Created by YEONGJUNG KIM on 2021/08/25.
//
import Foundation
/// `DynamicDispatch`의 임의함수를 호출할 delegate
///
/// - Note: delegate 함수 리턴값은 현재 Void, NSNumber(Bool표현을 위해)만 지원한다.
///
/// ```
/// enum SampleMessage: DynamicDispatch {
/// static var prefix: String = "MyMessage"
///
/// case say_hello
/// case goodbye
/// }
///
/// class MyDispatcher: DynamicDispatchDelegate {
/// @objc func MyMessage_say_hello(_ body: Any) -> NSNumber { return NSNumber(value: true) }
/// @objc func MyMessage_goodbye(_ body: Any) {}
///
/// func test() {
/// SampleMessage(rawValue: "goodbye")?.run(on: self)
/// }
/// }
/// ```
///
public protocol DynamicDispatchDelegate: NSObjectProtocol {
// `func {prefix}_{rawValue}:`(_ body: Any)
// `func `{prefix}_{rawValue}:`(_ body: Any) -> NSNumber
}
/// 스크립트메시지에 해당하는 함수를 자동으로 호출하기 위한 로직
/// - note: `{prefix}_{rawValue}:` 형식의 셀렉터를 생성하고, 해당 셀렉터를 `DynamicDispatchDelegate`로 호출한다.
public protocol DynamicDispatch: RawRepresentable where RawValue: StringProtocol {
/// DynamicDispatch prefix
static var prefix: String { get }
/// 각 case마다 prefix을 지정할 수 있다. nil인 경우, `DynamicDispatch.prefix`이 사용된다.
var prefix: String? { get }
}
public extension DynamicDispatch {
/// 기본 prefix
static var prefix: String { "dynamicDispatch" }
/// 기본 case prefix
var prefix: String? { nil }
/// prefix과 rawValue을 이용해 셀렉터를 생성해 호출한다.
/// - Note: 셀렉터의 리턴값 확인이 iOS 버전, 기기/시뮬에 따라 다를 수 있다. 리턴 값 사용 시 주의가 필요.
/// - Parameters:
/// - delegate: 함수 호출할 객체
/// - body: 함수 호출 시 전달될 파라미터
/// - Returns: 해당 셀렉터 호출결과, 셀렉터 호출 불가 시 false
@discardableResult
func run(on delegate: DynamicDispatchDelegate, body: Any) -> Bool {
let fixedMessage = self.rawValue.replacingOccurrences(of: "-", with: "_").lowercased()
let prefix = prefix ?? Self.prefix
let selector = Selector("\(prefix)_\(fixedMessage):")
print("Selector: `\(selector)`")
if delegate.responds(to: selector) {
if let result = delegate.perform(selector, with: body) {
// NOTE: void를 리턴하는 셀렉터라도 시뮬/기기에 따라 `if let`동작에 차이가 있다.
// 기기14.5: if let 통과
// 시뮬: if let 미통과
// 따라서 if let 후에도 현재 제한적으로 지원하는 NSNumber 리턴타입변환까지 확인한다.
return (result.takeUnretainedValue() as? NSNumber)?.boolValue ?? true
} else {
return true
}
} else {
print("No message delegate function: \(selector)")
return false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment