diff --git a/.gitignore b/.gitignore
index d5796ae..29f2a42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,6 @@ xcuserdata/
/.idea
build
QueueITLibSwift.xcframework.zip
-.DS_Store
\ No newline at end of file
+.DS_Store
+.index-build
+.build
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Package.swift b/Package.swift
index db710e0..84d0275 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
@@ -6,18 +6,18 @@ import PackageDescription
let package = Package(
name: "QueueITLibrary",
platforms: [
- .iOS(.v11)
+ .iOS(.v15),
],
products: [
.library(
name: "QueueITLibrary",
- targets: ["QueueITLibrary"]),
+ targets: ["QueueITLibrary"]
+ ),
],
targets: [
.target(
name: "QueueITLibrary",
- path: "QueueItLib/",
- publicHeadersPath: ""
- )
+ path: "Sources/QueueITLib"
+ ),
]
)
diff --git a/QueueITLib/IOSUtils.h b/QueueITLib/IOSUtils.h
deleted file mode 100644
index 758480b..0000000
--- a/QueueITLib/IOSUtils.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#import
-#import "QueueConsts.h"
-
-@interface IOSUtils : NSObject
-
-+(NSString*)getUserId;
-+(void)getUserAgent:(void (^)(NSString*))completionHandler;
-+(NSString*)getLibraryVersion;
-+(NSString*)getSdkVersion;
-+(NSString*)convertTtlMinutesToSecondsString:(int)ttlMinutes;
-
-@end
diff --git a/QueueITLib/IOSUtils.m b/QueueITLib/IOSUtils.m
deleted file mode 100644
index b2266cf..0000000
--- a/QueueITLib/IOSUtils.m
+++ /dev/null
@@ -1,55 +0,0 @@
-#import
-#import "IOSUtils.h"
-
-@implementation IOSUtils
-
-WKWebView* webView;
-
-+(NSString*)getUserId{
- UIDevice* device = [[UIDevice alloc]init];
- NSUUID* deviceid = [device identifierForVendor];
- NSString* uuid = [deviceid UUIDString];
- return uuid;
-}
-
-+(void)getUserAgent:(void (^)(NSString*))completionHandler{
- dispatch_async(dispatch_get_main_queue(), ^{
- WKWebView* view = [[WKWebView alloc] initWithFrame:CGRectZero];
- [view evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable userAgent, NSError * _Nullable error) {
- if (error == nil) {
- completionHandler(userAgent);
- }
- else {
- completionHandler(@"");
- }
- webView = nil;
- }];
- webView = view;
- });
-}
-
-+(NSString*)getLibraryVersion{
- NSDictionary *infoDictionary = [[NSBundle mainBundle]infoDictionary];
-
- NSString *libName = infoDictionary[(NSString *)kCFBundleNameKey];
- NSString * major = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- NSString *minor = infoDictionary[(NSString*)kCFBundleVersionKey];
- NSString* libversion = [NSString stringWithFormat:@"%@-%@.%@", libName, major, minor];
-
- return libversion;
-}
-
-+(NSString*)getSdkVersion{
- return SDKVersion;
-}
-
-+(NSString*)convertTtlMinutesToSecondsString:(int)ttlMinutes
-{
- long currentTime = (long)(NSTimeInterval)([[NSDate date] timeIntervalSince1970]);
- int secondsToAdd = ttlMinutes * 60.0;
- long timeStamp = currentTime + secondsToAdd;
- NSString* urlTtlString = [NSString stringWithFormat:@"%li", timeStamp];
- return urlTtlString;
-}
-
-@end
diff --git a/QueueITLib/PrivacyInfo.xcprivacy b/QueueITLib/PrivacyInfo.xcprivacy
deleted file mode 100644
index 5139ebc..0000000
--- a/QueueITLib/PrivacyInfo.xcprivacy
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
- NSPrivacyCollectedDataTypes
-
-
- NSPrivacyCollectedDataType
- NSPrivacyCollectedDataTypeOtherDataTypes
- NSPrivacyCollectedDataTypeLinked
-
- NSPrivacyCollectedDataTypeTracking
-
- NSPrivacyCollectedDataTypePurposes
-
- NSPrivacyCollectedDataTypePurposeAppFunctionality
-
-
-
- NSPrivacyCollectedDataType
- NSPrivacyCollectedDataTypeDeviceID
- NSPrivacyCollectedDataTypeLinked
-
- NSPrivacyCollectedDataTypeTracking
-
- NSPrivacyCollectedDataTypePurposes
-
- NSPrivacyCollectedDataTypePurposeAppFunctionality
-
-
-
- NSPrivacyTracking
-
-
-
diff --git a/QueueITLib/QueueConsts.h b/QueueITLib/QueueConsts.h
deleted file mode 100644
index eeee409..0000000
--- a/QueueITLib/QueueConsts.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef QueueConsts_h
-#define QueueConsts_h
-
-#define QueueCloseUrl @"queueit://close"
-#define QueueRestartSessionUrl @"queueit://restartSession"
-#define SDKVersion @"iOS-3.4.4";
-
-#endif
diff --git a/QueueITLib/QueueDisabledInfo.h b/QueueITLib/QueueDisabledInfo.h
deleted file mode 100644
index 428f707..0000000
--- a/QueueITLib/QueueDisabledInfo.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#import
-
-@interface QueueDisabledInfo : NSObject
-
-@property (nonatomic, strong) NSString* _Nullable queueitToken;
-
--(instancetype _Nonnull )initWithQueueitToken:(NSString* _Nullable) queueitToken;
-
-@end
diff --git a/QueueITLib/QueueDisabledInfo.m b/QueueITLib/QueueDisabledInfo.m
deleted file mode 100644
index f0c4935..0000000
--- a/QueueITLib/QueueDisabledInfo.m
+++ /dev/null
@@ -1,14 +0,0 @@
-#import "QueueDisabledInfo.h"
-
-@implementation QueueDisabledInfo
-
--(instancetype)initWithQueueitToken:(NSString *)queueitToken
-{
- if(self = [super init]) {
- self.queueitToken = queueitToken;
- }
-
- return self;
-}
-
-@end
diff --git a/QueueITLib/QueueITApiClient.h b/QueueITLib/QueueITApiClient.h
deleted file mode 100644
index 37448d2..0000000
--- a/QueueITLib/QueueITApiClient.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#import
-#import "QueueStatus.h"
-
-typedef void (^QueueServiceSuccess)(NSData *data);
-typedef void (^QueueServiceFailure)(NSError *error, NSString* errorMessage);
-
-@interface QueueITApiClient: NSObject
-
-+ (QueueITApiClient *)getInstance;
-+ (void) setTesting:(bool)enabled;
-
--(NSString*)enqueue:(NSString*)customerId
- eventOrAliasId:(NSString*)eventorAliasId
- userId:(NSString*)userId
- userAgent:(NSString*)userAgent
- sdkVersion:(NSString*)sdkVersion
- layoutName:(NSString*)layoutName
- language:(NSString*)language
- enqueueToken:(NSString*)enqueueToken
- enqueueKey:(NSString*)enqueueKey
- success:(void(^)(QueueStatus* queueStatus))success
- failure:(QueueServiceFailure)failure;
-
-@end
diff --git a/QueueITLib/QueueITApiClient.m b/QueueITLib/QueueITApiClient.m
deleted file mode 100644
index f40752e..0000000
--- a/QueueITLib/QueueITApiClient.m
+++ /dev/null
@@ -1,118 +0,0 @@
-#import "QueueITApiClient.h"
-#import "QueueITApiClient_NSURLConnection.h"
-
-static QueueITApiClient *SharedInstance;
-
-static NSString * const API_ROOT = @"https://%@.queue-it.net/api/mobileapp/queue";
-static NSString * const TESTING_API_ROOT = @"https://%@.test.queue-it.net/api/mobileapp/queue";
-static bool testingIsEnabled = NO;
-
-@implementation QueueITApiClient
-
-+ (QueueITApiClient *)getInstance
-{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- SharedInstance = [[QueueITApiClient_NSURLConnection alloc] init];
- });
-
- return SharedInstance;
-}
-
-+ (void) setTesting:(bool)enabled
-{
- testingIsEnabled = enabled;
-}
-
--(NSString*)enqueue:(NSString *)customerId
- eventOrAliasId:(NSString *)eventorAliasId
- userId:(NSString *)userId
- userAgent:(NSString *)userAgent
- sdkVersion:(NSString*)sdkVersion
- layoutName:(NSString*)layoutName
- language:(NSString*)language
- enqueueToken:(NSString*)enqueueToken
- enqueueKey:(NSString*)enqueueKey
- success:(void (^)(QueueStatus *))success
- failure:(QueueServiceFailure)failure
-{
- NSMutableDictionary* bodyDict = [[NSMutableDictionary alloc] init];
- [bodyDict setObject:userId forKey:@"userId"];
- [bodyDict setObject:userAgent forKey:@"userAgent"];
- [bodyDict setObject:sdkVersion forKey:@"sdkVersion"];
-
- if(layoutName){
- [bodyDict setObject:layoutName forKey:@"layoutName"];
- }
-
- if(language){
- [bodyDict setObject:language forKey:@"language"];
- }
-
- if(enqueueToken){
- [bodyDict setObject:enqueueToken forKey:@"enqueueToken"];
- }
-
- if(enqueueKey){
- [bodyDict setObject:enqueueKey forKey:@"enqueueKey"];
- }
-
- NSString* urlAsString;
- if(testingIsEnabled){
- urlAsString = [NSString stringWithFormat:TESTING_API_ROOT, customerId];
- }else{
- urlAsString = [NSString stringWithFormat:API_ROOT, customerId];
- }
- urlAsString = [urlAsString stringByAppendingString:[NSString stringWithFormat:@"/%@", customerId]];
- urlAsString = [urlAsString stringByAppendingString:[NSString stringWithFormat:@"/%@", eventorAliasId]];
- urlAsString = [urlAsString stringByAppendingString:[NSString stringWithFormat:@"/enqueue"]];
-
- return [self submitPOSTPath:urlAsString body:bodyDict
- success:^(NSData *data)
- {
- NSError *error = nil;
- NSDictionary *userDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
- if (userDict && [userDict isKindOfClass:[NSDictionary class]])
- {
- QueueStatus* queueStatus = [[QueueStatus alloc] initWithDictionary:userDict];
-
- if (success != NULL) {
- success(queueStatus);
- }
- } else if (success != NULL) {
- success(NULL);
- }
- }
- failure:^(NSError *error, NSString* errorMessage)
- {
- failure(error, errorMessage);
- }
- ];
-}
-
-- (NSString *)submitPOSTPath:(NSString *)path
- body:(NSDictionary *)bodyDict
- success:(QueueServiceSuccess)success
- failure:(QueueServiceFailure)failure
-{
- NSURL *url = [NSURL URLWithString:path];
- return [self submitRequestWithURL:url
- method:@"POST"
- body:bodyDict
- expectedStatus:200
- success:success
- failure:failure];
-}
-
-#pragma mark - Abstract methods
-- (NSString *)submitRequestWithURL:(NSURL *)URL
- method:(NSString *)httpMethod
- body:(NSDictionary *)bodyDict
- expectedStatus:(NSInteger)expectedStatus
- success:(QueueServiceSuccess)success
- failure:(QueueServiceFailure)failure
-{
- return nil;
-}
-
-@end
diff --git a/QueueITLib/QueueITApiClient_NSURLConnection.h b/QueueITLib/QueueITApiClient_NSURLConnection.h
deleted file mode 100644
index 01071bf..0000000
--- a/QueueITLib/QueueITApiClient_NSURLConnection.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#import
-#import "QueueITApiClient.h"
-
-@interface QueueITApiClient_NSURLConnection : QueueITApiClient
-
-@end
diff --git a/QueueITLib/QueueITApiClient_NSURLConnection.m b/QueueITLib/QueueITApiClient_NSURLConnection.m
deleted file mode 100644
index 21edb11..0000000
--- a/QueueITLib/QueueITApiClient_NSURLConnection.m
+++ /dev/null
@@ -1,46 +0,0 @@
-#import "QueueITApiClient_NSURLConnection.h"
-#import "QueueITApiClient_NSURLConnectionRequest.h"
-
-@interface QueueITApiClient_NSURLConnection()
-@end
-
-
-@implementation QueueITApiClient_NSURLConnection
-
-- (NSString *)submitRequestWithURL:(NSURL *)URL
- method:(NSString *)httpMethod
- body:(NSDictionary *)bodyDict
- expectedStatus:(NSInteger)expectedStatus
- success:(QueueServiceSuccess)success
- failure:(QueueServiceFailure)failure
-{
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
- [request setHTTPMethod:httpMethod];
-
- NSError *error;
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:bodyDict
- options:0
- error:&error];
- [request setHTTPBody: jsonData];
- [request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
- [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
-
- QueueITApiClient_NSURLConnectionRequest *connectionRequest;
- connectionRequest = [[QueueITApiClient_NSURLConnectionRequest alloc] initWithRequest:request
- expectedStatusCode:expectedStatus
- success:success
- failure:failure
- delegate:self];
-
- NSString *connectionID = [connectionRequest uniqueIdentifier];
-
- return connectionID;
-}
-
-#pragma mark - NSURLConnectionRequestDelegate
-
-- (void)requestDidComplete:(QueueITApiClient_NSURLConnectionRequest *)request
-{
-}
-
-@end
diff --git a/QueueITLib/QueueITApiClient_NSURLConnectionRequest.h b/QueueITLib/QueueITApiClient_NSURLConnectionRequest.h
deleted file mode 100644
index 6b5013d..0000000
--- a/QueueITLib/QueueITApiClient_NSURLConnectionRequest.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#import
-#import "QueueITApiClient.h"
-
-@protocol QueueService_NSURLConnectionRequestDelegate;
-
-@interface QueueITApiClient_NSURLConnectionRequest : NSObject
-
-- (NSString *)uniqueIdentifier;
-
-- (instancetype)initWithRequest:(NSURLRequest *)request
- expectedStatusCode:(NSInteger)statusCode
- success:(QueueServiceSuccess)success
- failure:(QueueServiceFailure)failure
- delegate:(id)delegate;
-
-@end
-
-@protocol QueueService_NSURLConnectionRequestDelegate
-- (void)requestDidComplete:(QueueITApiClient_NSURLConnectionRequest *)request;
-@end
diff --git a/QueueITLib/QueueITApiClient_NSURLConnectionRequest.m b/QueueITLib/QueueITApiClient_NSURLConnectionRequest.m
deleted file mode 100644
index 13cdcc5..0000000
--- a/QueueITLib/QueueITApiClient_NSURLConnectionRequest.m
+++ /dev/null
@@ -1,133 +0,0 @@
-#import "QueueITApiClient_NSURLConnectionRequest.h"
-
-
-@interface QueueITApiClient_NSURLConnectionRequest()
-
-@property (nonatomic, strong) NSURLConnection *connection;
-@property (nonatomic, strong) NSURLRequest *request;
-@property (nonatomic, strong) NSURLResponse *response;
-@property (nonatomic, strong) NSMutableData *data;
-@property (nonatomic, copy) QueueServiceSuccess successCallback;
-@property (nonatomic, copy) QueueServiceFailure failureCallback;
-@property (nonatomic, weak) id delegate;
-@property (nonatomic, strong) NSString *uniqueIdentifier;
-@property (nonatomic, assign) NSInteger expectedStatusCode;
-@property (nonatomic, assign) NSInteger actualStatusCode;
-
-@end
-
-@implementation QueueITApiClient_NSURLConnectionRequest
-
-- (instancetype)initWithRequest:(NSURLRequest *)request
- expectedStatusCode:(NSInteger)statusCode
- success:(QueueServiceSuccess)success
- failure:(QueueServiceFailure)failure
- delegate:(id)delegate
-{
- if ((self = [super init])) {
- self.request = request;
- self.expectedStatusCode = statusCode;
- self.successCallback = success;
- self.failureCallback = failure;
- self.uniqueIdentifier = [[NSUUID UUID] UUIDString];
- self.delegate = delegate;
-
- [self initiateRequest];
- }
-
- return self;
-}
-
-- (void)initiateRequest
-{
- self.response = nil;
- self.data = [NSMutableData data];
- self.actualStatusCode = NSNotFound;
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self];
-#pragma GCC diagnostic pop
-}
-
-#pragma mark - NSURLConnectionDelegate
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
-{
- dispatch_async(dispatch_get_main_queue(), ^{
- self.failureCallback(error, @"Unexpected failure occured.");
- });
-
- [self.delegate requestDidComplete:self];
-}
-
-#pragma mark - NSURLConnectionDataDelegate
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-{
- self.response = response;
- NSInteger responseCode = [(NSHTTPURLResponse *)response statusCode];
- self.actualStatusCode = responseCode;
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
-{
- [self appendData:data];
-}
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
- if ([self hasExpectedStatusCode]) {
- dispatch_async(dispatch_get_main_queue(), ^{
- self.successCallback(self.data);
- });
- }
- else {
- NSString *message = [NSString stringWithFormat:@"Unexpected response code: %li", (long)self.actualStatusCode];
-
- if (self.actualStatusCode >= 400 && self.actualStatusCode < 500)
- {
- message = [NSString stringWithCString:[self.data bytes] encoding:NSASCIIStringEncoding];
- }
- else
- {
- if (self.data) {
- NSError *jsonError = nil;
- id json = [NSJSONSerialization JSONObjectWithData:self.data options:0 error:&jsonError];
- if (json && [json isKindOfClass:[NSDictionary class]]) {
- NSString *errorMessage = [(NSDictionary *)json valueForKey:@"error"];
- if (errorMessage) {
- message = errorMessage;
- }
- }
- }
- }
-
- NSError *error = [NSError errorWithDomain:@"QueueService"
- code:self.actualStatusCode
- userInfo:@{ NSLocalizedDescriptionKey: message }];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- self.failureCallback(error, message);
- });
- }
-
- [self.delegate requestDidComplete:self];
-}
-
-#pragma mark - Private helpers
-
-- (void)appendData:(NSData *)data
-{
- [self.data appendData:data];
-}
-
-- (BOOL)hasExpectedStatusCode
-{
- if (self.actualStatusCode != NSNotFound) {
- return self.expectedStatusCode == self.actualStatusCode;
- }
-
- return NO;
-}
-
-@end
diff --git a/QueueITLib/QueueITEngine.h b/QueueITLib/QueueITEngine.h
deleted file mode 100644
index 8931119..0000000
--- a/QueueITLib/QueueITEngine.h
+++ /dev/null
@@ -1,98 +0,0 @@
-#import
-#import "QueuePassedInfo.h"
-#import "QueueDisabledInfo.h"
-#import "QueueTryPassResult.h"
-#import "QueueConsts.h"
-#import "QueueITWaitingRoomView.h"
-#import "QueueITWaitingRoomProvider.h"
-
-@protocol QueuePassedDelegate;
-@protocol QueueViewWillOpenDelegate;
-@protocol QueueDisabledDelegate;
-@protocol QueueITUnavailableDelegate;
-@protocol QueueUserExitedDelegate;
-@protocol QueueITErrorDelegate;
-@protocol QueueViewClosedDelegate;
-@protocol QueueSessionRestartDelegate;
-@protocol QueueUrlChangedDelegate;
-
-@protocol QueueViewDidAppearDelegate;
-
-@interface QueueITEngine : NSObject
-
-@property (nonatomic, weak)id _Nullable queuePassedDelegate;
-@property (nonatomic, weak)id _Nullable queueViewWillOpenDelegate;
-@property (nonatomic, weak)id _Nullable queueDisabledDelegate;
-@property (nonatomic, weak)id _Nullable queueITUnavailableDelegate;
-@property (nonatomic, weak)id _Nullable queueErrorDelegate;
-@property (nonatomic, weak)id _Nullable queueViewClosedDelegate;
-@property (nonatomic, weak)id _Nullable queueUserExitedDelegate;
-@property (nonatomic, weak)id _Nullable queueSessionRestartDelegate;
-@property (nonatomic, weak)id _Nullable queueUrlChangedDelegate;
-
-@property (nonatomic, weak)id _Nullable queueViewDidAppearDelegate;
-
-@property (nonatomic, strong)NSString* _Nullable errorMessage;
-@property (nonatomic, copy)NSString* _Nonnull customerId;
-@property (nonatomic, copy)NSString* _Nonnull eventId;
-@property (nonatomic, copy)NSString* _Nullable layoutName;
-@property (nonatomic, copy)NSString* _Nullable language;
-
--(instancetype _Nonnull )initWithHost:(UIViewController* _Nonnull)host
- customerId:(NSString* _Nonnull)customerId
- eventOrAliasId:(NSString* _Nonnull)eventOrAliasId
- layoutName:(NSString* _Nullable)layoutName
- language:(NSString* _Nullable)language;
-
--(void)setViewDelay:(int)delayInterval;
-
--(BOOL)run:(NSError* _Nullable* _Nullable)error;
--(BOOL)runWithEnqueueToken:(NSString* _Nonnull) enqueueToken
- error:(NSError* _Nullable*_Nullable) error;
--(BOOL)runWithEnqueueKey:(NSString* _Nonnull) enqueueKey
- error:(NSError* _Nullable*_Nullable) error;
--(BOOL)isRequestInProgress;
-
-@end
-
-@protocol QueuePassedDelegate
--(void)notifyYourTurn:(QueuePassedInfo* _Nullable) queuePassedInfo;
-@end
-
-
-@protocol QueueViewWillOpenDelegate
--(void)notifyQueueViewWillOpen;
-@end
-
-@protocol QueueDisabledDelegate
--(void)notifyQueueDisabled:(QueueDisabledInfo* _Nullable) queueDisabledInfo;
-@end
-
-@protocol QueueITUnavailableDelegate
--(void)notifyQueueITUnavailable:(NSString* _Nonnull) errorMessage;
-@end
-
-@protocol QueueITErrorDelegate
--(void)notifyQueueError:(NSString* _Nonnull) errorMessage errorCode:(long)errorCode;
-@end
-
-@protocol QueueViewClosedDelegate
--(void)notifyViewClosed;
-@end
-
-@protocol QueueUserExitedDelegate
--(void)notifyUserExited;
-@end
-
-@protocol QueueSessionRestartDelegate
--(void)notifySessionRestart;
-@end
-
-@protocol QueueUrlChangedDelegate
--(void)notifyQueueUrlChanged:(NSString* _Nonnull) url;
-@end
-
-
-@protocol QueueViewDidAppearDelegate
--(void)notifyQueueViewDidAppear;
-@end
diff --git a/QueueITLib/QueueITEngine.m b/QueueITLib/QueueITEngine.m
deleted file mode 100644
index 6778bf7..0000000
--- a/QueueITLib/QueueITEngine.m
+++ /dev/null
@@ -1,125 +0,0 @@
-#import "QueueITEngine.h"
-#import "QueueITApiClient.h"
-#import "QueueStatus.h"
-#import "IOSUtils.h"
-#import "QueueITWaitingRoomView.h"
-#import "QueueITWaitingRoomProvider.h"
-
-@interface QueueITEngine()
-@property (nonatomic, weak)UIViewController* host;
-
-@property QueueITWaitingRoomProvider* waitingRoomProvider;
-@property QueueITWaitingRoomView* waitingRoomView;
-@end
-
-@implementation QueueITEngine
-
--(instancetype)initWithHost:(UIViewController *)host customerId:(NSString*)customerId eventOrAliasId:(NSString*)eventOrAliasId layoutName:(NSString*)layoutName language:(NSString*)language
-{
- self = [super init];
- if(self) {
- self.waitingRoomProvider = [[QueueITWaitingRoomProvider alloc] initWithCustomerId:customerId
- eventOrAliasId:eventOrAliasId
- layoutName:layoutName
- language:language];
-
- self.waitingRoomView = [[QueueITWaitingRoomView alloc] initWithHost: host customerId: customerId eventId: eventOrAliasId];
- self.host = host;
- self.customerId = customerId;
- self.eventId = eventOrAliasId;
- self.layoutName = layoutName;
- self.language = language;
-
- self.waitingRoomView.delegate = self;
- self.waitingRoomProvider.delegate = self;
- }
- return self;
-}
-
--(void)setViewDelay:(int)delayInterval {
- [self.waitingRoomView setViewDelay:delayInterval];
-}
-
--(BOOL)isRequestInProgress {
- return [self.waitingRoomProvider IsRequestInProgress];
-}
-
--(BOOL)runWithEnqueueKey:(NSString *)enqueueKey
- error:(NSError *__autoreleasing *)error
-{
- return [self.waitingRoomProvider TryPassWithEnqueueKey:enqueueKey error:error];
-}
-
--(BOOL)runWithEnqueueToken:(NSString *)enqueueToken
- error:(NSError *__autoreleasing *)error
-{
- return [self.waitingRoomProvider TryPassWithEnqueueToken:enqueueToken error:error];
-}
-
--(BOOL)run:(NSError **)error
-{
- return [self.waitingRoomProvider TryPass:error];
-}
-
-
-
--(void)showQueue:(NSString*)queueUrl targetUrl:(NSString*)targetUrl
-{
- [self.waitingRoomView show:queueUrl targetUrl:targetUrl];
-}
-
-
-- (void)waitingRoomView:(nonnull QueueITWaitingRoomView *)view notifyViewPassedQueue:(QueuePassedInfo * _Nullable)queuePassedInfo {
- [self.queuePassedDelegate notifyYourTurn:queuePassedInfo];
-}
-
-- (void)notifyViewQueueWillOpen:(nonnull QueueITWaitingRoomView *)view {
- [self.queueViewWillOpenDelegate notifyQueueViewWillOpen];
-}
-
-- (void)waitingRoomProvider:(nonnull QueueITWaitingRoomProvider *)provider notifyProviderFailure:(NSString * _Nullable)errorMessage errorCode:(long)errorCode {
- if(errorCode == 3) {
- [self.queueITUnavailableDelegate notifyQueueITUnavailable:errorMessage];
- }
-
- [self.queueErrorDelegate notifyQueueError:errorMessage errorCode:errorCode];
-}
-
-- (void)notifyViewSessionRestart:(nonnull QueueITWaitingRoomView *)view {
- [self.queueSessionRestartDelegate notifySessionRestart];
-}
-
-- (void)notifyViewUserExited:(nonnull QueueITWaitingRoomView *)view {
- [self.queueUserExitedDelegate notifyUserExited];
-}
-
-- (void)notifyViewUserClosed:(nonnull QueueITWaitingRoomView *)view {
- [self.queueViewClosedDelegate notifyViewClosed];
-}
-
-- (void)waitingRoomView:(nonnull QueueITWaitingRoomView *)view notifyViewUpdatePageUrl:(NSString * _Nullable)urlString {
- [self.queueUrlChangedDelegate notifyQueueUrlChanged:urlString];
-}
-
--(void)notifyViewQueueDidAppear:(nonnull QueueITWaitingRoomView *)view {
- [self.queueViewDidAppearDelegate notifyQueueViewDidAppear];
-}
-
-- (void)waitingRoomProvider:(nonnull QueueITWaitingRoomProvider *)provider notifyProviderSuccess:(QueueTryPassResult * _Nonnull)queuePassResult {
- if([[queuePassResult redirectType] isEqual: @"safetynet"])
- {
- QueuePassedInfo* queuePassedInfo = [[QueuePassedInfo alloc] initWithQueueitToken:queuePassResult.queueToken];
- [self.queuePassedDelegate notifyYourTurn:queuePassedInfo];
- return;
- }
- else if([[queuePassResult redirectType] isEqual: @"disabled"] || [[queuePassResult redirectType] isEqual: @"idle"] || [[queuePassResult redirectType] isEqual: @"afterevent"])
- {
- QueueDisabledInfo* queueDisabledInfo = [[QueueDisabledInfo alloc]initWithQueueitToken:queuePassResult.queueToken];
- [self.queueDisabledDelegate notifyQueueDisabled:queueDisabledInfo];
- return;
- }
-
- [self showQueue:queuePassResult.queueUrl targetUrl:queuePassResult.targetUrl];
-
-}
-@end
diff --git a/QueueITLib/QueueITReachability.h b/QueueITLib/QueueITReachability.h
deleted file mode 100644
index d9c8fbb..0000000
--- a/QueueITLib/QueueITReachability.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- File: Reachability.h
- Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
- Version: 3.5
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
- Inc. ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or redistribution of
- this Apple software constitutes acceptance of these terms. If you do
- not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and
- subject to these terms, Apple grants you a personal, non-exclusive
- license, under Apple's copyrights in this original Apple software (the
- "Apple Software"), to use, reproduce, modify and redistribute the Apple
- Software, with or without modifications, in source and/or binary forms;
- provided that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the following
- text and disclaimers in all such redistributions of the Apple Software.
- Neither the name, trademarks, service marks or logos of Apple Inc. may
- be used to endorse or promote products derived from the Apple Software
- without specific prior written permission from Apple. Except as
- expressly stated in this notice, no other rights or licenses, express or
- implied, are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or by other
- works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE
- MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
- THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
- OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
- MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
- AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
- STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
- Copyright (C) 2014 Apple Inc. All Rights Reserved.
-
- */
-
-#import
-#import
-#import
-
-
-typedef enum : NSInteger {
- NotReachable = 0,
- ReachableViaWiFi,
- ReachableViaWWAN
-} NetworkStatus;
-
-
-extern NSString *kReachabilityChangedNotification;
-
-
-@interface QueueITReachability : NSObject
-
-/*!
- * Use to check the reachability of a given host name.
- */
-+ (instancetype)reachabilityWithHostName:(NSString *)hostName;
-
-/*!
- * Use to check the reachability of a given IP address.
- */
-+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
-
-/*!
- * Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
- */
-+ (instancetype)reachabilityForInternetConnection;
-
-/*!
- * Checks whether a local WiFi connection is available.
- */
-+ (instancetype)reachabilityForLocalWiFi;
-
-/*!
- * Start listening for reachability notifications on the current run loop.
- */
-- (BOOL)startNotifier;
-- (void)stopNotifier;
-
-- (NetworkStatus)currentReachabilityStatus;
-
-/*!
- * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
- */
-- (BOOL)connectionRequired;
-
-@end
-
-
diff --git a/QueueITLib/QueueITReachability.m b/QueueITLib/QueueITReachability.m
deleted file mode 100644
index 0bf7155..0000000
--- a/QueueITLib/QueueITReachability.m
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- File: Reachability.m
- Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.
- Version: 3.5
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
- Inc. ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or redistribution of
- this Apple software constitutes acceptance of these terms. If you do
- not agree with these terms, please do not use, install, modify or
- redistribute this Apple software.
-
- In consideration of your agreement to abide by the following terms, and
- subject to these terms, Apple grants you a personal, non-exclusive
- license, under Apple's copyrights in this original Apple software (the
- "Apple Software"), to use, reproduce, modify and redistribute the Apple
- Software, with or without modifications, in source and/or binary forms;
- provided that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the following
- text and disclaimers in all such redistributions of the Apple Software.
- Neither the name, trademarks, service marks or logos of Apple Inc. may
- be used to endorse or promote products derived from the Apple Software
- without specific prior written permission from Apple. Except as
- expressly stated in this notice, no other rights or licenses, express or
- implied, are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or by other
- works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE
- MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
- THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
- OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
- MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
- AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
- STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
- Copyright (C) 2014 Apple Inc. All Rights Reserved.
-
- */
-
-#import
-#import
-#import
-#import
-
-#import
-
-#import "QueueITReachability.h"
-
-
-NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";
-
-
-#pragma mark - Supporting functions
-
-#define kShouldPrintReachabilityFlags 1
-
-static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
-{
-#if kShouldPrintReachabilityFlags
-#endif
-}
-
-
-static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
-{
-#pragma unused (target, flags)
- NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
- NSCAssert([(__bridge NSObject*) info isKindOfClass: [QueueITReachability class]], @"info was wrong class in ReachabilityCallback");
-
- QueueITReachability* noteObject = (__bridge QueueITReachability *)info;
- // Post a notification to notify the client that the network reachability changed.
- [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
-}
-
-
-#pragma mark - Reachability implementation
-
-@implementation QueueITReachability
-{
- BOOL _alwaysReturnLocalWiFiStatus; //default is NO
- SCNetworkReachabilityRef _reachabilityRef;
-}
-
-+ (instancetype)reachabilityWithHostName:(NSString *)hostName
-{
- QueueITReachability* returnValue = NULL;
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
- if (reachability != NULL)
- {
- returnValue= [[self alloc] init];
- if (returnValue != NULL)
- {
- returnValue->_reachabilityRef = reachability;
- returnValue->_alwaysReturnLocalWiFiStatus = NO;
- }
- }
- return returnValue;
-}
-
-
-+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
-{
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
-
- QueueITReachability* returnValue = NULL;
-
- if (reachability != NULL)
- {
- returnValue = [[self alloc] init];
- if (returnValue != NULL)
- {
- returnValue->_reachabilityRef = reachability;
- returnValue->_alwaysReturnLocalWiFiStatus = NO;
- }
- }
- return returnValue;
-}
-
-
-
-+ (instancetype)reachabilityForInternetConnection
-{
- struct sockaddr_in zeroAddress;
- bzero(&zeroAddress, sizeof(zeroAddress));
- zeroAddress.sin_len = sizeof(zeroAddress);
- zeroAddress.sin_family = AF_INET;
-
- return [self reachabilityWithAddress:&zeroAddress];
-}
-
-
-+ (instancetype)reachabilityForLocalWiFi
-{
- struct sockaddr_in localWifiAddress;
- bzero(&localWifiAddress, sizeof(localWifiAddress));
- localWifiAddress.sin_len = sizeof(localWifiAddress);
- localWifiAddress.sin_family = AF_INET;
-
- // IN_LINKLOCALNETNUM is defined in as 169.254.0.0.
- localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
-
- QueueITReachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];
- if (returnValue != NULL)
- {
- returnValue->_alwaysReturnLocalWiFiStatus = YES;
- }
-
- return returnValue;
-}
-
-
-#pragma mark - Start and stop notifier
-
-- (BOOL)startNotifier
-{
- BOOL returnValue = NO;
- SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
-
- if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))
- {
- if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
- {
- returnValue = YES;
- }
- }
-
- return returnValue;
-}
-
-
-- (void)stopNotifier
-{
- if (_reachabilityRef != NULL)
- {
- SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
- }
-}
-
-
-- (void)dealloc
-{
- [self stopNotifier];
- if (_reachabilityRef != NULL)
- {
- CFRelease(_reachabilityRef);
- }
-}
-
-
-#pragma mark - Network Flag Handling
-
-- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
-{
- PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
- NetworkStatus returnValue = NotReachable;
-
- if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
- {
- returnValue = ReachableViaWiFi;
- }
-
- return returnValue;
-}
-
-
-- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
-{
- PrintReachabilityFlags(flags, "networkStatusForFlags");
- if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
- {
- // The target host is not reachable.
- return NotReachable;
- }
-
- NetworkStatus returnValue = NotReachable;
-
- if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
- {
- /*
- If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
- */
- returnValue = ReachableViaWiFi;
- }
-
- if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
- (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
- {
- /*
- ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
- */
-
- if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
- {
- /*
- ... and no [user] intervention is needed...
- */
- returnValue = ReachableViaWiFi;
- }
- }
-
- if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
- {
- /*
- ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
- */
- returnValue = ReachableViaWWAN;
- }
-
- return returnValue;
-}
-
-
-- (BOOL)connectionRequired
-{
- NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
- SCNetworkReachabilityFlags flags;
-
- if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
- {
- return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
- }
-
- return NO;
-}
-
-
-- (NetworkStatus)currentReachabilityStatus
-{
- NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");
- NetworkStatus returnValue = NotReachable;
- SCNetworkReachabilityFlags flags;
-
- if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
- {
- if (_alwaysReturnLocalWiFiStatus)
- {
- returnValue = [self localWiFiStatusForFlags:flags];
- }
- else
- {
- returnValue = [self networkStatusForFlags:flags];
- }
- }
-
- return returnValue;
-}
-
-
-@end
diff --git a/QueueITLib/QueueITWKViewController.h b/QueueITLib/QueueITWKViewController.h
deleted file mode 100644
index b84d5ce..0000000
--- a/QueueITLib/QueueITWKViewController.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#import
-
-@protocol QueueITViewControllerDelegate;
-
-@interface QueueITWKViewController : UIViewController
-
-@property (nonatomic, weak)id _Nullable delegate;
-
--(instancetype _Nullable )initWithHost:(nonnull UIViewController *)host
- queueUrl:(nonnull NSString*)queueUrl
- eventTargetUrl:(nonnull NSString*)eventTargetUrl
- customerId:(nonnull NSString*)customerId
- eventId:(nonnull NSString*)eventId;
-
-- (void) close:(void (^ __nullable)(void))completion;
-- (BOOL) handleSpecialUrls:(nonnull NSURL*) url
- decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler;
-- (BOOL) isTargetUrl:(nonnull NSURL*) targetUrl
- destinationUrl:(nonnull NSURL*) destinationUrl;
-- (BOOL) isBlockedUrl:(nonnull NSURL*) destinationUrl;
-
-@end
-
-@protocol QueueITViewControllerDelegate
--(void)notifyViewControllerClosed;
--(void)notifyViewControllerUserExited;
--(void)notifyViewControllerSessionRestart;
--(void)notifyViewControllerQueuePassed:(NSString* _Nullable) queueToken;
--(void)notifyViewControllerPageUrlChanged:(NSString* _Nullable) urlString;
-@end
diff --git a/QueueITLib/QueueITWKViewController.m b/QueueITLib/QueueITWKViewController.m
deleted file mode 100644
index 0c0220b..0000000
--- a/QueueITLib/QueueITWKViewController.m
+++ /dev/null
@@ -1,235 +0,0 @@
-#import "QueueITWKViewController.h"
-#import "QueueConsts.h"
-
-@interface QueueITWKViewController ()
-@property (nonatomic) WKWebView* webView;
-@property (nonatomic, strong) UIViewController* host;
-
-@property (nonatomic, strong)NSString* queueUrl;
-@property (nonatomic, strong)NSString* eventTargetUrl;
-@property (nonatomic, strong)UIActivityIndicatorView* spinner;
-@property (nonatomic, strong)NSString* customerId;
-@property (nonatomic, strong)NSString* eventId;
-@property BOOL isQueuePassed;
-@end
-
-static NSString * const JAVASCRIPT_GET_BODY_CLASSES = @"document.getElementsByTagName('body')[0].className";
-
-@implementation QueueITWKViewController
-
--(instancetype)initWithHost:(UIViewController *)host
- queueUrl:(NSString*)queueUrl
- eventTargetUrl:(NSString*)eventTargetUrl
- customerId:(NSString*)customerId
- eventId:(NSString*)eventId
-{
- self = [super init];
- if(self) {
- self.host = host;
- self.queueUrl = queueUrl;
- self.eventTargetUrl = eventTargetUrl;
- self.customerId = customerId;
- self.eventId = eventId;
- self.isQueuePassed = NO;
- }
- return self;
-}
-
-- (void)close:(void (^ __nullable)(void))onComplete {
- [self.host dismissViewControllerAnimated:YES completion:^{
- if(onComplete!=nil){
- onComplete();
- }
- }];
-}
-
-- (BOOL) isTargetUrl:(nonnull NSURL*) targetUrl
- destinationUrl:(nonnull NSURL*) destinationUrl {
- NSString* destinationHost = destinationUrl.host;
- NSString* destinationPath = destinationUrl.path;
- NSString* targetHost = targetUrl.host;
- NSString* targetPath = targetUrl.path;
-
- return [destinationHost isEqualToString: targetHost]
- && [destinationPath isEqualToString: targetPath];
-}
-
-- (BOOL) isBlockedUrl:(nonnull NSURL*) destinationUrl {
- NSString* path = destinationUrl.path;
- if([path hasPrefix: @"/what-is-this.html"]){
- return true;
- }
- return false;
-}
-
-- (BOOL)handleSpecialUrls:(NSURL*) url
- decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler {
- if([[url absoluteString] isEqualToString: QueueCloseUrl]){
- [self close: ^{
- [self.delegate notifyViewControllerClosed];
- }];
- decisionHandler(WKNavigationActionPolicyCancel);
- return true;
- } else if ([[url absoluteString] isEqualToString: QueueRestartSessionUrl]){
- [self close:^{
- [self.delegate notifyViewControllerSessionRestart];
- }];
- decisionHandler(WKNavigationActionPolicyCancel);
- return true;
- }
- return NO;
-}
-
-- (void)viewDidLoad {
- [super viewDidLoad];
- self.spinner = [[UIActivityIndicatorView alloc]initWithFrame:self.view.bounds];
- [self.spinner setColor:[UIColor grayColor]];
-
- WKPreferences* preferences = [[WKPreferences alloc]init];
- preferences.javaScriptEnabled = YES;
- WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc]init];
- config.preferences = preferences;
- WKWebView* webview = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
- webview.navigationDelegate = self;
- [webview setAutoresizingMask: UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
- // Make webview transparent
- webview.opaque = NO;
- webview.backgroundColor = [UIColor clearColor];
- self.webView = webview;
-}
-
-- (void)viewWillAppear:(BOOL)animated{
- [super viewWillAppear:animated];
-}
-
--(void)viewWillLayoutSubviews{
- [super viewWillLayoutSubviews];
- [self.spinner startAnimating];
- self.webView.frame = self.view.bounds;
- self.spinner.frame = self.view.bounds;
-
- [self.view addSubview:self.webView];
- [self.webView addSubview:self.spinner];
-
- [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.queueUrl]]];
-}
-
--(void)viewDidAppear:(BOOL)animated{
- [super viewDidAppear:animated];
-}
-
-- (void)viewDidDisappear:(BOOL)animated
-{
- [super viewDidDisappear:animated];
- [self.webView removeFromSuperview];
- self.webView = nil;
-}
-
-#pragma mark - WKNavigationDelegate
-
-- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler{
- if (!self.isQueuePassed) {
- NSURLRequest* request = navigationAction.request;
- NSString* urlString = [[request URL] absoluteString];
- NSString* targetUrlString = self.eventTargetUrl;
- if (urlString != nil) {
- NSURL* url = [NSURL URLWithString:urlString];
- NSURL* targetUrl = [NSURL URLWithString:targetUrlString];
- if(urlString != nil && ![urlString isEqualToString:@"about:blank"]) {
- BOOL isQueueUrl = [self.queueUrl containsString:url.host];
- BOOL isNotFrame = [[[request URL] absoluteString] isEqualToString:[[request mainDocumentURL] absoluteString]];
-
- if([self handleSpecialUrls:url decisionHandler:decisionHandler]){
- return;
- }
-
- if([self isBlockedUrl: url]){
- decisionHandler(WKNavigationActionPolicyCancel);
- return;
- }
-
- if (isNotFrame) {
- if (isQueueUrl) {
- [self raiseQueuePageUrl:urlString];
- }
- if ([self isTargetUrl: targetUrl
- destinationUrl: url]) {
- self.isQueuePassed = YES;
- NSString* queueitToken = [self extractQueueToken:url.absoluteString];
- [self.delegate notifyViewControllerQueuePassed:queueitToken];
- [self.host dismissViewControllerAnimated:YES completion:^{
- }];
- decisionHandler(WKNavigationActionPolicyCancel);
- return;
- }
- }
- if (navigationAction.navigationType == WKNavigationTypeLinkActivated && !isQueueUrl) {
- if (@available(iOS 10, *)){
- [[UIApplication sharedApplication] openURL:[request URL] options:@{} completionHandler:^(BOOL success){
-
- }];
- }
- else {
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- [[UIApplication sharedApplication] openURL:[request URL]];
-#pragma GCC diagnostic pop
- }
-
- decisionHandler(WKNavigationActionPolicyCancel);
- return;
- }
- }
- }
- }
-
- decisionHandler(WKNavigationActionPolicyAllow);
-}
-
-- (NSString*)extractQueueToken:(NSString*) url {
- NSString* tokenKey = @"queueittoken=";
- if ([url containsString:tokenKey]) {
- NSString* token = [url substringFromIndex:NSMaxRange([url rangeOfString:tokenKey])];
- if([token containsString:@"&"]) {
- token = [token substringToIndex:NSMaxRange([token rangeOfString:@"&"]) - 1];
- }
- return token;
- }
- return nil;
-}
-
-- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
-}
-
-- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
- [self.spinner stopAnimating];
- if (![self.webView isLoading])
- {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
- }
-
- // Check if user exitted through the default exit link and notify the engine
- [self.webView evaluateJavaScript:JAVASCRIPT_GET_BODY_CLASSES completionHandler:^(id result, NSError* error){
- if (error != nil) {
- NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
- }
- else {
- NSString* resultString = [NSString stringWithFormat:@"%@", result];
- NSArray *htmlBodyClasses = [resultString componentsSeparatedByString:@" "];
- BOOL isExitClassPresent = [htmlBodyClasses containsObject:@"exit"];
- if (isExitClassPresent) {
- [self.delegate notifyViewControllerUserExited];
- }
- }
- }];
-}
-
-- (void)raiseQueuePageUrl:(NSString *)urlString {
- [self.delegate notifyViewControllerPageUrlChanged:urlString];
-}
-
--(void)appWillResignActive:(NSNotification*)note
-{
-}
-
-@end
diff --git a/QueueITLib/QueueITWaitingRoomProvider.h b/QueueITLib/QueueITWaitingRoomProvider.h
deleted file mode 100644
index 2bd25b3..0000000
--- a/QueueITLib/QueueITWaitingRoomProvider.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#import "QueueITWaitingRoomView.h"
-#import "QueueTryPassResult.h"
-
-@protocol QueueITWaitingRoomProviderDelegate;
-
-@interface QueueITWaitingRoomProvider : NSObject
-
-typedef enum {
- NetworkUnavailable = -100,
- RequestAlreadyInProgress = 10
-} QueueITRuntimeError;
-#define QueueITRuntimeErrorArray @"Network connection is unavailable", @"Enqueue request is already in progress", nil
-
-@property (nonatomic, weak)id _Nullable delegate;
-
--(instancetype _Nonnull)initWithCustomerId:(NSString* _Nonnull)customerId
- eventOrAliasId:(NSString* _Nonnull)eventOrAliasId
- layoutName:(NSString* _Nullable)layoutName
- language:(NSString* _Nullable)language;
-
--(BOOL)TryPass: (NSError* _Nullable*_Nullable)error;
--(BOOL)TryPassWithEnqueueToken:(NSString* _Nullable)enqueueToken
- error:(NSError* _Nullable*_Nullable)error;
--(BOOL)TryPassWithEnqueueKey:(NSString* _Nullable)enqueueKey
- error:(NSError* _Nullable*_Nullable)error;
--(BOOL)IsRequestInProgress;
-@end
-
-@protocol QueueITWaitingRoomProviderDelegate
-
--(void)waitingRoomProvider:(nonnull QueueITWaitingRoomProvider*)provider notifyProviderSuccess:(QueueTryPassResult* _Nonnull) queuePassResult;
--(void)waitingRoomProvider:(nonnull QueueITWaitingRoomProvider*)provider notifyProviderFailure:(NSString* _Nullable)errorMessage
- errorCode:(long)errorCode;
-@end
diff --git a/QueueITLib/QueueITWaitingRoomProvider.m b/QueueITLib/QueueITWaitingRoomProvider.m
deleted file mode 100644
index 189639b..0000000
--- a/QueueITLib/QueueITWaitingRoomProvider.m
+++ /dev/null
@@ -1,207 +0,0 @@
-#import "QueueITWaitingRoomProvider.h"
-#import "IOSUtils.h"
-#import "QueueITApiClient.h"
-#import "QueueTryPassResult.h"
-#import "QueueITReachability.h"
-
-// TODO: Include all the method calls here
-@interface QueueITWaitingRoomProvider()
-@property (nonatomic) QueueITReachability *internetReachability;
-@property NSString* customerId;
-@property NSString* eventOrAliasId;
-@property NSString* layoutName;
-@property NSString* language;
-@property BOOL requestInProgress;
-@property int deltaSec;
-
-
-@end
-
-@implementation QueueITWaitingRoomProvider
-
-static int MAX_RETRY_SEC = 10;
-static int INITIAL_WAIT_RETRY_SEC = 1;
-
--(instancetype _Nonnull)initWithCustomerId:(NSString* _Nonnull)customerId
- eventOrAliasId:(NSString* _Nonnull)eventOrAliasId
- layoutName:(NSString* _Nullable)layoutName
- language:(NSString* _Nullable)language {
-
- if(self = [super init]) {
- self.customerId = customerId;
- self.eventOrAliasId = eventOrAliasId;
- self.layoutName = layoutName;
- self.language = language;
- self.deltaSec = INITIAL_WAIT_RETRY_SEC;
- self.internetReachability = [QueueITReachability reachabilityForInternetConnection];
- }
-
- return self;
-}
-
--(BOOL) TryPass: (NSError**)error {
- return [self tryEnqueue:nil enqueueKey:nil error:error];
-}
-
--(BOOL) TryPassWithEnqueueToken: (NSString*)enqueueToken error:(NSError *__autoreleasing *)error {
- return [self tryEnqueue:enqueueToken enqueueKey:nil error:error];
-}
-
--(BOOL) TryPassWithEnqueueKey: (NSString*)enqueueKey error:(NSError *__autoreleasing *)error {
- return [self tryEnqueue:nil enqueueKey:enqueueKey error:error];
-}
-
-
--(BOOL)tryEnqueue:(NSString*)enqueueToken
- enqueueKey:(NSString*)enqueueKey
- error:(NSError**)error
-{
- if(![self checkConnection:error]) {
- return NO;
- }
-
- if(self.requestInProgress) {
- *error = [NSError errorWithDomain:@"QueueITRuntimeException" code:RequestAlreadyInProgress userInfo:nil];
- return NO;
- }
-
- [IOSUtils getUserAgent:^(NSString * userAgent) {
- [self tryEnqueueWithUserAgent:userAgent enqueueToken:enqueueToken enqueueKey:enqueueKey error:error];
- }];
-
- return YES;
-}
-
--(void)tryEnqueueWithUserAgent:(NSString*)secretAgent
- enqueueToken:(NSString*)enqueueToken
- enqueueKey:(NSString*)enqueueKey
- error:(NSError**)error
-{
- NSString* userId = [IOSUtils getUserId];
- NSString* userAgent = [NSString stringWithFormat:@"%@;%@", secretAgent, [IOSUtils getLibraryVersion]];
- NSString* sdkVersion = [IOSUtils getSdkVersion];
-
- QueueITApiClient* apiClient = [QueueITApiClient getInstance];
- [apiClient enqueue:self.customerId
- eventOrAliasId:self.eventOrAliasId
- userId:userId
- userAgent:userAgent
- sdkVersion:sdkVersion
- layoutName:self.layoutName
- language:self.language
- enqueueToken:enqueueToken
- enqueueKey:enqueueKey
- success:^(QueueStatus *queueStatus)
-{
- if (queueStatus == NULL) {
- [self enqueueRetryMonitor:enqueueToken enqueueKey:enqueueKey error:error];
- return;
- }
-
- [self handleAppEnqueueResponse: queueStatus.queueId
- queueURL:queueStatus.queueUrlString
- eventTargetURL:queueStatus.eventTargetUrl
- queueItToken:queueStatus.queueitToken];
-
- self.requestInProgress = NO;
- }
- failure:^(NSError *error, NSString* errorMessage)
- {
- if (error.code >= 400 && error.code < 500)
- {
- [self.delegate waitingRoomProvider:self notifyProviderFailure:errorMessage errorCode:error.code];
- }
- else
- {
- [self enqueueRetryMonitor:enqueueToken enqueueKey:enqueueKey error:&error];
- }
- }];
-}
-
--(void)handleAppEnqueueResponse:(NSString*) queueId
- queueURL:(NSString*) queueURL
- eventTargetURL:(NSString*) targetURL
- queueItToken:(NSString*) token {
-
- bool isPassedThrough = ![self isNullOrEmpty:token];
-
- NSString* redirectType = [self getRedirectTypeFromToken:token];
-
- QueueTryPassResult* queueTryPassResult = [[QueueTryPassResult alloc]
- initWithQueueUrl:queueURL
- targetUrl:targetURL
- redirectType:redirectType
- isPassedThrough:isPassedThrough
- queueToken:token];
-
- [self.delegate waitingRoomProvider:self notifyProviderSuccess:queueTryPassResult];
-}
-
--(void)enqueueRetryMonitor:(NSString*)enqueueToken
- enqueueKey:(NSString*)enqueueKey
- error:(NSError**)error
-{
- if (self.deltaSec < MAX_RETRY_SEC)
- {
- [self tryEnqueue:enqueueToken enqueueKey:enqueueKey error:error];
-
- [NSThread sleepForTimeInterval:self.deltaSec];
- self.deltaSec = self.deltaSec * 2;
- }
- else
- {
- self.deltaSec = INITIAL_WAIT_RETRY_SEC;
- self.requestInProgress = NO;
- [self.delegate waitingRoomProvider:self notifyProviderFailure:@"Error! Queue is unavailable." errorCode:3];
- }
-}
-
--(BOOL)checkConnection:(NSError **)error
-{
- int count = 0;
- while (count < 5)
- {
- NetworkStatus netStatus = [self.internetReachability currentReachabilityStatus];
- if (netStatus == NotReachable)
- {
- [NSThread sleepForTimeInterval:1.0f];
- count++;
- }
- else
- {
- return YES;
- }
- }
- *error = [NSError errorWithDomain:@"QueueITRuntimeException" code:NetworkUnavailable userInfo:nil];
- return NO;
-}
-
--(BOOL)IsRequestInProgress {
- return self.requestInProgress;
-}
-
--(BOOL)isNullOrEmpty:(NSString*)queueToken {
- bool isNull = queueToken == nil || queueToken == (id)[NSNull null];
- bool isEmpty = isNull || [queueToken length] == 0;
-
- return isNull && isEmpty;
-}
-
--(NSString*) getRedirectTypeFromToken: (NSString*) queueToken {
-
- if([self isNullOrEmpty:queueToken])
- {
- return @"queue";
- }
-
- NSString *searchedString = queueToken;
- NSRange searchedRange = NSMakeRange(0, [searchedString length]);
- NSString *pattern = @"\\~rt_(.*?)\\~";
- NSError *error = nil;
-
- NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
- NSTextCheckingResult *match = [regex firstMatchInString:searchedString options:0 range: searchedRange];
- return [searchedString substringWithRange:[match rangeAtIndex:1]];
-}
-
-@end
diff --git a/QueueITLib/QueueITWaitingRoomView.h b/QueueITLib/QueueITWaitingRoomView.h
deleted file mode 100644
index ca4af3c..0000000
--- a/QueueITLib/QueueITWaitingRoomView.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#import "QueueITWKViewController.h"
-#import "QueueDisabledInfo.h"
-#import "QueuePassedInfo.h"
-
-@protocol QueueITWaitingRoomViewDelegate;
-
-@interface QueueITWaitingRoomView : NSObject
-
-@property (nonatomic, weak)id _Nullable delegate;
-
--(instancetype _Nonnull)initWithHost:(UIViewController* _Nonnull)host
- customerId: (NSString* _Nonnull)customerId
- eventId: (NSString* _Nonnull)eventId;
-
--(void)show:(NSString* _Nonnull)queueUrl targetUrl:(NSString* _Nonnull)targetUrl;
--(void)setViewDelay:(int)delayInterval;
--(void)close:(void (^ __nullable)(void))onComplete;
-
-@end
-
-@protocol QueueITWaitingRoomViewDelegate
--(void) notifyViewUserExited:(nonnull QueueITWaitingRoomView*)view;
--(void) notifyViewUserClosed:(nonnull QueueITWaitingRoomView*)view;
--(void) notifyViewSessionRestart:(nonnull QueueITWaitingRoomView*)view;
--(void) waitingRoomView:(nonnull QueueITWaitingRoomView*)view notifyViewPassedQueue:(QueuePassedInfo* _Nullable)queuePassedInfo;
--(void) notifyViewQueueDidAppear:(nonnull QueueITWaitingRoomView*)view;
--(void) notifyViewQueueWillOpen:(nonnull QueueITWaitingRoomView*)view;
--(void) waitingRoomView:(nonnull QueueITWaitingRoomView*)view notifyViewUpdatePageUrl:(NSString* _Nullable) urlString;
-@end
diff --git a/QueueITLib/QueueITWaitingRoomView.m b/QueueITLib/QueueITWaitingRoomView.m
deleted file mode 100644
index 329095b..0000000
--- a/QueueITLib/QueueITWaitingRoomView.m
+++ /dev/null
@@ -1,100 +0,0 @@
-#import
-#import "QueueITWaitingRoomView.h"
-#import "QueueITWKViewController.h"
-
-@interface QueueITWaitingRoomView ()
-@property (nonatomic, weak) UIViewController* host;
-@property (nonatomic, weak) QueueITWKViewController* currentWebView;
-@property NSString* customerId;
-@property NSString* eventId;
-@property int delayInterval;
-@end
-
-@implementation QueueITWaitingRoomView
-
--(instancetype _Nonnull)initWithHost:(UIViewController *)host
- customerId: (NSString* _Nonnull) customerId
- eventId: (NSString * _Nonnull)eventId
-
-{
- if(self = [super init]) {
- self.host = host;
- self.customerId = customerId;
- self.eventId = eventId;
- }
-
- return self;
-}
-
--(void) show:(NSString* _Nonnull)queueUrl targetUrl:(NSString* _Nonnull)targetUrl
-{
- [self raiseQueueViewWillOpen];
-
- QueueITWKViewController *queueWKVC = [[QueueITWKViewController alloc] initWithHost:self.host
- queueUrl:queueUrl
- eventTargetUrl:targetUrl
- customerId:self.customerId
- eventId:self.eventId];
-
- queueWKVC.delegate = self;
-
- if (@available(iOS 13.0, *)) {
- [queueWKVC setModalPresentationStyle: UIModalPresentationFullScreen];
- }
- if (self.delayInterval > 0) {
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delayInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [self.host presentViewController:queueWKVC animated:YES completion:^{
- self.currentWebView = queueWKVC;
- [self.delegate notifyViewQueueDidAppear:self ];
- }];
- });
- } else {
- dispatch_async(dispatch_get_main_queue(), ^{
- [self.host presentViewController:queueWKVC animated:YES completion:^{
- self.currentWebView = queueWKVC;
- [self.delegate notifyViewQueueDidAppear:self ];
- }];
- });
- }
-}
-
--(void)close:(void (^ __nullable)(void))onComplete
-{
- if(self.currentWebView!=nil){
- dispatch_async(dispatch_get_main_queue(), ^{
- [self.currentWebView close: onComplete];
- });
- }
-}
-
-- (void)raiseQueueViewWillOpen {
- [self.delegate notifyViewQueueWillOpen:self];
-}
-
--(void)setViewDelay:(int)delayInterval {
- self.delayInterval = delayInterval;
-}
-
--(void) notifyViewControllerUserExited {
- [self.delegate notifyViewUserExited:self];
-}
-
--(void) notifyViewControllerClosed {
- [self.delegate notifyViewUserClosed:self];
-}
-
--(void) notifyViewControllerSessionRestart {
- [self.delegate notifyViewSessionRestart:self];
-}
-
--(void) notifyViewControllerQueuePassed:(NSString *)queueToken {
- QueuePassedInfo* queuePassedInfo = [[QueuePassedInfo alloc] initWithQueueitToken:queueToken];
- [self.delegate waitingRoomView:self notifyViewPassedQueue:queuePassedInfo];
-}
-
--(void)notifyViewControllerPageUrlChanged:(NSString* _Nullable) urlString {
- [self.delegate waitingRoomView:self notifyViewUpdatePageUrl:urlString];
-}
-
-@end
-
diff --git a/QueueITLib/QueuePassedInfo.h b/QueueITLib/QueuePassedInfo.h
deleted file mode 100644
index 36fcc6f..0000000
--- a/QueueITLib/QueuePassedInfo.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#import
-
-@interface QueuePassedInfo : NSObject
-
-@property (nonatomic, strong) NSString* _Nullable queueitToken;
-
--(instancetype _Nonnull )initWithQueueitToken:(NSString* _Nullable) queueitToken;
-
-@end
diff --git a/QueueITLib/QueuePassedInfo.m b/QueueITLib/QueuePassedInfo.m
deleted file mode 100644
index 66def51..0000000
--- a/QueueITLib/QueuePassedInfo.m
+++ /dev/null
@@ -1,14 +0,0 @@
-#import "QueuePassedInfo.h"
-
-@implementation QueuePassedInfo
-
--(instancetype)initWithQueueitToken:(NSString *)queueitToken
-{
- if(self = [super init]) {
- self.queueitToken = queueitToken;
- }
-
- return self;
-}
-
-@end
diff --git a/QueueITLib/QueueStatus.h b/QueueITLib/QueueStatus.h
deleted file mode 100644
index e3713d6..0000000
--- a/QueueITLib/QueueStatus.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#import
-
-@interface QueueStatus : NSObject
-
-@property (nonatomic, strong) NSString* queueId;
-@property (nonatomic, strong)NSString* queueUrlString;
-@property (nonatomic, strong) NSString* eventTargetUrl;
-@property (nonatomic, strong) NSString* queueitToken;
-
--(instancetype)initWithDictionary:(NSDictionary *)dictionary;
-
-@end
diff --git a/QueueITLib/QueueStatus.m b/QueueITLib/QueueStatus.m
deleted file mode 100644
index b53b58f..0000000
--- a/QueueITLib/QueueStatus.m
+++ /dev/null
@@ -1,59 +0,0 @@
-#import "QueueStatus.h"
-
-NSString * const KEY_QUEUE_ID = @"QueueId";
-NSString * const KEY_QUEUE_URL = @"QueueUrl";
-NSString * const KEY_EVENT_TARGET_URL = @"EventTargetUrl";
-NSString * const KEY_QUEUEIT_TOKEN = @"QueueitToken";
-
-@implementation QueueStatus
-
--(instancetype)init:(NSString *)queueId
- queueUrl:(NSString *)queueUrlString
- eventTargetUrl:(NSString *)eventTargetUrl
- queueitToken:(NSString *)queueitToken
-{
- if(self = [super init]) {
- self.queueId = queueId;
- self.queueUrlString = queueUrlString;
- self.eventTargetUrl = eventTargetUrl;
- self.queueitToken = queueitToken;
- }
-
- return self;
-}
-
-- (instancetype)initWithDictionary:(NSDictionary *)dictionary
-{
- NSString *queueId;
- NSString *queueUrlString;
- NSString *eventTargetUrl;
- NSString *queueitToken;
- id value;
-
-
- value = dictionary[KEY_QUEUE_ID];
- if ([value isKindOfClass:[NSString class]]) {
- queueId = (NSString*)value;
- }
-
- value = dictionary[KEY_QUEUE_URL];
- if ([value isKindOfClass:[NSString class]]) {
- queueUrlString = (NSString*)value;
- }
-
- value = dictionary[KEY_EVENT_TARGET_URL];
- if ([value isKindOfClass:[NSString class]]) {
- eventTargetUrl = (NSString*)value;
- }
-
- value = dictionary[KEY_QUEUEIT_TOKEN];
- if ([value isKindOfClass:[NSString class]]) {
- queueitToken = (NSString*)value;
- }
-
- return [self init:queueId
- queueUrl:queueUrlString
- eventTargetUrl:eventTargetUrl
- queueitToken:queueitToken];
-}
-@end
diff --git a/QueueITLib/QueueTryPassResult.h b/QueueITLib/QueueTryPassResult.h
deleted file mode 100644
index 8abef8f..0000000
--- a/QueueITLib/QueueTryPassResult.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#import
-
-@interface QueueTryPassResult : NSObject
-
-@property (nonatomic, strong) NSString* _Nullable queueUrl;
-@property (nonatomic, strong) NSString* _Nullable targetUrl;
-@property (nonatomic, strong) NSString* _Nonnull redirectType;
-@property (nonatomic) BOOL isPassedThrough;
-@property (nonatomic) NSString* _Nullable queueToken;
-
-
--(instancetype _Nonnull )
- initWithQueueUrl: (NSString* _Nullable) queueUrl
- targetUrl:(NSString* _Nullable)targetUrl
- redirectType: (NSString* _Nonnull) redirectType
- isPassedThrough: (BOOL) isPassedThrough
- queueToken: (NSString* _Nullable) queueToken;
-
-@end
diff --git a/QueueITLib/QueueTryPassResult.m b/QueueITLib/QueueTryPassResult.m
deleted file mode 100644
index 76784c3..0000000
--- a/QueueITLib/QueueTryPassResult.m
+++ /dev/null
@@ -1,24 +0,0 @@
-#import "QueueTryPassResult.h"
-
-
-@implementation QueueTryPassResult
-
--(instancetype _Nonnull )
- initWithQueueUrl: (NSString* _Nullable) queueUrl
- targetUrl:(NSString* _Nullable)targetUrl
- redirectType: (NSString* _Nonnull) redirectType
- isPassedThrough: (BOOL) isPassedThrough
- queueToken: (NSString* _Nullable) queueToken
-{
- if(self = [super init]) {
- self.queueUrl = queueUrl;
- self.targetUrl = targetUrl;
- self.redirectType = redirectType;
- self.isPassedThrough = isPassedThrough;
- self.queueToken = queueToken;
- }
-
- return self;
-}
-
-@end
diff --git a/README.md b/README.md
index 4c870ec..ab38737 100644
--- a/README.md
+++ b/README.md
@@ -168,7 +168,7 @@ When the user clicks back, the same check needs to be done.
### Getting the status of a waiting room
-If you're using version ```3.1.14``` or newer, it's possible to get the state of the waiting room using the new ```QueueITWaitingRoomProvider``` with one of the following methods:
+If you're using version ```3.1.14``` or newer, it's possible to get the state of the waiting room using the new ```WaitingRoomProvider``` with one of the following methods:
* ```TryPass```
* ```TryPassWithEnqueueToken```
@@ -176,21 +176,21 @@ If you're using version ```3.1.14``` or newer, it's possible to get the state of
Calling one of the above methods will trigger either the ```notifyProviderSuccess``` callback on success, or ```notifyProviderFailure``` callback on failure.
-When using the ```notifyProviderQueueITUnavailable``` from the ```ProviderSuccessDelegate``` it'll provide with a ```QueueTryPassResult``` depending on the ```isPassThrough``` result:
+When using the ```notifyProviderQueueITUnavailable``` from the ```ProviderSuccessDelegate``` it'll provide with a ```TryPassResult``` depending on the ```isPassThrough``` result:
-* ```true``` means that the ```QueueItToken``` is *not* empty, and more information is available in the ```QueueTryPassResult```
-* ```false``` means that the waiting room is *active*. You can show the visitor the waiting room by calling ```show``` from the ```QueueITWaitingRoomView```, by providing a ```queueUrl``` and ```targetUrl``` *([Read more about it here](#showing-the-queue-page-to-visitors))*
+* ```true``` means that the ```QueueItToken``` is *not* empty, and more information is available in the ```TryPassResult```
+* ```false``` means that the waiting room is *active*. You can show the visitor the waiting room by calling ```show``` from the ```WaitingRoomView```, by providing a ```queueUrl``` and ```targetUrl``` *([Read more about it here](#showing-the-queue-page-to-visitors))*
### Showing the queue page to visitors
-If you're using version ```3.1.14``` or newer, the ```QueueITWaitingRoomView``` class is available.
+If you're using version ```3.1.14``` or newer, the ```WaitingRoomView``` class is available.
-When the waiting room is queueing visitors, each visitor has to visit it once. Using the ```show``` method you can do this, you have to provide the ```queueUrl```, and the ```targetUrl``` which is returned by the ```notifyProviderSuccess``` from ```QueueITWaitingRoomProvider``` class, given the waiting room is *active* ([Read more about it here](#getting-the-status-of-a-waiting-room))
+When the waiting room is queueing visitors, each visitor has to visit it once. Using the ```show``` method you can do this, you have to provide the ```queueUrl```, and the ```targetUrl``` which is returned by the ```notifyProviderSuccess``` from ```WaitingRoomProvider``` class, given the waiting room is *active* ([Read more about it here](#getting-the-status-of-a-waiting-room))
#### Sample code showing the queue page:
``` objc
--(void)notifyProviderSuccess:(QueueTryPassResult* _Nonnull) queuePassResult {
+-(void)notifyProviderSuccess:(TryPassResult* _Nonnull) queuePassResult {
[self.waitingRoomView show:queuePassResult.queueUrl targetUrl:queuePassResult.targetUrl];
}
```
diff --git a/Sources/QueueITLib/ApiClient.swift b/Sources/QueueITLib/ApiClient.swift
new file mode 100644
index 0000000..fa91e09
--- /dev/null
+++ b/Sources/QueueITLib/ApiClient.swift
@@ -0,0 +1,147 @@
+import Foundation
+
+typealias QueueServiceSuccess = (Data) -> Void
+typealias QueueServiceFailure = (Error, String) -> Void
+
+@MainActor
+final class ApiClient {
+ static let API_ROOT = "https://%@.queue-it.net/api/mobileapp/queue"
+ static let TESTING_API_ROOT = "https://%@.test.queue-it.net/api/mobileapp/queue"
+ private var testingIsEnabled = false
+
+ func setTesting(_ enabled: Bool) {
+ testingIsEnabled = enabled
+ }
+
+ func enqueue(
+ customerId: String,
+ eventOrAliasId: String,
+ userId: String,
+ userAgent: String,
+ sdkVersion: String,
+ layoutName: String?,
+ language: String?,
+ enqueueToken: String?,
+ enqueueKey: String?
+ ) async throws -> Status? {
+ var bodyDict: [String: Any] = [
+ "userId": userId,
+ "userAgent": userAgent,
+ "sdkVersion": sdkVersion,
+ ]
+
+ if let layoutName = layoutName {
+ bodyDict["layoutName"] = layoutName
+ }
+
+ if let language = language {
+ bodyDict["language"] = language
+ }
+
+ if let enqueueToken = enqueueToken {
+ bodyDict["enqueueToken"] = enqueueToken
+ }
+
+ if let enqueueKey = enqueueKey {
+ bodyDict["enqueueKey"] = enqueueKey
+ }
+
+ let apiRoot = testingIsEnabled ? ApiClient.TESTING_API_ROOT : ApiClient.API_ROOT
+ var urlAsString = String(format: apiRoot, customerId)
+ urlAsString += "/\(customerId)"
+ urlAsString += "/\(eventOrAliasId)"
+ urlAsString += "/enqueue"
+
+ let data = try await submitPOSTPath(path: urlAsString, body: bodyDict)
+ do {
+ if let userDict = try JSONSerialization.jsonObject(
+ with: data,
+ options: []
+ ) as? [String: Any] {
+ return Status(dictionary: userDict)
+ } else {
+ return nil
+ }
+ } catch {
+ return nil
+ }
+ }
+}
+
+private extension ApiClient {
+ func submitPOSTPath(
+ path: String,
+ body bodyDict: [String: Any]
+ ) async throws -> Data {
+ guard let url = URL(string: path) else {
+ let error = NSError(
+ domain: "ApiClient",
+ code: -1,
+ userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]
+ )
+ throw error
+ }
+
+ return try await submitRequest(
+ with: url,
+ method: "POST",
+ body: bodyDict
+ )
+ }
+
+ func submitRequest(
+ with url: URL,
+ method httpMethod: String,
+ body bodyDict: [String: Any]
+ ) async throws -> Data {
+ var request = URLRequest(url: url)
+ request.httpMethod = httpMethod
+
+ do {
+ let jsonData = try JSONSerialization.data(withJSONObject: bodyDict, options: [])
+ request.httpBody = jsonData
+ } catch {
+ throw error
+ }
+
+ request.addValue("application/json", forHTTPHeaderField: "Accept")
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+
+ return try await initiateRequest(request: request)
+ }
+
+ func initiateRequest(request: URLRequest) async throws -> Data {
+ let (data, response) = try await URLSession.shared.data(for: request)
+
+ guard let response = response as? HTTPURLResponse else {
+ throw ApiClientError.nilResponse
+ }
+
+ let actualStatusCode = response.statusCode
+ if actualStatusCode == 200 {
+ return data
+ } else {
+ var message = "Unexpected response code: \(actualStatusCode)"
+ if actualStatusCode >= 400, actualStatusCode < 500 {
+ if let decodedMessage = String(data: data, encoding: .ascii) {
+ message = decodedMessage
+ }
+ } else if let json = try? JSONSerialization.jsonObject(with: data, options: []),
+ let jsonDict = json as? [String: Any],
+ let errorMessage = jsonDict["error"] as? String
+ {
+ message = errorMessage
+ }
+
+ throw NSError(
+ domain: "QueueService",
+ code: actualStatusCode,
+ userInfo: [NSLocalizedDescriptionKey: message]
+ )
+ }
+ }
+}
+
+enum ApiClientError: Error {
+ case nilResponse
+}
diff --git a/Sources/QueueITLib/Constants.swift b/Sources/QueueITLib/Constants.swift
new file mode 100644
index 0000000..7f9e5b8
--- /dev/null
+++ b/Sources/QueueITLib/Constants.swift
@@ -0,0 +1,5 @@
+enum Constants {
+ static let queueCloseUrl = "queueit://close"
+ static let queueRestartSessionUrl = "queueit://restartSession"
+ static let sdkVersion = "iOS-3.4.4"
+}
diff --git a/Sources/QueueITLib/QueueITEngine.swift b/Sources/QueueITLib/QueueITEngine.swift
new file mode 100644
index 0000000..27bb65c
--- /dev/null
+++ b/Sources/QueueITLib/QueueITEngine.swift
@@ -0,0 +1,116 @@
+import UIKit
+
+public protocol QueueItEngineDelegate: AnyObject {
+ @MainActor func notifyQueuePassed(info: QueuePassedInfo?)
+ @MainActor func notifyQueueViewWillOpen()
+ @MainActor func notifyQueueDisabled(queueDisabledInfo: QueueDisabledInfo?)
+ @MainActor func notifyQueueUnavailable(errorMessage: String)
+ @MainActor func notifyQueueError(errorMessage: String, errorCode: Int)
+ @MainActor func notifyViewClosed()
+ @MainActor func notifyUserExited()
+ @MainActor func notifySessionRestart()
+ @MainActor func notifyQueueUrlChanged(url: String)
+ @MainActor func notifyQueueViewDidAppear()
+}
+
+@MainActor
+public final class QueueItEngine {
+ public weak var delegate: QueueItEngineDelegate?
+
+ public weak var host: UIViewController?
+ private var waitingRoomProvider: WaitingRoomProvider
+ private var waitingRoomView: WaitingRoomView
+
+ public init(host: UIViewController, customerId: String, eventOrAliasId: String, layoutName: String?, language: String?) {
+ self.host = host
+
+ waitingRoomProvider = WaitingRoomProvider(
+ customerId: customerId,
+ eventOrAliasId: eventOrAliasId,
+ layoutName: layoutName,
+ language: language
+ )
+ waitingRoomView = WaitingRoomView(host: host, eventId: eventOrAliasId)
+ waitingRoomView.delegate = self
+ waitingRoomProvider.delegate = self
+ }
+
+ public func setViewDelay(_ delayInterval: Int) {
+ waitingRoomView.setViewDelay(delayInterval)
+ }
+
+ public func isRequestInProgress() -> Bool {
+ return waitingRoomProvider.isRequestInProgress()
+ }
+
+ public func run(withEnqueueKey enqueueKey: String) async throws {
+ try await waitingRoomProvider.tryPassWithEnqueueKey(enqueueKey)
+ }
+
+ public func run(withEnqueueToken enqueueToken: String) async throws {
+ try await waitingRoomProvider.tryPassWithEnqueueToken(enqueueToken)
+ }
+
+ @MainActor
+ public func run() {
+ Task { @MainActor in
+ try await waitingRoomProvider.tryPass()
+ }
+ }
+
+ @MainActor public func showQueue(queueUrl: String, targetUrl: String) {
+ waitingRoomView.show(queueUrl: queueUrl, targetUrl: targetUrl)
+ }
+}
+
+extension QueueItEngine: WaitingRoomViewDelegate {
+ @MainActor public func notifyViewUserExited() {
+ delegate?.notifyUserExited()
+ }
+
+ @MainActor public func notifyViewUserClosed() {
+ delegate?.notifyViewClosed()
+ }
+
+ @MainActor public func notifyViewSessionRestart() {
+ delegate?.notifySessionRestart()
+ }
+
+ @MainActor public func notifyQueuePassed(info: QueuePassedInfo?) {
+ delegate?.notifyQueuePassed(info: info)
+ }
+
+ @MainActor public func notifyViewQueueDidAppear() {
+ delegate?.notifyQueueViewDidAppear()
+ }
+
+ @MainActor public func notifyViewQueueWillOpen() {
+ delegate?.notifyQueueViewWillOpen()
+ }
+
+ @MainActor public func notifyViewUpdatePageUrl(urlString: String) {
+ delegate?.notifyQueueUrlChanged(url: urlString)
+ }
+}
+
+extension QueueItEngine: WaitingRoomProviderDelegate {
+ @MainActor public func notifyProviderSuccess(queuePassResult: TryPassResult) async {
+ switch queuePassResult.redirectType {
+ case "safetynet":
+ let queuePassedInfo = QueuePassedInfo(queueitToken: queuePassResult.queueToken)
+ delegate?.notifyQueuePassed(info: queuePassedInfo)
+ case "disabled", "idle", "afterevent":
+ let queueDisabledInfo = QueueDisabledInfo(queueitToken: queuePassResult.queueToken)
+ delegate?.notifyQueueDisabled(queueDisabledInfo: queueDisabledInfo)
+ default:
+ showQueue(queueUrl: queuePassResult.queueUrl, targetUrl: queuePassResult.targetUrl)
+ }
+ }
+
+ @MainActor public func notifyProviderFailure(errorMessage: String, errorCode: Int) async {
+ if errorCode == 3 {
+ delegate?.notifyQueueUnavailable(errorMessage: errorMessage)
+ }
+ delegate?.notifyQueueError(errorMessage: errorMessage, errorCode: errorCode)
+ }
+}
diff --git a/Sources/QueueITLib/QueueInfo.swift b/Sources/QueueITLib/QueueInfo.swift
new file mode 100644
index 0000000..5411c03
--- /dev/null
+++ b/Sources/QueueITLib/QueueInfo.swift
@@ -0,0 +1,17 @@
+import Foundation
+
+public struct QueuePassedInfo {
+ public var queueitToken: String?
+
+ public init(queueitToken: String?) {
+ self.queueitToken = queueitToken
+ }
+}
+
+public struct QueueDisabledInfo {
+ public var queueitToken: String?
+
+ public init(queueitToken: String?) {
+ self.queueitToken = queueitToken
+ }
+}
diff --git a/Sources/QueueITLib/Reachability.swift b/Sources/QueueITLib/Reachability.swift
new file mode 100644
index 0000000..c1f5a85
--- /dev/null
+++ b/Sources/QueueITLib/Reachability.swift
@@ -0,0 +1,118 @@
+import Foundation
+import Network
+import SystemConfiguration
+
+
+extension Notification.Name {
+ static let reachabilityChanged = Notification.Name("kNetworkReachabilityChangedNotification")
+}
+
+final class Reachability {
+ private var monitor: NWPathMonitor?
+ private var isMonitoringLocalWiFi: Bool = false
+ private var queue = DispatchQueue.global(qos: .background)
+
+ private init() {}
+
+ static func reachabilityWithHostName(_ hostName: String) -> Reachability {
+ let reachability = Reachability()
+ reachability.startMonitoringHost(hostName: hostName)
+ return reachability
+ }
+
+ static func reachabilityWithAddress(_ hostAddress: sockaddr_in) -> Reachability {
+ let reachability = Reachability()
+ reachability.startMonitoringIP(address: hostAddress)
+ return reachability
+ }
+
+ static func reachabilityForInternetConnection() -> Reachability {
+ let reachability = Reachability()
+ reachability.startMonitoringInternet()
+ return reachability
+ }
+
+ static func reachabilityForLocalWiFi() -> Reachability {
+ let reachability = Reachability()
+ reachability.startMonitoringWiFi()
+ return reachability
+ }
+
+ func startNotifier() -> Bool {
+ guard monitor == nil else { return true }
+ monitor = NWPathMonitor()
+ monitor?.pathUpdateHandler = { _ in
+ NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
+ }
+ monitor?.start(queue: queue)
+ return true
+ }
+
+ func stopNotifier() {
+ monitor?.cancel()
+ monitor = nil
+ }
+
+ func currentReachabilityStatus() -> NetworkStatus {
+ guard let monitor = monitor else { return .notReachable }
+ let path = monitor.currentPath
+ if path.status == .unsatisfied {
+ return .notReachable
+ }
+ if path.usesInterfaceType(.wifi) {
+ return .reachableViaWiFi
+ }
+ if path.usesInterfaceType(.cellular) {
+ return .reachableViaWWAN
+ }
+ return .notReachable
+ }
+
+ func connectionRequired() -> Bool {
+ guard let monitor = monitor else { return false }
+ return monitor.currentPath.status != .satisfied
+ }
+}
+
+extension Reachability {
+ enum NetworkStatus: Int {
+ case notReachable = 0
+ case reachableViaWiFi
+ case reachableViaWWAN
+ }
+}
+
+private extension Reachability {
+ func startMonitoringHost(hostName _: String) {
+ monitor = NWPathMonitor(requiredInterfaceType: .other)
+ monitor?.pathUpdateHandler = { _ in
+ NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
+ }
+ monitor?.start(queue: queue)
+ }
+
+ func startMonitoringIP(address _: sockaddr_in) {
+ monitor = NWPathMonitor()
+ monitor?.pathUpdateHandler = { _ in
+ NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
+ }
+ monitor?.start(queue: queue)
+ }
+
+ func startMonitoringInternet() {
+ monitor = NWPathMonitor()
+ monitor?.pathUpdateHandler = { _ in
+ NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
+ }
+ monitor?.start(queue: queue)
+ }
+
+ func startMonitoringWiFi() {
+ isMonitoringLocalWiFi = true
+ monitor = NWPathMonitor(requiredInterfaceType: .wifi)
+ monitor?.pathUpdateHandler = { _ in
+ NotificationCenter.default.post(name: .reachabilityChanged, object: nil)
+ }
+ monitor?.start(queue: queue)
+ }
+}
diff --git a/Sources/QueueITLib/Status.swift b/Sources/QueueITLib/Status.swift
new file mode 100644
index 0000000..b811b4c
--- /dev/null
+++ b/Sources/QueueITLib/Status.swift
@@ -0,0 +1,31 @@
+struct Status: Sendable {
+ let queueId: String
+ let queueUrlString: String
+ let eventTargetUrl: String
+ let queueitToken: String
+
+ init(queueId: String, queueUrl: String, eventTargetUrl: String, queueitToken: String) {
+ self.queueId = queueId
+ self.queueUrlString = queueUrl
+ self.eventTargetUrl = eventTargetUrl
+ self.queueitToken = queueitToken
+ }
+
+ init(dictionary: [String: Any]) {
+ self.init(
+ queueId: dictionary[Constants.KEY_QUEUE_ID] as? String ?? "",
+ queueUrl: dictionary[Constants.KEY_QUEUE_URL] as? String ?? "",
+ eventTargetUrl: dictionary[Constants.KEY_EVENT_TARGET_URL] as? String ?? "",
+ queueitToken: dictionary[Constants.KEY_QUEUEIT_TOKEN] as? String ?? ""
+ )
+ }
+}
+
+private extension Status {
+ enum Constants {
+ static let KEY_QUEUE_ID = "QueueId"
+ static let KEY_QUEUE_URL = "QueueUrl"
+ static let KEY_EVENT_TARGET_URL = "EventTargetUrl"
+ static let KEY_QUEUEIT_TOKEN = "QueueitToken"
+ }
+}
diff --git a/Sources/QueueITLib/TryPassResult.swift b/Sources/QueueITLib/TryPassResult.swift
new file mode 100644
index 0000000..35c9fa6
--- /dev/null
+++ b/Sources/QueueITLib/TryPassResult.swift
@@ -0,0 +1,9 @@
+import Foundation
+
+public struct TryPassResult {
+ public let queueUrl: String
+ public let targetUrl: String
+ public let redirectType: String
+ public let isPassedThrough: Bool
+ public let queueToken: String
+}
diff --git a/Sources/QueueITLib/Utils.swift b/Sources/QueueITLib/Utils.swift
new file mode 100644
index 0000000..a7a29d0
--- /dev/null
+++ b/Sources/QueueITLib/Utils.swift
@@ -0,0 +1,40 @@
+import Foundation
+import WebKit
+
+enum Utils {
+ @MainActor static func getUserId() -> String {
+ let device = UIDevice()
+ if let deviceId = device.identifierForVendor {
+ return deviceId.uuidString
+ }
+ return ""
+ }
+
+ @MainActor static func getUserAgent() async -> String {
+ await withCheckedContinuation { continuation in
+ let view = WKWebView(frame: .zero)
+ view.evaluateJavaScript("navigator.userAgent") { result, error in
+ if let userAgent = result as? String, error == nil {
+ continuation.resume(returning: userAgent)
+ } else {
+ continuation.resume(returning: "")
+ }
+ }
+ }
+ }
+
+ static func getLibraryVersion() -> String {
+ if let infoDictionary = Bundle.main.infoDictionary,
+ let libName = infoDictionary[kCFBundleNameKey as String] as? String,
+ let major = infoDictionary["CFBundleShortVersionString"] as? String,
+ let minor = infoDictionary[kCFBundleVersionKey as String] as? String
+ {
+ return "\(libName)-\(major).\(minor)"
+ }
+ return ""
+ }
+
+ static func getSdkVersion() -> String {
+ return Constants.sdkVersion
+ }
+}
diff --git a/Sources/QueueITLib/WaitingRoomProvider.swift b/Sources/QueueITLib/WaitingRoomProvider.swift
new file mode 100644
index 0000000..c2bc289
--- /dev/null
+++ b/Sources/QueueITLib/WaitingRoomProvider.swift
@@ -0,0 +1,192 @@
+import Foundation
+
+@MainActor
+public protocol WaitingRoomProviderDelegate: AnyObject {
+ func notifyProviderSuccess(queuePassResult: TryPassResult) async
+ func notifyProviderFailure(errorMessage: String, errorCode: Int) async
+}
+
+enum QueueITRuntimeError: Int {
+ case networkUnavailable = -100
+ case requestAlreadyInProgress = 10
+
+ static let errorMessages = [
+ networkUnavailable: "Network connection is unavailable",
+ requestAlreadyInProgress: "Enqueue request is already in progress",
+ ]
+}
+
+@MainActor
+public final class WaitingRoomProvider {
+ static let maxRetrySec = 10
+ static let initialWaitRetrySec = 1
+
+ public weak var delegate: WaitingRoomProviderDelegate?
+
+ private let customerId: String
+ private let eventOrAliasId: String
+ private let layoutName: String?
+ private let language: String?
+
+ private var apiClient: ApiClient?
+ private var deltaSec: Int = WaitingRoomProvider.initialWaitRetrySec
+ private var requestInProgress: Bool = false
+ private let internetReachability: Reachability
+
+ public init(customerId: String, eventOrAliasId: String, layoutName: String? = nil, language: String? = nil) {
+ self.customerId = customerId
+ self.eventOrAliasId = eventOrAliasId
+ self.layoutName = layoutName
+ self.language = language
+ internetReachability = Reachability.reachabilityForInternetConnection()
+ }
+
+ @MainActor
+ public func tryPass() {
+ Task {
+ try await tryEnqueue(enqueueToken: nil, enqueueKey: nil)
+ }
+ }
+
+ public func tryPassWithEnqueueToken(_ enqueueToken: String?) async throws {
+ try await tryEnqueue(enqueueToken: enqueueToken, enqueueKey: nil)
+ }
+
+ public func tryPassWithEnqueueKey(_ enqueueKey: String?) async throws {
+ try await tryEnqueue(enqueueToken: nil, enqueueKey: enqueueKey)
+ }
+
+ public func isRequestInProgress() -> Bool {
+ return requestInProgress
+ }
+}
+
+private extension WaitingRoomProvider {
+ func tryEnqueue(enqueueToken: String?, enqueueKey: String?) async throws {
+ guard checkConnection() else {
+ throw NSError(
+ domain: "QueueITRuntimeException",
+ code: QueueITRuntimeError.networkUnavailable.rawValue,
+ userInfo: nil
+ )
+ }
+
+ if requestInProgress {
+ throw NSError(
+ domain: "QueueITRuntimeException",
+ code: QueueITRuntimeError.requestAlreadyInProgress.rawValue,
+ userInfo: nil
+ )
+ }
+
+ requestInProgress = true
+
+ let userAgent = await Utils.getUserAgent()
+ do {
+ try await self.tryEnqueueWithUserAgent(
+ secretAgent: userAgent,
+ enqueueToken: enqueueToken,
+ enqueueKey: enqueueKey
+ )
+ } catch {
+ self.requestInProgress = false
+ await delegate?.notifyProviderFailure(
+ errorMessage: error.localizedDescription,
+ errorCode: (error as NSError).code
+ )
+ }
+ }
+
+ func tryEnqueueWithUserAgent(secretAgent: String, enqueueToken: String?, enqueueKey: String?) async throws {
+ let userId = Utils.getUserId()
+ let userAgent = "\(secretAgent);\(Utils.getLibraryVersion())"
+ let sdkVersion = Utils.getSdkVersion()
+ apiClient = ApiClient()
+
+ do {
+ let status = try await apiClient?.enqueue(
+ customerId: customerId,
+ eventOrAliasId: eventOrAliasId,
+ userId: userId,
+ userAgent: userAgent,
+ sdkVersion: sdkVersion,
+ layoutName: layoutName,
+ language: language,
+ enqueueToken: enqueueToken,
+ enqueueKey: enqueueKey
+ )
+ guard let status else {
+ await self.enqueueRetryMonitor(enqueueToken: enqueueToken, enqueueKey: enqueueKey)
+ return
+ }
+
+ await self.handleAppEnqueueResponse(
+ queueURL: status.queueUrlString,
+ eventTargetURL: status.eventTargetUrl,
+ queueItToken: status.queueitToken
+ )
+ self.requestInProgress = false
+ } catch {
+ let nsError = error as NSError
+ if nsError.code >= 400, nsError.code < 500 {
+ await self.delegate?.notifyProviderFailure(errorMessage: error.localizedDescription, errorCode: nsError.code)
+ } else {
+ await self.enqueueRetryMonitor(enqueueToken: enqueueToken, enqueueKey: enqueueKey)
+ }
+ }
+ }
+
+ func handleAppEnqueueResponse(
+ queueURL: String,
+ eventTargetURL: String,
+ queueItToken: String
+ ) async {
+ let isPassedThrough = !queueItToken.isEmpty
+ let redirectType = getRedirectType(fromToken: queueItToken)
+
+ let tryPassResult = TryPassResult(
+ queueUrl: queueURL,
+ targetUrl: eventTargetURL,
+ redirectType: redirectType,
+ isPassedThrough: isPassedThrough,
+ queueToken: queueItToken
+ )
+ await delegate?.notifyProviderSuccess(queuePassResult: tryPassResult)
+ }
+
+ func enqueueRetryMonitor(enqueueToken: String?, enqueueKey: String?) async {
+ if deltaSec < WaitingRoomProvider.maxRetrySec {
+ try? await tryEnqueue(enqueueToken: enqueueToken, enqueueKey: enqueueKey)
+ try? await Task.sleep(nanoseconds: UInt64(deltaSec * 1_000_000_000))
+ deltaSec *= 2
+ } else {
+ deltaSec = WaitingRoomProvider.initialWaitRetrySec
+ requestInProgress = false
+ await delegate?.notifyProviderFailure(errorMessage: "Error! Queue is unavailable.", errorCode: 3)
+ }
+ }
+
+ func checkConnection() -> Bool {
+ for _ in 0 ..< 5 {
+ if internetReachability.currentReachabilityStatus() != .notReachable {
+ return true
+ }
+ Thread.sleep(forTimeInterval: 1.0)
+ }
+ return false
+ }
+
+ func getRedirectType(fromToken queueToken: String?) -> String {
+ guard let token = queueToken, !token.isEmpty else {
+ return "queue"
+ }
+
+ let pattern = "\\~rt_(.*?)\\~"
+ if let regex = try? NSRegularExpression(pattern: pattern),
+ let match = regex.firstMatch(in: token, range: NSRange(token.startIndex..., in: token))
+ {
+ return String(token[Range(match.range(at: 1), in: token)!])
+ }
+ return "queue"
+ }
+}
diff --git a/Sources/QueueITLib/WaitingRoomView.swift b/Sources/QueueITLib/WaitingRoomView.swift
new file mode 100644
index 0000000..e06c54c
--- /dev/null
+++ b/Sources/QueueITLib/WaitingRoomView.swift
@@ -0,0 +1,100 @@
+import UIKit
+
+public protocol WaitingRoomViewDelegate: AnyObject {
+ @MainActor func notifyViewUserExited()
+ @MainActor func notifyViewUserClosed()
+ @MainActor func notifyViewSessionRestart()
+ @MainActor func notifyQueuePassed(info: QueuePassedInfo?)
+ @MainActor func notifyViewQueueDidAppear()
+ @MainActor func notifyViewQueueWillOpen()
+ @MainActor func notifyViewUpdatePageUrl(urlString: String)
+}
+
+public final class WaitingRoomView {
+ weak var host: UIViewController?
+ public weak var delegate: WaitingRoomViewDelegate?
+ weak var currentWebView: WebViewController?
+
+ private var eventId: String
+ private var delayInterval: Int = 0
+
+ public init(host: UIViewController, eventId: String) {
+ self.host = host
+ self.eventId = eventId
+ }
+
+ @MainActor public func show(queueUrl: String, targetUrl: String) {
+ raiseQueueViewWillOpen()
+
+ let queueWKVC = WebViewController(
+ queueUrl: queueUrl,
+ eventTargetUrl: targetUrl,
+ eventId: eventId
+ )
+
+ queueWKVC.delegate = self
+
+ if #available(iOS 13.0, *) {
+ queueWKVC.modalPresentationStyle = UIModalPresentationStyle.pageSheet
+ }
+
+ DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(delayInterval)) { [weak self] in
+ guard let self else {
+ return
+ }
+ self.host?.present(queueWKVC, animated: true, completion: { [weak self] in
+ guard let self else {
+ return
+ }
+ self.currentWebView = queueWKVC
+ self.currentWebView?.loadWebView()
+ self.delegate?.notifyViewQueueDidAppear()
+ })
+ }
+ }
+
+ public func setViewDelay(_ delayInterval: Int) {
+ self.delayInterval = delayInterval
+ }
+}
+
+extension WaitingRoomView: WebViewControllerDelegate {
+ @MainActor func notifyViewControllerClosed() {
+ delegate?.notifyViewUserClosed()
+ close()
+ }
+
+ @MainActor func notifyViewControllerUserExited() {
+ delegate?.notifyViewUserExited()
+ }
+
+ @MainActor func notifyViewControllerSessionRestart() {
+ delegate?.notifyViewSessionRestart()
+ close()
+ }
+
+ @MainActor func notifyViewControllerQueuePassed(queueToken: String?) {
+ let queuePassedInfo = QueuePassedInfo(queueitToken: queueToken)
+ delegate?.notifyQueuePassed(info: queuePassedInfo)
+ close()
+ }
+
+ @MainActor func notifyViewControllerPageUrlChanged(urlString: String) {
+ delegate?.notifyViewUpdatePageUrl(urlString: urlString)
+ }
+}
+
+private extension WaitingRoomView {
+ @MainActor func close(onComplete: (() -> Void)? = nil) {
+ DispatchQueue.main.async { [weak self] in
+ guard let self, let host else {
+ return
+ }
+ host.dismiss(animated: true, completion: onComplete)
+ }
+ }
+
+ @MainActor func raiseQueueViewWillOpen() {
+ delegate?.notifyViewQueueWillOpen()
+ }
+}
diff --git a/Sources/QueueITLib/WebViewController.swift b/Sources/QueueITLib/WebViewController.swift
new file mode 100644
index 0000000..a648780
--- /dev/null
+++ b/Sources/QueueITLib/WebViewController.swift
@@ -0,0 +1,208 @@
+import UIKit
+@preconcurrency import WebKit
+
+protocol WebViewControllerDelegate: AnyObject {
+ @MainActor func notifyViewControllerClosed()
+ @MainActor func notifyViewControllerUserExited()
+ @MainActor func notifyViewControllerSessionRestart()
+ @MainActor func notifyViewControllerQueuePassed(queueToken: String?)
+ @MainActor func notifyViewControllerPageUrlChanged(urlString: String)
+}
+
+final class WebViewController: UIViewController {
+ weak var delegate: WebViewControllerDelegate?
+
+ private var webView: WKWebView?
+ private var spinner: UIActivityIndicatorView?
+ private var closeButton: UIButton?
+ private var isQueuePassed: Bool
+
+ private var queueUrl: String
+ private var eventTargetUrl: String
+ private var eventId: String
+
+ private let JAVASCRIPT_GET_BODY_CLASSES = "document.getElementsByTagName('body')[0].className"
+
+ init(queueUrl: String, eventTargetUrl: String, eventId: String) {
+ self.queueUrl = queueUrl
+ self.eventTargetUrl = eventTargetUrl
+ self.eventId = eventId
+ isQueuePassed = false
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ @available(*, unavailable)
+ required init?(coder _: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ let preferences = WKPreferences()
+ preferences.javaScriptEnabled = true
+
+ let config = WKWebViewConfiguration()
+ config.preferences = preferences
+
+ spinner = UIActivityIndicatorView(frame: view.bounds)
+ webView = WKWebView(frame: view.bounds, configuration: config)
+
+ closeButton = UIButton(type: .system)
+ closeButton?.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal)
+ closeButton?.addTarget(self, action: #selector(closeTapped), for: .touchUpInside)
+ closeButton?.translatesAutoresizingMaskIntoConstraints = false
+ closeButton?.tintColor = .white
+
+ guard let spinner, let webView, let closeButton else {
+ return
+ }
+
+ spinner.color = .gray
+ webView.navigationDelegate = self
+ webView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
+ webView.isOpaque = false
+ webView.backgroundColor = .white
+
+ view.addSubview(webView)
+ view.addSubview(spinner)
+ view.addSubview(closeButton)
+
+ webView.frame = view.bounds
+ spinner.frame = view.bounds
+ NSLayoutConstraint.activate([
+ closeButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
+ closeButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8),
+ closeButton.widthAnchor.constraint(equalToConstant: 44),
+ closeButton.heightAnchor.constraint(equalToConstant: 44)
+ ])
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ self.delegate?.notifyViewControllerClosed()
+ }
+
+ func loadWebView() {
+ guard let spinner,
+ let webView,
+ let url = URL(string: queueUrl)
+ else {
+ return
+ }
+ spinner.startAnimating()
+ webView.load(URLRequest(url: url))
+ }
+}
+
+extension WebViewController: WKNavigationDelegate {
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences) async -> (WKNavigationActionPolicy, WKWebpagePreferences) {
+ if !isQueuePassed {
+ let request = navigationAction.request
+ let urlString = request.url?.absoluteString
+ let targetUrlString = eventTargetUrl
+ if let urlString, urlString != "about:blank" {
+ let url = URL(string: urlString)!
+ let targetUrl = URL(string: targetUrlString)!
+ let isQueueUrl = queueUrl.contains(url.host!)
+ let isNotFrame = request.url?.absoluteString == request.mainDocumentURL?.absoluteString
+
+
+ if url.absoluteString == Constants.queueCloseUrl {
+ delegate?.notifyViewControllerClosed()
+ return (.cancel, preferences)
+ } else if url.absoluteString == Constants.queueRestartSessionUrl {
+ delegate?.notifyViewControllerSessionRestart()
+ return (.cancel, preferences)
+ }
+
+ if isBlockedUrl(destinationUrl: url) {
+ return (.cancel, preferences)
+ }
+
+ if isNotFrame {
+ if isQueueUrl {
+ raiseQueuePageUrl(urlString)
+ }
+ if isTargetUrl(targetUrl: targetUrl, destinationUrl: url) {
+ isQueuePassed = true
+ let queueitToken = extractQueueToken(urlString)
+ delegate?.notifyViewControllerQueuePassed(queueToken: queueitToken)
+ return (.cancel, preferences)
+ }
+ }
+
+ if navigationAction.navigationType == .linkActivated && !isQueueUrl {
+ await UIApplication.shared.open(request.url!)
+ return (.cancel, preferences)
+ }
+ }
+ }
+
+ return (.allow, preferences)
+ }
+
+ func webView(_: WKWebView, didStartProvisionalNavigation _: WKNavigation!) {}
+
+ func webView(_: WKWebView, didFinish _: WKNavigation!) {
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(appWillResignActive(_:)),
+ name: UIApplication.willResignActiveNotification,
+ object: nil
+ )
+
+ guard let spinner, let webView else {
+ return
+ }
+
+ spinner.stopAnimating()
+ webView.evaluateJavaScript(JAVASCRIPT_GET_BODY_CLASSES) { [weak self] result, error in
+ guard let self else {
+ return
+ }
+ if let error {
+ print("evaluateJavaScript error: \(error.localizedDescription)")
+ } else if let resultString = result as? String {
+ let htmlBodyClasses = resultString.split(separator: " ")
+ let isExitClassPresent = htmlBodyClasses.contains("exit")
+ if isExitClassPresent {
+ self.delegate?.notifyViewControllerUserExited()
+ }
+ }
+ }
+ }
+}
+
+private extension WebViewController {
+ func isTargetUrl(targetUrl: URL, destinationUrl: URL) -> Bool {
+ return destinationUrl.host == targetUrl.host && destinationUrl.path == targetUrl.path
+ }
+
+ func isBlockedUrl(destinationUrl: URL) -> Bool {
+ return destinationUrl.path.hasPrefix("/what-is-this.html")
+ }
+
+ func extractQueueToken(_ url: String) -> String? {
+ let tokenKey = "queueittoken="
+ if let range = url.range(of: tokenKey) {
+ var token = String(url[range.upperBound...])
+ if let ampersandRange = token.range(of: "&") {
+ token = String(token[..