๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐Ÿ“ฑ iOS ์„ค์ •

์ด ๋ฌธ์„œ๋Š” ๋„ค์ดํ‹ฐ๋ธŒ iOS ํ”„๋กœ์ ํŠธ(Swift ๊ธฐ๋ฐ˜)์— 'Test Solution SDK'๋ฅผ ํ†ตํ•ฉํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ธ ์„ค์ • ๋ฐฉ๋ฒ•์„ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค.

'์‹œ์ž‘ํ•˜๊ธฐ' ์„น์…˜์˜ ์„ค์น˜ ๊ฐ€์ด๋“œ๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์— Flutter Module์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.


FlutterEngine ์œ ์ง€๊ด€๋ฆฌ ํด๋ž˜์Šค ์ƒ์„ฑโ€‹

์•ฑ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋™์•ˆ ์œ ์ง€ํ•  ๋ณ„๋„์˜ ๊ด€๋ฆฌ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

FlutterEngineManager.swift
import Foundation
import Flutter
import FlutterPluginRegistrant

class FlutterEngineManager: NSObject, FlutterAppLifeCycleProvider {
static let shared = FlutterEngineManager()

let flutterEngine: FlutterEngine

private let lifecycleDelegate = FlutterPluginAppLifeCycleDelegate()

private override init() {
self.flutterEngine = FlutterEngine(name: "MSCFlutterEngine")
super.init()

flutterEngine.run()
GeneratedPluginRegistrant.register(with: self.flutterEngine)
}

func add(_ delegate: any FlutterApplicationLifeCycleDelegate) {
lifecycleDelegate.add(delegate)
}
}

AppDelegate.swift ํŒŒ์ผ์€ iOS ์•ฑ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  Flutter Engine์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

AppDelegate.swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let _ = FlutterEngineManager.shared
return true
}

// ...
}

ViewController.swift ํŒŒ์ผ์— ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฒ„ํŠผ๊ณผ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

ViewController.swift
import UIKit
import Flutter

class ViewController: UIViewController {

private let CHANNEL: String = "com.mscbrain.sdk.test_solution_sdk/channel"

override func viewDidLoad() {
super.viewDidLoad()

let button = UIButton(type:UIButton.ButtonType.custom)
button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside)
button.setTitle("ํ…Œ์ŠคํŠธ ์‹œ์ž‘", for: UIControl.State.normal)
button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)
button.backgroundColor = UIColor.orange
self.view.addSubview(button)
}

@objc func showFlutter() {
let flutterEngine = FlutterEngineManager.shared.flutterEngine
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
flutterViewController.modalPresentationStyle = .fullScreen

let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: flutterViewController.binaryMessenger)
channel.setMethodCallHandler({(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in

switch call.method {
case "onResult":
guard let resultMap = call.arguments as? [String: Any],
let status = resultMap["status"] as? String else {
return
}

print("onResult received: \(resultMap)")

if status == "COMPLETED" {
// ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„ ์ฒ˜๋ฆฌ ๋กœ์ง
} else if status == "ERROR" {
// ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋กœ์ง
}
break

case "onInfo":
guard let versionMap = call.arguments as? [String: Any] else { return }
print("TestSolutionSDK_VERSION: \(versionMap)") // ๋ฒ„์ „ ์ •๋ณด ์ถœ๋ ฅ
break

case "onLog":
guard let logMap = call.arguments as? [String: Any] else { return }
print("TestSolutionSDK_LOG: \(logMap)") // ๋””๋ฒ„๊ทธ ๋กœ๊ทธ ์ถœ๋ ฅ
break

default:
result(FlutterMethodNotImplemented)
}
})

let startData: [String: Any] = [
// ํ•„์ˆ˜ ๊ฐ’
"goodsId": "[GOODS_ID]",
"suid": "[SUID]",
"language": "[LANGUAGE]",
// ์„ ํƒ ๊ฐ’
"isDevelopment": true,
"enableHostLogging": true,
"themeColor": 0xFF7E52F3, // ์˜ˆ์‹œ: ๋ณด๋ผ์ƒ‰ (AARRGGBB)
"themeMode": "light",
"titleText": "๋‚˜์˜ ์„ฑ๊ฒฉ ์œ ํ˜• ์ฐพ๊ธฐ"
]

channel.invokeMethod("startTest", arguments: startData)

present(flutterViewController, animated: true, completion: nil)

}
}

์ฃผ์š” ํ๋ฆ„ ์š”์•ฝโ€‹

  1. ์•ฑ ์‹คํ–‰: AppDelegate์—์„œ FlutterEngine์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. SDK ํ™”๋ฉด์„ ์š”์ฒญํ•˜๊ธฐ ์ „์— ์ด๋ฏธ ์—”์ง„์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋˜์–ด ์ค€๋น„๋œ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด SDK ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๋Š” ์†๋„๊ฐ€ ๋งค์šฐ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค. ๐Ÿš€
  2. ํ†ต์‹  ์ฑ„๋„ ์„ค์ •: ๋„ค์ดํ‹ฐ๋ธŒ์™€ SDK ๊ฐ„์˜ ํ†ต์‹  ์ฑ„๋„์ธ FlutterMethodChannel์„ ์„ค์ •ํ•˜์—ฌ SDK๋กœ๋ถ€ํ„ฐ onResult, onLog, onInfo ๋ฅผ ์ˆ˜์‹ ํ•  ์ค€๋น„๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.
  3. ํ…Œ์ŠคํŠธ ์‹œ์ž‘: ViewController์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, channel.invokeMethod("startTest", arguments: startData) ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด "startTest"๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ startData๋ฅผ SDK๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  4. ํ™”๋ฉด ํ‘œ์‹œ: FlutterViewController๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
  5. ๊ฒฐ๊ณผ ์ˆ˜์‹ : SDK์—์„œ onResult, onInfo, onLog๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ด ์‹ ํ˜ธ๋ฅผ ๋ฐ›์•„ ๋„ค์ดํ‹ฐ๋ธŒ์—์„œ ํ›„์† ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.