iOS 넷퍼넬 대기실 미사용
이 문서는 NetFUNNEL iOS 에이전트에서 넷퍼넬 대기실을 사용하지 않고, 자체 커스텀 대기실을 적용하기 위한 최소한의 예제를 제공합니다.
1. nfContinue
nfContinue
nfContinue는 넷퍼넬 대기실이 아닌 자체 커스텀 대기실로 구현할 경우 사용하는 함수입니다.
| parameter | type | 설명 | 예시 |
|---|---|---|---|
| statusCode | Int | 대기 상태에 대한 응답 코드 | 201 |
| message | String | 대기 상태에 대한 메시지 | Continue |
| aheadWait | Int | 내 앞 대기자 수 | {N} |
| behindWait | Int | 내 뒤 대기자 수 | {N} |
| waitTime | String | 예상 대기 시간 | {HH:mm:ss} |
| progressRate | Int | 진행률 (%) | {0~100} |
danger
자체 커스텀 대기실 사용 시, 기본 대기 외 기능 사용에 제한이 있습니다.
따라서 넷퍼넬 대기실을 사용하는 useNetfunnelTemplate=true 설정을 권장합니다.
2. 자체 커스텀 대기실 적용
2.1 초기화 함수 설정
기본 WebView 대기실 대신 자체 구현한 대기실 UI를 사용하려면, useNetfunnelTemplate: false로 설정합니다.
이 설정 시 nfContinue 콜백을 통해 실시간 대기 정보를 직접 받아 커스텀 다이얼로그에 반영해야 합니다.
- SwiftUI
- UIKit
SampleApp.swift
import SwiftUI
import Netfunnel_iOS
@main
struct SampleApp: App {
init() {
AppConfig.shared.useNetfunnelTemplate = false
Netfunnel.shared.initialize(
clientId: "{CLIENT_ID}",
delegate: NetfunnelHandler.shared,
networkTimeout: 3000,
retryCount: 0,
printLog: true,
errorBypass: false,
useNetfunnelTemplate: false
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
AppDelegate.swift
import UIKit
import Netfunnel_iOS
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Netfunnel.shared.initialize(
clientId: "{CLIENT_ID}",
delegate: NetfunnelHandler.shared,
networkTimeout: 3000,
retryCount: 0,
printLog: true,
errorBypass: false,
useNetfunnelTemplate: false
)
return true
}
}
2.2 자체 커스텀 대기실 생성
자체 커스텀 대기실은 예상 대기 시간, 진행률, 앞/뒤 대기자 수, 닫기 버튼 등의 요소를 포함해 SwiftUI로 구성할 수 있습니다.
해당 UI는 커스텀 대기실로 구현하고, nfContinue 델리게이트를 통해 상태를 갱신합니다.
- SwiftUI
- UIKit
CustomWaitingView.swift
import SwiftUI
struct CustomWaitingView: View {
let aheadWait: Int
let behindWait: Int
let waitTime: String
let progressRate: Int
let onCancel: () -> Void
var body: some View {
VStack(spacing: 24) {
Text("Waiting in progress")
.font(.title2)
.fontWeight(.semibold)
VStack(spacing: 12) {
InfoRow(title: "People ahead of me", value: "\(aheadWait)")
InfoRow(title: "People behind me", value: "\(behindWait)")
InfoRow(title: "Estimated wait time", value: waitTime)
}
VStack(spacing: 8) {
ProgressView(value: Float(progressRate), total: 100)
.progressViewStyle(LinearProgressViewStyle(tint: .blue))
.frame(height: 10)
.clipShape(Capsule())
Text("Progress: \(progressRate)%")
.font(.subheadline)
.foregroundColor(.gray)
}
Button(action: onCancel) {
Text("Cancel Waiting")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.red)
.cornerRadius(12)
}
}
.padding()
.background(Color.white)
.cornerRadius(20)
.shadow(radius: 10)
.padding(.horizontal, 24)
}
}
private struct InfoRow: View {
let title: String
let value: String
var body: some View {
HStack {
Text(title)
.font(.subheadline)
.foregroundColor(.gray)
Spacer()
Text(value)
.font(.body)
.fontWeight(.medium)
}
}
}
CustomWaitingViewController.swift
import UIKit
import Netfunnel_iOS
class CustomWaitingViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var aheadWaitLabel: UILabel!
@IBOutlet weak var behindWaitLabel: UILabel!
@IBOutlet weak var waitTimeLabel: UILabel!
@IBOutlet weak var progressView: UIProgressView!
@IBOutlet weak var progressLabel: UILabel!
@IBOutlet weak var cancelButton: UIButton!
var projectKey: String?
var segmentKey: String?
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
titleLabel.text = "Waiting in progress"
titleLabel.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
cancelButton.layer.cornerRadius = 12
cancelButton.backgroundColor = UIColor.systemRed
cancelButton.setTitle("Cancel Waiting", for: .normal)
cancelButton.setTitleColor(.white, for: .normal)
progressView.progressTintColor = UIColor.systemBlue
progressView.trackTintColor = UIColor.systemGray4
}
func updateWaitingInfo(aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
DispatchQueue.main.async { [weak self] in
self?.aheadWaitLabel.text = "People ahead of me: \(aheadWait)"
self?.behindWaitLabel.text = "People behind me: \(behindWait)"
self?.waitTimeLabel.text = "Estimated wait time: \(waitTime)"
self?.progressView.progress = Float(progressRate) / 100.0
self?.progressLabel.text = "Progress: \(progressRate)%";
}
}
@IBAction func cancelButtonTapped(_ sender: UIButton) {
if let projectKey = projectKey, let segmentKey = segmentKey {
Netfunnel.shared.nfStop(projectKey: projectKey, segmentKey: segmentKey)
}
dismiss(animated: true)
}
}
2.3 NetFUNNEL 델리게이트 핸들러
nfContinue 콜백을 처리하는 델리게이트 클래스를 구성합니다.
- SwiftUI
- UIKit
NetfunnelHandler.swift
import Foundation
import Netfunnel_iOS
class NetfunnelHandler: NSObject, NetfunnelDelegate {
static let shared = NetfunnelHandler()
var onContinue: ((String, String, Int, String, Int, Int, String, Int) -> Void)?
var onSuccess: ((String, String, Int, String) -> Void)?
var onError: ((String, String, Int, String) -> Void)?
var onNetworkError: ((String, String, Int, String) -> Void)?
private override init() {
super.init()
}
func nfContinue(projectKey: String, segmentKey: String, statusCode: Int, message: String, aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
onContinue?(projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate)
}
func nfSuccess(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
onSuccess?(projectKey, segmentKey, statusCode, message)
}
func nfError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
onError?(projectKey, segmentKey, statusCode, message)
}
func nfNetworkError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
onNetworkError?(projectKey, segmentKey, statusCode, message)
}
}
NetfunnelHandler.swift
import Foundation
import Netfunnel_iOS
protocol NetfunnelHandlerDelegate: AnyObject {
func didReceiveContinue(aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int)
func didReceiveSuccess()
func didReceiveError(message: String)
func didReceiveNetworkError(statusCode: Int, message: String)
}
class NetfunnelHandler: NSObject, NetfunnelDelegate {
static let shared = NetfunnelHandler()
weak var delegate: NetfunnelHandlerDelegate?
private override init() {
super.init()
}
func nfContinue(projectKey: String, segmentKey: String, statusCode: Int, message: String, aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didReceiveContinue(aheadWait: aheadWait, behindWait: behindWait, waitTime: waitTime, progressRate: progressRate)
}
}
func nfSuccess(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didReceiveSuccess()
}
}
func nfError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didReceiveError(message: message)
}
}
func nfNetworkError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
DispatchQueue.main.async { [weak self] in
self?.delegate?.didReceiveNetworkError(statusCode: statusCode, message: message)
}
}
}
2.4 nfContinue 사용
nfContinue 콜백에서 대기실을 띄우고 실시간 대기 정보를 업데이트합니다.
warning
중요: 대기 취소 시 반드시 해당 함수 호출
대기를 취소할 때는 반드시 다음 함수 중 하나를 호출해야 합니다:
기본 제어:
nfStop()구간 제어:
nfStopSection()
이 함수들을 호출하지 않으면:
서버에서 사용자의 대기 상태가 정리되지 않음
정확한 대기열 관리에 영향을 줄 수 있음
- SwiftUI
- UIKit
ContentView.swift
import SwiftUI
import Netfunnel_iOS
struct ContentView: View {
@State private var showWaitingRoom = false
@State private var ahead = 0
@State private var behind = 0
@State private var progress = 0
@State private var waitTime = ""
@State private var showCompleted = false
let projectKey = "{PROJECT_KEY}"
let segmentKey = "{SEGMENT_KEY}"
var body: some View {
if showCompleted {
Text("Entry Complete Screen")
.font(.title)
.padding()
} else if showWaitingRoom {
CustomWaitingView(
aheadWait: ahead,
behindWait: behind,
waitTime: waitTime,
progressRate: progress
) {
// Basic Control - Cancel Waiting
Netfunnel.shared.nfStop(projectKey: projectKey, segmentKey: segmentKey)
showWaitingRoom = false
}
} else {
SplashView(
projectKey: projectKey,
segmentKey: segmentKey,
showWaitingRoom: $showWaitingRoom,
ahead: $ahead,
behind: $behind,
waitTime: $waitTime,
progress: $progress,
showTabs: $showCompleted
)
}
}
}
struct SplashView: View {
let projectKey: String
let segmentKey: String
@Binding var showWaitingRoom: Bool
@Binding var ahead: Int
@Binding var behind: Int
@Binding var waitTime: String
@Binding var progress: Int
@Binding var showTabs: Bool
var body: some View {
VStack {
Spacer()
Text("Splash").font(.title3)
Spacer()
}
.onAppear {
bindNetfunnelCallbacks()
Netfunnel.shared.nfStart(projectKey: projectKey, segmentKey: segmentKey)
}
}
private func bindNetfunnelCallbacks() {
NetfunnelHandler.shared.onContinue = { _, _, _, _, aheadWait, behindWait, time, prog in
DispatchQueue.main.async {
if !AppConfig.shared.useNetfunnelTemplate {
ahead = aheadWait
behind = behindWait
waitTime = time
progress = prog
showWaitingRoom = true
}
}
}
NetfunnelHandler.shared.onSuccess = { _, _, _, _ in
DispatchQueue.main.async {
showWaitingRoom = false
showTabs = true
}
}
NetfunnelHandler.shared.onError = { _, _, _, _ in
DispatchQueue.main.async {
showWaitingRoom = false
showTabs = true
}
}
NetfunnelHandler.shared.onNetworkError = { _, _, _, _ in
DispatchQueue.main.async {
showWaitingRoom = false
showTabs = true
}
}
}
}
ViewController.swift
import UIKit
import Netfunnel_iOS
class ViewController: UIViewController {
let projectKey = "{PROJECT_KEY}"
let segmentKey = "{SEGMENT_KEY}"
var customWaitingVC: CustomWaitingViewController?
override func viewDidLoad() {
super.viewDidLoad()
setupNetfunnelHandler()
}
private func setupNetfunnelHandler() {
NetfunnelHandler.shared.delegate = self
}
@IBAction func startNetfunnelButtonTapped(_ sender: UIButton) {
Netfunnel.shared.nfStart(projectKey: projectKey, segmentKey: segmentKey)
}
private func showCustomWaitingRoom() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let waitingVC = storyboard.instantiateViewController(withIdentifier: "CustomWaitingViewController") as? CustomWaitingViewController {
waitingVC.projectKey = projectKey
waitingVC.segmentKey = segmentKey
waitingVC.modalPresentationStyle = .fullScreen
present(waitingVC, animated: true)
customWaitingVC = waitingVC
}
}
private func hideCustomWaitingRoom() {
customWaitingVC?.dismiss(animated: true) { [weak self] in
self?.customWaitingVC = nil
}
}
}
// MARK: - NetfunnelHandlerDelegate
extension ViewController: NetfunnelHandlerDelegate {
func didReceiveContinue(aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
if customWaitingVC == nil {
showCustomWaitingRoom()
}
customWaitingVC?.updateWaitingInfo(aheadWait: aheadWait, behindWait: behindWait, waitTime: waitTime, progressRate: progressRate)
}
func didReceiveSuccess() {
hideCustomWaitingRoom()
// Move to the next screen after success
performSegue(withIdentifier: "showMainTabBar", sender: nil)
}
func didReceiveError(message: String) {
hideCustomWaitingRoom()
showAlert(title: "Error", message: message)
}
func didReceiveNetworkError(statusCode: Int, message: String) {
hideCustomWaitingRoom()
showAlert(title: "Network Error", message: message)
}
private func showAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}