iOS 퀵 가이드
NetFUNNEL iOS 에이전트를 처음 도입하는 개발자가, 최단 시간 안에 시작할 수 있도록 필수적인 사항들을 간결하게 안내합니다.
1. 디렉터리 구조
이후 단계에서 구현할 NetFUNNEL 적용 샘플의 기본 디렉터리 구조입니다. 각 파일은 에이전트 초기화, 시작/종료 콜백 처리, UI 흐름 제어를 담당합니다.
Project Root/
├── netfunnel/
│ └── NetfunnelHandler.swift // Start and end callback handling (nfContinue, nfSuccess, nfComplete, etc.)
│
├── views/
│ ├── ContentView.swift // nfStart call - entry request (Splash role)
│ ├── EventView.swift // nfStop call - entry completion handling
│ └── CustomWaitingView.swift // Custom waiting room view definition
│
└── SampleApp.swift // @main struct - agent initialization call
2. 초기화
NetFUNNEL iOS 에이전트는 앱 전역에서 안정적인 동작을 위해, 앱의 진입 시점에서 초기화되어야 합니다.
SwiftUI:
@main구조체의init()에서 초기화를 수행합니다.UIKit:
AppDelegate의application(_:didFinishLaunchingWithOptions:)에서 초기화를 수행합니다.
- Swift
- Objective-C
SampleApp.swift
import SwiftUI
import Netfunnel_iOS
@main
struct SampleApp: App {
init() {
let agent = Netfunnel.shared
agent.initialize(
clientId: "{CLIENT_ID}",
delegate: NetfunnelHandler.shared // Set the delegate to a specified delegate object or 'self'.
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
AppDelegate.m
#import "AppDelegate.h"
#import <Netfunnel_iOS/Netfunnel_iOS.h>
#import "NetfunnelHandler.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Netfunnel *agent = [Netfunnel shared];
[agent initializeWithClientId:@"{CLIENT_ID}"
delegate:[NetfunnelHandler sharedInstance]];
return YES;
}
@end
3. 델리게이트
시작 델리게이트는 대기실 시작 이후, nfSuccess, nfContinue, nfError 등의 델리게이트가 호출됩니다.
종료 델리게이트는 대기실 종료 이후, 서버로 진입 키 반납이 완료되면 nfComplete 델리게이트가 호출됩니다.
- Swift
- Objective-C
NetfunnelHandler.swift
import Foundation
import Netfunnel_iOS
class NetfunnelHandler: NSObject, NetfunnelDelegate {
static let shared = NetfunnelHandler()
// MARK: - Required callback closures (Must be defined)
var onSuccess: ((String, String, Int, String) -> Void)?
var onError: ((String, String, Int, String) -> Void)?
var onNetworkError: ((String, String, Int, String) -> Void)?
// MARK: - Optional callback closures (Define as needed)
var onContinue: ((String, String, Int, String, Int, Int, String, Int) -> Void)?
var onBlock: ((String, String, Int, String) -> Void)?
var onClose: ((String, String, Int, String) -> Void)?
var onComplete: ((String, String, Int, String) -> Void)?
private override init() {}
// MARK: - NetfunnelDelegate Methods
func nfSuccess(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfSuccess \(statusCode) \(message)")
onSuccess?(projectKey, segmentKey, statusCode, message)
}
func nfError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfError \(statusCode) \(message)")
onError?(projectKey, segmentKey, statusCode, message)
}
func nfNetworkError(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfNetworkError \(statusCode) \(message)")
onNetworkError?(projectKey, segmentKey, statusCode, message)
}
func nfContinue(projectKey: String, segmentKey: String, statusCode: Int, message: String,
aheadWait: Int, behindWait: Int, waitTime: String, progressRate: Int) {
NSLog("nfContinue \(statusCode) \(message)")
onContinue?(projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate)
}
func nfBlock(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfBlock \(statusCode) \(message)")
onBlock?(projectKey, segmentKey, statusCode, message)
}
func nfClose(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfClose \(statusCode) \(message)")
onClose?(projectKey, segmentKey, statusCode, message)
}
func nfComplete(projectKey: String, segmentKey: String, statusCode: Int, message: String) {
NSLog("nfComplete \(statusCode) \(message)")
onComplete?(projectKey, segmentKey, statusCode, message)
}
}
NetfunnelHandler.h
#import <Foundation/Foundation.h>
#import <Netfunnel_iOS/NetfunnelDelegate.h>
NS_ASSUME_NONNULL_BEGIN
@interface NetfunnelHandler : NSObject <NetfunnelDelegate>
+ (instancetype)sharedInstance;
// Required callbacks
@property (nonatomic, copy, nullable) void (^onSuccess)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
@property (nonatomic, copy, nullable) void (^onError)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
@property (nonatomic, copy, nullable) void (^onNetworkError)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
// Optional callbacks
@property (nonatomic, copy, nullable) void (^onContinue)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message, NSInteger aheadWait, NSInteger behindWait, NSString *waitTime, NSInteger progressRate);
@property (nonatomic, copy, nullable) void (^onBlock)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
@property (nonatomic, copy, nullable) void (^onClose)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
@property (nonatomic, copy, nullable) void (^onComplete)(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message);
@end
NS_ASSUME_NONNULL_END
NetfunnelHandler.m
#import "NetfunnelHandler.h"
@implementation NetfunnelHandler
+ (instancetype)sharedInstance {
static NetfunnelHandler *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[NetfunnelHandler alloc] init];
});
return sharedInstance;
}
#pragma mark - NetfunnelDelegate Methods
- (void)nfSuccessWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfSuccess %ld %@", (long)statusCode, message);
if (self.onSuccess) {
self.onSuccess(projectKey, segmentKey, statusCode, message);
}
}
- (void)nfErrorWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfError %ld %@", (long)statusCode, message);
if (self.onError) {
self.onError(projectKey, segmentKey, statusCode, message);
}
}
- (void)nfNetworkErrorWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfNetworkError %ld %@", (long)statusCode, message);
if (self.onNetworkError) {
self.onNetworkError(projectKey, segmentKey, statusCode, message);
}
}
- (void)nfContinueWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message
aheadWait:(NSInteger)aheadWait
behindWait:(NSInteger)behindWait
waitTime:(NSString *)waitTime
progressRate:(NSInteger)progressRate {
NSLog(@"nfContinue %ld %@", (long)statusCode, message);
if (self.onContinue) {
self.onContinue(projectKey, segmentKey, statusCode, message, aheadWait, behindWait, waitTime, progressRate);
}
}
- (void)nfBlockWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfBlock %ld %@", (long)statusCode, message);
if (self.onBlock) {
self.onBlock(projectKey, segmentKey, statusCode, message);
}
}
- (void)nfCloseWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfClose %ld %@", (long)statusCode, message);
if (self.onClose) {
self.onClose(projectKey, segmentKey, statusCode, message);
}
}
- (void)nfCompleteWithProjectKey:(NSString *)projectKey
segmentKey:(NSString *)segmentKey
statusCode:(NSInteger)statusCode
message:(NSString *)message {
NSLog(@"nfComplete %ld %@", (long)statusCode, message);
if (self.onComplete) {
self.onComplete(projectKey, segmentKey, statusCode, message);
}
}
@end
4. 시작 함수
nfStart(기본 제어), nfStartSection(구간 제어)은 대기 상태를 확인하고 필요 시 대기실을 띄우기 위한 함수입니다.
- Swift
- Objective-C
ContentView.swift
import SwiftUI
import Netfunnel_iOS
struct ContentView: View {
@State private var showEventView = false
private let projectKey = "{CLIENT_ID}"
private let segmentKey = "{SEGMENT_KEY}"
var body: some View {
if showEventView {
AnyView(EventView(projectKey: projectKey, segmentKey: segmentKey))
} else {
AnyView(
VStack {
Spacer()
Text("Splash")
.font(.largeTitle)
.padding()
Spacer()
}
.onAppear {
// Callback configuration
NetfunnelHandler.shared.onSuccess = { (projectKey, segmentKey, statusCode, message) in
showEventView = true
}
NetfunnelHandler.shared.onError = { (projectKey, segmentKey, statusCode, message) in
showEventView = true
}
NetfunnelHandler.shared.onNetworkError = { (projectKey, segmentKey, statusCode, message) in
showEventView = true
}
// Waiting room entry (start function call)
Netfunnel.shared.nfStart(projectKey: projectKey, segmentKey: segmentKey)
}
)
}
}
}
ContentViewController.h
#import <UIKit/UIKit.h>
@interface ContentViewController : UIViewController
@end
ContentViewController.m
#import "ContentViewController.h"
#import <Netfunnel_iOS/Netfunnel.h>
#import "NetfunnelHandler.h"
#import "EventViewController.h"
@interface ContentViewController ()
@property (nonatomic, assign) BOOL didEnterEvent;
@property (nonatomic, strong) NSString *projectKey;
@property (nonatomic, strong) NSString *segmentKey;
@end
@implementation ContentViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.projectKey = @"{CLIENT_ID}";
self.segmentKey = @"{SEGMENT_KEY}";
self.view.backgroundColor = [UIColor whiteColor];
UILabel *label = [[UILabel alloc] initWithFrame:self.view.bounds];
label.text = @"Splash";
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:32 weight:UIFontWeightBold];
[self.view addSubview:label];
__weak typeof(self) weakSelf = self;
NetfunnelHandler.sharedInstance.onSuccess = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
[weakSelf navigateToEvent];
};
NetfunnelHandler.sharedInstance.onError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
[weakSelf navigateToEvent];
};
NetfunnelHandler.sharedInstance.onNetworkError = ^(NSString *projectKey, NSString *segmentKey, NSInteger statusCode, NSString *message) {
[weakSelf navigateToEvent];
};
// Start request
[[Netfunnel shared] nfStartWithProjectKey:self.projectKey segmentKey:self.segmentKey];
}
- (void)navigateToEvent {
if (self.didEnterEvent) return;
self.didEnterEvent = YES;
dispatch_async(dispatch_get_main_queue(), ^{
EventViewController *eventVC = [[EventViewController alloc] initWithProjectKey:self.projectKey segmentKey:self.segmentKey];
eventVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:eventVC animated:YES completion:nil];
});
}
@end
5. 종료 함수
nfStop(기본 제어), nfStopSection(구간 제어)은 대기 완료 후 서버에 진입 키를 반납합니다.
진입 키를 서버에 반납하여 다음 사용자가 진입할 수 있도록 하며, 일반적으로 서비스 진입 직후 실행합니다.
- Swift
- Objective-C
Event.swift
import SwiftUI
import Netfunnel_iOS
struct EventView: View {
let projectKey: String
let segmentKey: String
var body: some View {
VStack {
Spacer()
Text("Event")
.font(.largeTitle)
.padding()
Spacer()
}
.onAppear {
// End waiting room when entering event page (end function call)
Netfunnel.shared.nfStop(projectKey: projectKey, segmentKey: segmentKey)
}
}
}
EventViewController.h
#import <UIKit/UIKit.h>
@interface EventViewController : UIViewController
- (instancetype)initWithProjectKey:(NSString *)projectKey segmentKey:(NSString *)segmentKey;
@end
EventViewController.m
#import "EventViewController.h"
#import <Netfunnel_iOS/Netfunnel.h>
@interface EventViewController ()
@property (nonatomic, strong) NSString *projectKey;
@property (nonatomic, strong) NSString *segmentKey;
@end
@implementation EventViewController
- (instancetype)initWithProjectKey:(NSString *)projectKey segmentKey:(NSString *)segmentKey {
self = [super init];
if (self) {
_projectKey = projectKey;
_segmentKey = segmentKey;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
UILabel *label = [[UILabel alloc] initWithFrame:self.view.bounds];
label.text = @"Event";
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:32 weight:UIFontWeightBold];
[self.view addSubview:label];
// End waiting room when entering event page (end function call)
[[Netfunnel shared] nfStopWithProjectKey:self.projectKey segmentKey:self.segmentKey];
}
@end