From d7600f58898b3980d56cb4b58c97245b93e0dc5c Mon Sep 17 00:00:00 2001 From: alexanderthoren Date: Wed, 11 Dec 2024 19:18:12 +0100 Subject: [PATCH 1/3] feat: migrate QueueITLib to Swift - Modified .gitignore and Package.swift - Deleted old Objective-C files from QueueITLib - Added new Swift files to Sources/QueueITLib --- .gitignore | 3 +- Package.swift | 10 +- QueueITLib/IOSUtils.h | 12 - QueueITLib/IOSUtils.m | 55 ---- QueueITLib/PrivacyInfo.xcprivacy | 35 --- QueueITLib/QueueConsts.h | 8 - QueueITLib/QueueDisabledInfo.h | 9 - QueueITLib/QueueDisabledInfo.m | 14 - QueueITLib/QueueITApiClient.h | 24 -- QueueITLib/QueueITApiClient.m | 118 ------- QueueITLib/QueueITApiClient_NSURLConnection.h | 6 - QueueITLib/QueueITApiClient_NSURLConnection.m | 46 --- .../QueueITApiClient_NSURLConnectionRequest.h | 20 -- .../QueueITApiClient_NSURLConnectionRequest.m | 133 -------- QueueITLib/QueueITEngine.h | 98 ------ QueueITLib/QueueITEngine.m | 125 -------- QueueITLib/QueueITReachability.h | 100 ------ QueueITLib/QueueITReachability.m | 297 ------------------ QueueITLib/QueueITWKViewController.h | 30 -- QueueITLib/QueueITWKViewController.m | 235 -------------- QueueITLib/QueueITWaitingRoomProvider.h | 34 -- QueueITLib/QueueITWaitingRoomProvider.m | 207 ------------ QueueITLib/QueueITWaitingRoomView.h | 29 -- QueueITLib/QueueITWaitingRoomView.m | 100 ------ QueueITLib/QueuePassedInfo.h | 9 - QueueITLib/QueuePassedInfo.m | 14 - QueueITLib/QueueStatus.h | 12 - QueueITLib/QueueStatus.m | 59 ---- QueueITLib/QueueTryPassResult.h | 19 -- QueueITLib/QueueTryPassResult.m | 24 -- Sources/QueueITLib/NewConnection.swift | 44 +++ Sources/QueueITLib/NewConnectionRequest.swift | 97 ++++++ Sources/QueueITLib/NewIOSUtils.swift | 40 +++ Sources/QueueITLib/NewQueueConsts.swift | 5 + Sources/QueueITLib/NewQueueITApiClient.swift | 122 +++++++ Sources/QueueITLib/NewQueueITEngine.swift | 152 +++++++++ .../QueueITLib/NewQueueITReachability.swift | 118 +++++++ .../NewQueueITWKViewController.swift | 193 ++++++++++++ .../NewQueueITWaitingRoomProvider.swift | 199 ++++++++++++ .../NewQueueITWaitingRoomView.swift | 100 ++++++ Sources/QueueITLib/NewQueueInfo.swift | 17 + Sources/QueueITLib/NewQueueStatus.swift | 31 ++ .../QueueITLib/NewQueueTryPassResult.swift | 9 + 43 files changed, 1134 insertions(+), 1878 deletions(-) delete mode 100644 QueueITLib/IOSUtils.h delete mode 100644 QueueITLib/IOSUtils.m delete mode 100644 QueueITLib/PrivacyInfo.xcprivacy delete mode 100644 QueueITLib/QueueConsts.h delete mode 100644 QueueITLib/QueueDisabledInfo.h delete mode 100644 QueueITLib/QueueDisabledInfo.m delete mode 100644 QueueITLib/QueueITApiClient.h delete mode 100644 QueueITLib/QueueITApiClient.m delete mode 100644 QueueITLib/QueueITApiClient_NSURLConnection.h delete mode 100644 QueueITLib/QueueITApiClient_NSURLConnection.m delete mode 100644 QueueITLib/QueueITApiClient_NSURLConnectionRequest.h delete mode 100644 QueueITLib/QueueITApiClient_NSURLConnectionRequest.m delete mode 100644 QueueITLib/QueueITEngine.h delete mode 100644 QueueITLib/QueueITEngine.m delete mode 100644 QueueITLib/QueueITReachability.h delete mode 100644 QueueITLib/QueueITReachability.m delete mode 100644 QueueITLib/QueueITWKViewController.h delete mode 100644 QueueITLib/QueueITWKViewController.m delete mode 100644 QueueITLib/QueueITWaitingRoomProvider.h delete mode 100644 QueueITLib/QueueITWaitingRoomProvider.m delete mode 100644 QueueITLib/QueueITWaitingRoomView.h delete mode 100644 QueueITLib/QueueITWaitingRoomView.m delete mode 100644 QueueITLib/QueuePassedInfo.h delete mode 100644 QueueITLib/QueuePassedInfo.m delete mode 100644 QueueITLib/QueueStatus.h delete mode 100644 QueueITLib/QueueStatus.m delete mode 100644 QueueITLib/QueueTryPassResult.h delete mode 100644 QueueITLib/QueueTryPassResult.m create mode 100644 Sources/QueueITLib/NewConnection.swift create mode 100644 Sources/QueueITLib/NewConnectionRequest.swift create mode 100644 Sources/QueueITLib/NewIOSUtils.swift create mode 100644 Sources/QueueITLib/NewQueueConsts.swift create mode 100644 Sources/QueueITLib/NewQueueITApiClient.swift create mode 100644 Sources/QueueITLib/NewQueueITEngine.swift create mode 100644 Sources/QueueITLib/NewQueueITReachability.swift create mode 100644 Sources/QueueITLib/NewQueueITWKViewController.swift create mode 100644 Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift create mode 100644 Sources/QueueITLib/NewQueueITWaitingRoomView.swift create mode 100644 Sources/QueueITLib/NewQueueInfo.swift create mode 100644 Sources/QueueITLib/NewQueueStatus.swift create mode 100644 Sources/QueueITLib/NewQueueTryPassResult.swift diff --git a/.gitignore b/.gitignore index d5796ae..abca1e5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ xcuserdata/ /.idea build QueueITLibSwift.xcframework.zip -.DS_Store \ No newline at end of file +.DS_Store +.index-build diff --git a/Package.swift b/Package.swift index db710e0..0a4b44b 100644 --- a/Package.swift +++ b/Package.swift @@ -6,18 +6,18 @@ import PackageDescription let package = Package( name: "QueueITLibrary", platforms: [ - .iOS(.v11) + .iOS(.v11), ], 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/Sources/QueueITLib/NewConnection.swift b/Sources/QueueITLib/NewConnection.swift new file mode 100644 index 0000000..ce96456 --- /dev/null +++ b/Sources/QueueITLib/NewConnection.swift @@ -0,0 +1,44 @@ +import Foundation + +final class Connection: QueueITApiClient { + private var connectionRequest: ConnectionRequest? + + override func submitRequest( + with url: URL, + method httpMethod: String, + body bodyDict: [String: Any], + expectedStatus: Int, + success: @escaping QueueServiceSuccess, + failure: @escaping QueueServiceFailure + ) -> String { + var request = URLRequest(url: url) + request.httpMethod = httpMethod + + do { + let jsonData = try JSONSerialization.data(withJSONObject: bodyDict, options: []) + request.httpBody = jsonData + } catch { + failure(error, "Failed to serialize request body.") + return "" + } + + request.addValue("application/json", forHTTPHeaderField: "Accept") + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + connectionRequest = ConnectionRequest( + request: request, + expectedStatusCode: expectedStatus, + success: success, + failure: failure, + delegate: self + ) + + return connectionRequest?.uniqueIdentifier ?? "" + } +} + +extension Connection: ConnectionRequestDelegate { + func requestDidComplete(_: ConnectionRequest) { + // Handle the completion of the request if needed + } +} diff --git a/Sources/QueueITLib/NewConnectionRequest.swift b/Sources/QueueITLib/NewConnectionRequest.swift new file mode 100644 index 0000000..08c2128 --- /dev/null +++ b/Sources/QueueITLib/NewConnectionRequest.swift @@ -0,0 +1,97 @@ +import Foundation + +protocol ConnectionRequestDelegate: AnyObject { + func requestDidComplete(_ request: ConnectionRequest) +} + +final class ConnectionRequest { + let uniqueIdentifier: String + + private var request: URLRequest + private var response: URLResponse? + private var data: Data + private var successCallback: QueueServiceSuccess + private var failureCallback: QueueServiceFailure + private weak var delegate: ConnectionRequestDelegate? + private var expectedStatusCode: Int + private var actualStatusCode: Int = NSNotFound + + init( + request: URLRequest, + expectedStatusCode: Int, + success: @escaping QueueServiceSuccess, + failure: @escaping QueueServiceFailure, + delegate: ConnectionRequestDelegate? + ) { + self.request = request + self.expectedStatusCode = expectedStatusCode + successCallback = success + failureCallback = failure + self.delegate = delegate + uniqueIdentifier = UUID().uuidString + data = Data() + initiateRequest() + } +} + +private extension ConnectionRequest { + func initiateRequest() { + response = nil + data = Data() + + let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in + guard let self else { return } + + if let error = error { + DispatchQueue.main.async { + self.failureCallback(error, "Unexpected failure occurred.") + self.delegate?.requestDidComplete(self) + } + return + } + + if let response = response as? HTTPURLResponse { + self.actualStatusCode = response.statusCode + self.response = response + } + + if let receivedData = data { + self.data.append(receivedData) + } + + DispatchQueue.main.async { + self.handleResponse() + } + } + task.resume() + } + + func handleResponse() { + if hasExpectedStatusCode() { + successCallback(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 + } + + let error = NSError(domain: "QueueService", + code: actualStatusCode, + userInfo: [NSLocalizedDescriptionKey: message]) + failureCallback(error, message) + } + + delegate?.requestDidComplete(self) + } + + func hasExpectedStatusCode() -> Bool { + return actualStatusCode == expectedStatusCode + } +} diff --git a/Sources/QueueITLib/NewIOSUtils.swift b/Sources/QueueITLib/NewIOSUtils.swift new file mode 100644 index 0000000..1b15bf6 --- /dev/null +++ b/Sources/QueueITLib/NewIOSUtils.swift @@ -0,0 +1,40 @@ +import Foundation +import WebKit + +enum IOSUtils { + static func getUserId() -> String { + let device = UIDevice() + if let deviceId = device.identifierForVendor { + return deviceId.uuidString + } + return "" + } + + static func getUserAgent(completionHandler: @escaping (String) -> Void) { + DispatchQueue.main.async { + let view = WKWebView(frame: .zero) + view.evaluateJavaScript("navigator.userAgent") { result, error in + if let userAgent = result as? String, error == nil { + completionHandler(userAgent) + } else { + completionHandler("") + } + } + } + } + + 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 QueueConsts.sdkVersion + } +} diff --git a/Sources/QueueITLib/NewQueueConsts.swift b/Sources/QueueITLib/NewQueueConsts.swift new file mode 100644 index 0000000..f2f6e7e --- /dev/null +++ b/Sources/QueueITLib/NewQueueConsts.swift @@ -0,0 +1,5 @@ +enum QueueConsts { + static let queueCloseUrl = "queueit://close" + static let queueRestartSessionUrl = "queueit://restartSession" + static let sdkVersion = "iOS-3.4.4" +} diff --git a/Sources/QueueITLib/NewQueueITApiClient.swift b/Sources/QueueITLib/NewQueueITApiClient.swift new file mode 100644 index 0000000..33e6cd9 --- /dev/null +++ b/Sources/QueueITLib/NewQueueITApiClient.swift @@ -0,0 +1,122 @@ +import Foundation + +typealias QueueServiceSuccess = (Data) -> Void +typealias QueueServiceFailure = (Error, String) -> Void + +class QueueITApiClient { + 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 static var testingIsEnabled = false + private static var sharedInstance: QueueITApiClient? + + static func getInstance() -> QueueITApiClient { + if sharedInstance == nil { + sharedInstance = Connection() + } + return sharedInstance! + } + + static 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?, + success: @escaping (QueueStatus?) -> Void, + failure: @escaping QueueServiceFailure + ) -> String { + 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 = QueueITApiClient.testingIsEnabled ? QueueITApiClient.TESTING_API_ROOT : QueueITApiClient.API_ROOT + var urlAsString = String(format: apiRoot, customerId) + urlAsString += "/\(customerId)" + urlAsString += "/\(eventOrAliasId)" + urlAsString += "/enqueue" + + return submitPOSTPath( + path: urlAsString, + body: bodyDict, + success: { data in + do { + if let userDict = try JSONSerialization.jsonObject( + with: data, + options: [] + ) as? [String: Any] { + let queueStatus = QueueStatus(dictionary: userDict) + success(queueStatus) + } else { + success(nil) + } + } catch { + success(nil) + } + }, + failure: failure + ) + } + + func submitPOSTPath( + path: String, + body bodyDict: [String: Any], + success: @escaping QueueServiceSuccess, + failure: @escaping QueueServiceFailure + ) -> String { + guard let url = URL(string: path) else { + let error = NSError( + domain: "QueueITApiClient", + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Invalid URL"] + ) + failure(error, "Invalid URL") + return "" + } + + return submitRequest( + with: url, + method: "POST", + body: bodyDict, + expectedStatus: 200, + success: success, + failure: failure + ) + } + + func submitRequest( + with _: URL, + method _: String, + body _: [String: Any], + expectedStatus _: Int, + success _: @escaping QueueServiceSuccess, + failure _: @escaping QueueServiceFailure + ) -> String { + return "" + } +} diff --git a/Sources/QueueITLib/NewQueueITEngine.swift b/Sources/QueueITLib/NewQueueITEngine.swift new file mode 100644 index 0000000..6f320be --- /dev/null +++ b/Sources/QueueITLib/NewQueueITEngine.swift @@ -0,0 +1,152 @@ +import UIKit + +public protocol QueuePassedDelegate: AnyObject { + func notifyYourTurn(queuePassedInfo: QueuePassedInfo?) +} + +public protocol QueueViewWillOpenDelegate: AnyObject { + func notifyQueueViewWillOpen() +} + +public protocol QueueDisabledDelegate: AnyObject { + func notifyQueueDisabled(queueDisabledInfo: QueueDisabledInfo?) +} + +public protocol QueueUnavailableDelegate: AnyObject { + func notifyQueueITUnavailable(errorMessage: String) +} + +public protocol QueueErrorDelegate: AnyObject { + func notifyQueueError(errorMessage: String, errorCode: Int) +} + +public protocol QueueViewClosedDelegate: AnyObject { + func notifyViewClosed() +} + +public protocol QueueUserExitedDelegate: AnyObject { + func notifyUserExited() +} + +public protocol QueueSessionRestartDelegate: AnyObject { + func notifySessionRestart() +} + +public protocol QueueUrlChangedDelegate: AnyObject { + func notifyQueueUrlChanged(url: String) +} + +public protocol QueueViewDidAppearDelegate: AnyObject { + func notifyQueueViewDidAppear() +} + +public final class QueueItEngine { + public weak var queuePassedDelegate: QueuePassedDelegate? + public weak var queueViewWillOpenDelegate: QueueViewWillOpenDelegate? + public weak var queueDisabledDelegate: QueueDisabledDelegate? + public weak var queueUnavailableDelegate: QueueUnavailableDelegate? + public weak var queueErrorDelegate: QueueErrorDelegate? + public weak var queueViewClosedDelegate: QueueViewClosedDelegate? + public weak var queueUserExitedDelegate: QueueUserExitedDelegate? + public weak var queueSessionRestartDelegate: QueueSessionRestartDelegate? + public weak var queueUrlChangedDelegate: QueueUrlChangedDelegate? + public weak var queueViewDidAppearDelegate: QueueViewDidAppearDelegate? + + public weak var host: UIViewController? + private var waitingRoomProvider: QueueITWaitingRoomProvider + private var waitingRoomView: QueueITWaitingRoomView + + public init(host: UIViewController, customerId: String, eventOrAliasId: String, layoutName: String?, language: String?) { + self.host = host + + waitingRoomProvider = QueueITWaitingRoomProvider( + customerId: customerId, + eventOrAliasId: eventOrAliasId, + layoutName: layoutName, + language: language + ) + waitingRoomView = QueueITWaitingRoomView(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) throws -> Bool { + return try waitingRoomProvider.tryPassWithEnqueueKey(enqueueKey) + } + + public func run(withEnqueueToken enqueueToken: String) throws -> Bool { + return try waitingRoomProvider.tryPassWithEnqueueToken(enqueueToken) + } + + public func run() throws -> Bool { + return try waitingRoomProvider.tryPass() + } + + public func showQueue(queueUrl: String, targetUrl: String) { + waitingRoomView.show(queueUrl: queueUrl, targetUrl: targetUrl) + } +} + +extension QueueItEngine: QueueITWaitingRoomViewDelegate { + func notifyViewUserExited() { + queueUserExitedDelegate?.notifyUserExited() + } + + func notifyViewUserClosed() { + queueViewClosedDelegate?.notifyViewClosed() + } + + func notifyViewSessionRestart() { + queueSessionRestartDelegate?.notifySessionRestart() + } + + func notifyQueuePassed(info: QueuePassedInfo?) { + queuePassedDelegate?.notifyYourTurn(queuePassedInfo: info) + } + + func notifyViewQueueDidAppear() { + queueViewDidAppearDelegate?.notifyQueueViewDidAppear() + } + + func notifyViewQueueWillOpen() { + queueViewWillOpenDelegate?.notifyQueueViewWillOpen() + } + + func notifyViewUpdatePageUrl(urlString: String?) { + // TODO: fix optional parameter + queueUrlChangedDelegate?.notifyQueueUrlChanged(url: urlString ?? "") + } +} + +extension QueueItEngine: QueueITWaitingRoomProviderDelegate { + func notifyProviderSuccess(queuePassResult: QueueTryPassResult) { + switch queuePassResult.redirectType { + case "safetynet": + let queuePassedInfo = QueuePassedInfo(queueitToken: queuePassResult.queueToken) + queuePassedDelegate?.notifyYourTurn(queuePassedInfo: queuePassedInfo) + case "disabled", "idle", "afterevent": + let queueDisabledInfo = QueueDisabledInfo(queueitToken: queuePassResult.queueToken) + queueDisabledDelegate?.notifyQueueDisabled(queueDisabledInfo: queueDisabledInfo) + default: + // TODO: fix optional parameter + showQueue(queueUrl: queuePassResult.queueUrl ?? "", targetUrl: queuePassResult.targetUrl ?? "") + } + } + + func notifyProviderFailure(errorMessage: String?, errorCode: Int) { + // TODO: fix optional parameter + let errorMessage = errorMessage ?? "" + if errorCode == 3 { + queueUnavailableDelegate?.notifyQueueITUnavailable(errorMessage: errorMessage) + } + queueErrorDelegate?.notifyQueueError(errorMessage: errorMessage, errorCode: errorCode) + } +} diff --git a/Sources/QueueITLib/NewQueueITReachability.swift b/Sources/QueueITLib/NewQueueITReachability.swift new file mode 100644 index 0000000..3771353 --- /dev/null +++ b/Sources/QueueITLib/NewQueueITReachability.swift @@ -0,0 +1,118 @@ +import Foundation +import Network +import SystemConfiguration + + +extension Notification.Name { + static let reachabilityChanged = Notification.Name("kNetworkReachabilityChangedNotification") +} + +final class QueueITReachability { + private var monitor: NWPathMonitor? + private var isMonitoringLocalWiFi: Bool = false + private var queue = DispatchQueue.global(qos: .background) + + private init() {} + + static func reachabilityWithHostName(_ hostName: String) -> QueueITReachability { + let reachability = QueueITReachability() + reachability.startMonitoringHost(hostName: hostName) + return reachability + } + + static func reachabilityWithAddress(_ hostAddress: sockaddr_in) -> QueueITReachability { + let reachability = QueueITReachability() + reachability.startMonitoringIP(address: hostAddress) + return reachability + } + + static func reachabilityForInternetConnection() -> QueueITReachability { + let reachability = QueueITReachability() + reachability.startMonitoringInternet() + return reachability + } + + static func reachabilityForLocalWiFi() -> QueueITReachability { + let reachability = QueueITReachability() + reachability.startMonitoringWiFi() + return reachability + } + + func startNotifier() -> Bool { + guard monitor == nil else { return true } + monitor = NWPathMonitor() + monitor?.pathUpdateHandler = { [weak self] _ 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 QueueITReachability { + enum NetworkStatus: Int { + case notReachable = 0 + case reachableViaWiFi + case reachableViaWWAN + } +} + +private extension QueueITReachability { + 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/NewQueueITWKViewController.swift b/Sources/QueueITLib/NewQueueITWKViewController.swift new file mode 100644 index 0000000..bb68ca7 --- /dev/null +++ b/Sources/QueueITLib/NewQueueITWKViewController.swift @@ -0,0 +1,193 @@ +import UIKit +import WebKit + +protocol QueueITViewControllerDelegate: AnyObject { + func notifyViewControllerClosed() + func notifyViewControllerUserExited() + func notifyViewControllerSessionRestart() + func notifyViewControllerQueuePassed(queueToken: String?) + func notifyViewControllerPageUrlChanged(urlString: String?) +} + +final class QueueITWKViewController: UIViewController { + weak var delegate: QueueITViewControllerDelegate? + weak var webView: WKWebView? + + private var spinner: UIActivityIndicatorView? + 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) + + guard let spinner, let webView else { + return + } + + spinner.color = .gray + webView.navigationDelegate = self + webView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + webView.isOpaque = false + webView.backgroundColor = .clear + + view.addSubview(webView) + view.addSubview(spinner) + + webView.frame = view.bounds + spinner.frame = view.bounds + } + + func loadWebView() { + guard let spinner, + let webView, + let url = URL(string: queueUrl) + else { + return + } + spinner.startAnimating() + webView.load(URLRequest(url: url)) + } +} + +extension QueueITWKViewController: WKNavigationDelegate { + func webView( + _: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void + ) { + 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 == QueueConsts.queueCloseUrl { + delegate?.notifyViewControllerClosed() + decisionHandler(.cancel) + return + } else if url.absoluteString == QueueConsts.queueRestartSessionUrl { + delegate?.notifyViewControllerSessionRestart() + decisionHandler(.cancel) + return + } + + if isBlockedUrl(destinationUrl: url) { + decisionHandler(.cancel) + return + } + + if isNotFrame { + if isQueueUrl { + raiseQueuePageUrl(urlString) + } + if isTargetUrl(targetUrl: targetUrl, destinationUrl: url) { + isQueuePassed = true + let queueitToken = extractQueueToken(urlString) + delegate?.notifyViewControllerQueuePassed(queueToken: queueitToken) + decisionHandler(.cancel) + return + } + } + + if navigationAction.navigationType == .linkActivated && !isQueueUrl { + UIApplication.shared.open(request.url!) + decisionHandler(.cancel) + return + } + } + } + + decisionHandler(.allow) + } + + 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 QueueITWKViewController { + 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[.. Bool { + return try tryEnqueue(enqueueToken: nil, enqueueKey: nil) + } + + func tryPassWithEnqueueToken(_ enqueueToken: String?) throws -> Bool { + return try tryEnqueue(enqueueToken: enqueueToken, enqueueKey: nil) + } + + func tryPassWithEnqueueKey(_ enqueueKey: String?) throws -> Bool { + return try tryEnqueue(enqueueToken: nil, enqueueKey: enqueueKey) + } + + func isRequestInProgress() -> Bool { + return requestInProgress + } +} + +private extension QueueITWaitingRoomProvider { + func tryEnqueue(enqueueToken: String?, enqueueKey: String?) throws -> Bool { + 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 + + IOSUtils.getUserAgent { [weak self] userAgent in + guard let self else { + return + } + do { + try self.tryEnqueueWithUserAgent( + secretAgent: userAgent, + enqueueToken: enqueueToken, + enqueueKey: enqueueKey + ) + } catch { + self.requestInProgress = false + self.delegate?.notifyProviderFailure( + errorMessage: error.localizedDescription, + errorCode: (error as NSError).code + ) + } + } + return true + } + + func tryEnqueueWithUserAgent(secretAgent: String, enqueueToken: String?, enqueueKey: String?) throws { + let userId = IOSUtils.getUserId() + let userAgent = "\(secretAgent);\(IOSUtils.getLibraryVersion())" + let sdkVersion = IOSUtils.getSdkVersion() + let apiClient = QueueITApiClient.getInstance() + + apiClient.enqueue( + customerId: customerId, + eventOrAliasId: eventOrAliasId, + userId: userId, + userAgent: userAgent, + sdkVersion: sdkVersion, + layoutName: layoutName, + language: language, + enqueueToken: enqueueToken, + enqueueKey: enqueueKey, + success: { [weak self] queueStatus in + guard let self else { + return + } + guard let queueStatus else { + self.enqueueRetryMonitor(enqueueToken: enqueueToken, enqueueKey: enqueueKey) + return + } + + self.handleAppEnqueueResponse( + queueURL: queueStatus.queueUrlString, + eventTargetURL: queueStatus.eventTargetUrl, + queueItToken: queueStatus.queueitToken + ) + self.requestInProgress = false + }, + failure: { [weak self] error, errorMessage in + guard let self else { + return + } + if let nsError = error as? NSError { + if nsError.code >= 400, nsError.code < 500 { + self.delegate?.notifyProviderFailure(errorMessage: errorMessage, errorCode: nsError.code) + } else { + self.enqueueRetryMonitor(enqueueToken: enqueueToken, enqueueKey: enqueueKey) + } + } + } + ) + } + + func handleAppEnqueueResponse( + queueURL: String, + eventTargetURL: String?, + queueItToken: String? + ) { + let isPassedThrough = !(queueItToken?.isEmpty ?? true) + let redirectType = getRedirectType(fromToken: queueItToken) + + let queueTryPassResult = QueueTryPassResult( + queueUrl: queueURL, + targetUrl: eventTargetURL, + redirectType: redirectType, + isPassedThrough: isPassedThrough, + queueToken: queueItToken + ) + delegate?.notifyProviderSuccess(queuePassResult: queueTryPassResult) + } + + func enqueueRetryMonitor(enqueueToken: String?, enqueueKey: String?) { + if deltaSec < QueueITWaitingRoomProvider.maxRetrySec { + try? tryEnqueue(enqueueToken: enqueueToken, enqueueKey: enqueueKey) + Thread.sleep(forTimeInterval: TimeInterval(deltaSec)) + deltaSec *= 2 + } else { + deltaSec = QueueITWaitingRoomProvider.initialWaitRetrySec + requestInProgress = false + 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/NewQueueITWaitingRoomView.swift b/Sources/QueueITLib/NewQueueITWaitingRoomView.swift new file mode 100644 index 0000000..d951c29 --- /dev/null +++ b/Sources/QueueITLib/NewQueueITWaitingRoomView.swift @@ -0,0 +1,100 @@ +import UIKit + +protocol QueueITWaitingRoomViewDelegate: AnyObject { + func notifyViewUserExited() + func notifyViewUserClosed() + func notifyViewSessionRestart() + func notifyQueuePassed(info: QueuePassedInfo?) + func notifyViewQueueDidAppear() + func notifyViewQueueWillOpen() + func notifyViewUpdatePageUrl(urlString: String?) +} + +final class QueueITWaitingRoomView { + weak var host: UIViewController? + weak var delegate: QueueITWaitingRoomViewDelegate? + weak var currentWebView: QueueITWKViewController? + + private var eventId: String + private var delayInterval: Int = 0 + + init(host: UIViewController, eventId: String) { + self.host = host + self.eventId = eventId + } + + func show(queueUrl: String, targetUrl: String) { + raiseQueueViewWillOpen() + + let queueWKVC = QueueITWKViewController( + queueUrl: queueUrl, + eventTargetUrl: targetUrl, + eventId: eventId + ) + + queueWKVC.delegate = self + + if #available(iOS 13.0, *) { + queueWKVC.modalPresentationStyle = UIModalPresentationStyle.fullScreen + } + + 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() + }) + } + } + + func setViewDelay(_ delayInterval: Int) { + self.delayInterval = delayInterval + } +} + +extension QueueITWaitingRoomView: QueueITViewControllerDelegate { + func notifyViewControllerClosed() { + delegate?.notifyViewUserClosed() + close() + } + + func notifyViewControllerUserExited() { + delegate?.notifyViewUserExited() + } + + func notifyViewControllerSessionRestart() { + delegate?.notifyViewSessionRestart() + close() + } + + func notifyViewControllerQueuePassed(queueToken: String?) { + let queuePassedInfo = QueuePassedInfo(queueitToken: queueToken) + delegate?.notifyQueuePassed(info: queuePassedInfo) + close() + } + + func notifyViewControllerPageUrlChanged(urlString: String?) { + delegate?.notifyViewUpdatePageUrl(urlString: urlString) + } +} + +private extension QueueITWaitingRoomView { + func close(onComplete: (() -> Void)? = nil) { + DispatchQueue.main.async { [weak self] in + guard let self, let host else { + return + } + host.dismiss(animated: true) + } + } + + func raiseQueueViewWillOpen() { + delegate?.notifyViewQueueWillOpen() + } +} diff --git a/Sources/QueueITLib/NewQueueInfo.swift b/Sources/QueueITLib/NewQueueInfo.swift new file mode 100644 index 0000000..5411c03 --- /dev/null +++ b/Sources/QueueITLib/NewQueueInfo.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/NewQueueStatus.swift b/Sources/QueueITLib/NewQueueStatus.swift new file mode 100644 index 0000000..f66106b --- /dev/null +++ b/Sources/QueueITLib/NewQueueStatus.swift @@ -0,0 +1,31 @@ +struct QueueStatus { + 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 QueueStatus { + 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/NewQueueTryPassResult.swift b/Sources/QueueITLib/NewQueueTryPassResult.swift new file mode 100644 index 0000000..94405f4 --- /dev/null +++ b/Sources/QueueITLib/NewQueueTryPassResult.swift @@ -0,0 +1,9 @@ +import Foundation + +struct QueueTryPassResult { + let queueUrl: String? + let targetUrl: String? + let redirectType: String + let isPassedThrough: Bool + let queueToken: String? +} From 2d152b2bd7ce8b24285a1944a200f940b3905420 Mon Sep 17 00:00:00 2001 From: alexanderthoren Date: Wed, 11 Dec 2024 19:35:31 +0100 Subject: [PATCH 2/3] refactor: remove redundant return types Removed redundant return types from methods in QueueItEngine and QueueITWaitingRoomProvider classes. This change simplifies the code by eliminating unnecessary return statements. --- Sources/QueueITLib/NewConnection.swift | 6 ++---- Sources/QueueITLib/NewQueueITApiClient.swift | 14 +++++++------- Sources/QueueITLib/NewQueueITEngine.swift | 12 ++++++------ .../NewQueueITWaitingRoomProvider.swift | 15 +++++++-------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Sources/QueueITLib/NewConnection.swift b/Sources/QueueITLib/NewConnection.swift index ce96456..3d3b11e 100644 --- a/Sources/QueueITLib/NewConnection.swift +++ b/Sources/QueueITLib/NewConnection.swift @@ -10,7 +10,7 @@ final class Connection: QueueITApiClient { expectedStatus: Int, success: @escaping QueueServiceSuccess, failure: @escaping QueueServiceFailure - ) -> String { + ) { var request = URLRequest(url: url) request.httpMethod = httpMethod @@ -19,7 +19,7 @@ final class Connection: QueueITApiClient { request.httpBody = jsonData } catch { failure(error, "Failed to serialize request body.") - return "" + return } request.addValue("application/json", forHTTPHeaderField: "Accept") @@ -32,8 +32,6 @@ final class Connection: QueueITApiClient { failure: failure, delegate: self ) - - return connectionRequest?.uniqueIdentifier ?? "" } } diff --git a/Sources/QueueITLib/NewQueueITApiClient.swift b/Sources/QueueITLib/NewQueueITApiClient.swift index 33e6cd9..231142f 100644 --- a/Sources/QueueITLib/NewQueueITApiClient.swift +++ b/Sources/QueueITLib/NewQueueITApiClient.swift @@ -32,7 +32,7 @@ class QueueITApiClient { enqueueKey: String?, success: @escaping (QueueStatus?) -> Void, failure: @escaping QueueServiceFailure - ) -> String { + ) { var bodyDict: [String: Any] = [ "userId": userId, "userAgent": userAgent, @@ -61,7 +61,7 @@ class QueueITApiClient { urlAsString += "/\(eventOrAliasId)" urlAsString += "/enqueue" - return submitPOSTPath( + submitPOSTPath( path: urlAsString, body: bodyDict, success: { data in @@ -88,7 +88,7 @@ class QueueITApiClient { body bodyDict: [String: Any], success: @escaping QueueServiceSuccess, failure: @escaping QueueServiceFailure - ) -> String { + ) { guard let url = URL(string: path) else { let error = NSError( domain: "QueueITApiClient", @@ -96,10 +96,10 @@ class QueueITApiClient { userInfo: [NSLocalizedDescriptionKey: "Invalid URL"] ) failure(error, "Invalid URL") - return "" + return } - return submitRequest( + submitRequest( with: url, method: "POST", body: bodyDict, @@ -116,7 +116,7 @@ class QueueITApiClient { expectedStatus _: Int, success _: @escaping QueueServiceSuccess, failure _: @escaping QueueServiceFailure - ) -> String { - return "" + ) { + return } } diff --git a/Sources/QueueITLib/NewQueueITEngine.swift b/Sources/QueueITLib/NewQueueITEngine.swift index 6f320be..48034a6 100644 --- a/Sources/QueueITLib/NewQueueITEngine.swift +++ b/Sources/QueueITLib/NewQueueITEngine.swift @@ -78,16 +78,16 @@ public final class QueueItEngine { return waitingRoomProvider.isRequestInProgress() } - public func run(withEnqueueKey enqueueKey: String) throws -> Bool { - return try waitingRoomProvider.tryPassWithEnqueueKey(enqueueKey) + public func run(withEnqueueKey enqueueKey: String) throws { + try waitingRoomProvider.tryPassWithEnqueueKey(enqueueKey) } - public func run(withEnqueueToken enqueueToken: String) throws -> Bool { - return try waitingRoomProvider.tryPassWithEnqueueToken(enqueueToken) + public func run(withEnqueueToken enqueueToken: String) throws { + try waitingRoomProvider.tryPassWithEnqueueToken(enqueueToken) } - public func run() throws -> Bool { - return try waitingRoomProvider.tryPass() + public func run() throws { + try waitingRoomProvider.tryPass() } public func showQueue(queueUrl: String, targetUrl: String) { diff --git a/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift b/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift index 6c07d32..7bbde19 100644 --- a/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift +++ b/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift @@ -38,16 +38,16 @@ final class QueueITWaitingRoomProvider { internetReachability = QueueITReachability.reachabilityForInternetConnection() } - func tryPass() throws -> Bool { - return try tryEnqueue(enqueueToken: nil, enqueueKey: nil) + func tryPass() throws { + try tryEnqueue(enqueueToken: nil, enqueueKey: nil) } - func tryPassWithEnqueueToken(_ enqueueToken: String?) throws -> Bool { - return try tryEnqueue(enqueueToken: enqueueToken, enqueueKey: nil) + func tryPassWithEnqueueToken(_ enqueueToken: String?) throws { + try tryEnqueue(enqueueToken: enqueueToken, enqueueKey: nil) } - func tryPassWithEnqueueKey(_ enqueueKey: String?) throws -> Bool { - return try tryEnqueue(enqueueToken: nil, enqueueKey: enqueueKey) + func tryPassWithEnqueueKey(_ enqueueKey: String?) throws { + try tryEnqueue(enqueueToken: nil, enqueueKey: enqueueKey) } func isRequestInProgress() -> Bool { @@ -56,7 +56,7 @@ final class QueueITWaitingRoomProvider { } private extension QueueITWaitingRoomProvider { - func tryEnqueue(enqueueToken: String?, enqueueKey: String?) throws -> Bool { + func tryEnqueue(enqueueToken: String?, enqueueKey: String?) throws { guard checkConnection() else { throw NSError( domain: "QueueITRuntimeException", @@ -93,7 +93,6 @@ private extension QueueITWaitingRoomProvider { ) } } - return true } func tryEnqueueWithUserAgent(secretAgent: String, enqueueToken: String?, enqueueKey: String?) throws { From b1e643c0f1e69c2d226d0a813679ca7f03e68d0f Mon Sep 17 00:00:00 2001 From: alexanderthoren Date: Wed, 11 Dec 2024 20:58:17 +0100 Subject: [PATCH 3/3] feat: rename files and classes for consistency Renamed various files and classes to improve naming consistency across the project. Updated references in the README.md and source files to reflect these changes. This includes renaming classes like QueueITApiClient to ApiClient, QueueITWaitingRoomProvider to WaitingRoomProvider, and others. --- .../contents.xcworkspacedata | 7 +++ README.md | 14 +++--- ...QueueITApiClient.swift => ApiClient.swift} | 16 +++---- .../{NewConnection.swift => Connection.swift} | 2 +- ...nRequest.swift => ConnectionRequest.swift} | 0 .../{NewQueueConsts.swift => Constants.swift} | 2 +- ...ueueITEngine.swift => QueueITEngine.swift} | 14 +++--- .../{NewQueueInfo.swift => QueueInfo.swift} | 0 ...TReachability.swift => Reachability.swift} | 22 +++++----- .../{NewQueueStatus.swift => Status.swift} | 4 +- ...ryPassResult.swift => TryPassResult.swift} | 2 +- .../{NewIOSUtils.swift => Utils.swift} | 4 +- ...ovider.swift => WaitingRoomProvider.swift} | 44 +++++++++---------- ...ngRoomView.swift => WaitingRoomView.swift} | 14 +++--- ...ntroller.swift => WebViewController.swift} | 14 +++--- 15 files changed, 83 insertions(+), 76 deletions(-) create mode 100644 .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata rename Sources/QueueITLib/{NewQueueITApiClient.swift => ApiClient.swift} (86%) rename Sources/QueueITLib/{NewConnection.swift => Connection.swift} (96%) rename Sources/QueueITLib/{NewConnectionRequest.swift => ConnectionRequest.swift} (100%) rename Sources/QueueITLib/{NewQueueConsts.swift => Constants.swift} (89%) rename Sources/QueueITLib/{NewQueueITEngine.swift => QueueITEngine.swift} (91%) rename Sources/QueueITLib/{NewQueueInfo.swift => QueueInfo.swift} (100%) rename Sources/QueueITLib/{NewQueueITReachability.swift => Reachability.swift} (86%) rename Sources/QueueITLib/{NewQueueStatus.swift => Status.swift} (94%) rename Sources/QueueITLib/{NewQueueTryPassResult.swift => TryPassResult.swift} (85%) rename Sources/QueueITLib/{NewIOSUtils.swift => Utils.swift} (95%) rename Sources/QueueITLib/{NewQueueITWaitingRoomProvider.swift => WaitingRoomProvider.swift} (80%) rename Sources/QueueITLib/{NewQueueITWaitingRoomView.swift => WaitingRoomView.swift} (87%) rename Sources/QueueITLib/{NewQueueITWKViewController.swift => WebViewController.swift} (93%) 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/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/NewQueueITApiClient.swift b/Sources/QueueITLib/ApiClient.swift similarity index 86% rename from Sources/QueueITLib/NewQueueITApiClient.swift rename to Sources/QueueITLib/ApiClient.swift index 231142f..3676bb0 100644 --- a/Sources/QueueITLib/NewQueueITApiClient.swift +++ b/Sources/QueueITLib/ApiClient.swift @@ -3,13 +3,13 @@ import Foundation typealias QueueServiceSuccess = (Data) -> Void typealias QueueServiceFailure = (Error, String) -> Void -class QueueITApiClient { +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 static var testingIsEnabled = false - private static var sharedInstance: QueueITApiClient? + private static var sharedInstance: ApiClient? - static func getInstance() -> QueueITApiClient { + static func getInstance() -> ApiClient { if sharedInstance == nil { sharedInstance = Connection() } @@ -30,7 +30,7 @@ class QueueITApiClient { language: String?, enqueueToken: String?, enqueueKey: String?, - success: @escaping (QueueStatus?) -> Void, + success: @escaping (Status?) -> Void, failure: @escaping QueueServiceFailure ) { var bodyDict: [String: Any] = [ @@ -55,7 +55,7 @@ class QueueITApiClient { bodyDict["enqueueKey"] = enqueueKey } - let apiRoot = QueueITApiClient.testingIsEnabled ? QueueITApiClient.TESTING_API_ROOT : QueueITApiClient.API_ROOT + let apiRoot = ApiClient.testingIsEnabled ? ApiClient.TESTING_API_ROOT : ApiClient.API_ROOT var urlAsString = String(format: apiRoot, customerId) urlAsString += "/\(customerId)" urlAsString += "/\(eventOrAliasId)" @@ -70,8 +70,8 @@ class QueueITApiClient { with: data, options: [] ) as? [String: Any] { - let queueStatus = QueueStatus(dictionary: userDict) - success(queueStatus) + let Status = Status(dictionary: userDict) + success(Status) } else { success(nil) } @@ -91,7 +91,7 @@ class QueueITApiClient { ) { guard let url = URL(string: path) else { let error = NSError( - domain: "QueueITApiClient", + domain: "ApiClient", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"] ) diff --git a/Sources/QueueITLib/NewConnection.swift b/Sources/QueueITLib/Connection.swift similarity index 96% rename from Sources/QueueITLib/NewConnection.swift rename to Sources/QueueITLib/Connection.swift index 3d3b11e..0c6f9b6 100644 --- a/Sources/QueueITLib/NewConnection.swift +++ b/Sources/QueueITLib/Connection.swift @@ -1,6 +1,6 @@ import Foundation -final class Connection: QueueITApiClient { +final class Connection: ApiClient { private var connectionRequest: ConnectionRequest? override func submitRequest( diff --git a/Sources/QueueITLib/NewConnectionRequest.swift b/Sources/QueueITLib/ConnectionRequest.swift similarity index 100% rename from Sources/QueueITLib/NewConnectionRequest.swift rename to Sources/QueueITLib/ConnectionRequest.swift diff --git a/Sources/QueueITLib/NewQueueConsts.swift b/Sources/QueueITLib/Constants.swift similarity index 89% rename from Sources/QueueITLib/NewQueueConsts.swift rename to Sources/QueueITLib/Constants.swift index f2f6e7e..7f9e5b8 100644 --- a/Sources/QueueITLib/NewQueueConsts.swift +++ b/Sources/QueueITLib/Constants.swift @@ -1,4 +1,4 @@ -enum QueueConsts { +enum Constants { static let queueCloseUrl = "queueit://close" static let queueRestartSessionUrl = "queueit://restartSession" static let sdkVersion = "iOS-3.4.4" diff --git a/Sources/QueueITLib/NewQueueITEngine.swift b/Sources/QueueITLib/QueueITEngine.swift similarity index 91% rename from Sources/QueueITLib/NewQueueITEngine.swift rename to Sources/QueueITLib/QueueITEngine.swift index 48034a6..884e59d 100644 --- a/Sources/QueueITLib/NewQueueITEngine.swift +++ b/Sources/QueueITLib/QueueITEngine.swift @@ -53,19 +53,19 @@ public final class QueueItEngine { public weak var queueViewDidAppearDelegate: QueueViewDidAppearDelegate? public weak var host: UIViewController? - private var waitingRoomProvider: QueueITWaitingRoomProvider - private var waitingRoomView: QueueITWaitingRoomView + private var waitingRoomProvider: WaitingRoomProvider + private var waitingRoomView: WaitingRoomView public init(host: UIViewController, customerId: String, eventOrAliasId: String, layoutName: String?, language: String?) { self.host = host - waitingRoomProvider = QueueITWaitingRoomProvider( + waitingRoomProvider = WaitingRoomProvider( customerId: customerId, eventOrAliasId: eventOrAliasId, layoutName: layoutName, language: language ) - waitingRoomView = QueueITWaitingRoomView(host: host, eventId: eventOrAliasId) + waitingRoomView = WaitingRoomView(host: host, eventId: eventOrAliasId) waitingRoomView.delegate = self waitingRoomProvider.delegate = self } @@ -95,7 +95,7 @@ public final class QueueItEngine { } } -extension QueueItEngine: QueueITWaitingRoomViewDelegate { +extension QueueItEngine: WaitingRoomViewDelegate { func notifyViewUserExited() { queueUserExitedDelegate?.notifyUserExited() } @@ -126,8 +126,8 @@ extension QueueItEngine: QueueITWaitingRoomViewDelegate { } } -extension QueueItEngine: QueueITWaitingRoomProviderDelegate { - func notifyProviderSuccess(queuePassResult: QueueTryPassResult) { +extension QueueItEngine: WaitingRoomProviderDelegate { + func notifyProviderSuccess(queuePassResult: TryPassResult) { switch queuePassResult.redirectType { case "safetynet": let queuePassedInfo = QueuePassedInfo(queueitToken: queuePassResult.queueToken) diff --git a/Sources/QueueITLib/NewQueueInfo.swift b/Sources/QueueITLib/QueueInfo.swift similarity index 100% rename from Sources/QueueITLib/NewQueueInfo.swift rename to Sources/QueueITLib/QueueInfo.swift diff --git a/Sources/QueueITLib/NewQueueITReachability.swift b/Sources/QueueITLib/Reachability.swift similarity index 86% rename from Sources/QueueITLib/NewQueueITReachability.swift rename to Sources/QueueITLib/Reachability.swift index 3771353..79236ed 100644 --- a/Sources/QueueITLib/NewQueueITReachability.swift +++ b/Sources/QueueITLib/Reachability.swift @@ -7,33 +7,33 @@ extension Notification.Name { static let reachabilityChanged = Notification.Name("kNetworkReachabilityChangedNotification") } -final class QueueITReachability { +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) -> QueueITReachability { - let reachability = QueueITReachability() + static func reachabilityWithHostName(_ hostName: String) -> Reachability { + let reachability = Reachability() reachability.startMonitoringHost(hostName: hostName) return reachability } - static func reachabilityWithAddress(_ hostAddress: sockaddr_in) -> QueueITReachability { - let reachability = QueueITReachability() + static func reachabilityWithAddress(_ hostAddress: sockaddr_in) -> Reachability { + let reachability = Reachability() reachability.startMonitoringIP(address: hostAddress) return reachability } - static func reachabilityForInternetConnection() -> QueueITReachability { - let reachability = QueueITReachability() + static func reachabilityForInternetConnection() -> Reachability { + let reachability = Reachability() reachability.startMonitoringInternet() return reachability } - static func reachabilityForLocalWiFi() -> QueueITReachability { - let reachability = QueueITReachability() + static func reachabilityForLocalWiFi() -> Reachability { + let reachability = Reachability() reachability.startMonitoringWiFi() return reachability } @@ -74,7 +74,7 @@ final class QueueITReachability { } } -extension QueueITReachability { +extension Reachability { enum NetworkStatus: Int { case notReachable = 0 case reachableViaWiFi @@ -82,7 +82,7 @@ extension QueueITReachability { } } -private extension QueueITReachability { +private extension Reachability { func startMonitoringHost(hostName _: String) { monitor = NWPathMonitor(requiredInterfaceType: .other) monitor?.pathUpdateHandler = { _ in diff --git a/Sources/QueueITLib/NewQueueStatus.swift b/Sources/QueueITLib/Status.swift similarity index 94% rename from Sources/QueueITLib/NewQueueStatus.swift rename to Sources/QueueITLib/Status.swift index f66106b..a5da82f 100644 --- a/Sources/QueueITLib/NewQueueStatus.swift +++ b/Sources/QueueITLib/Status.swift @@ -1,4 +1,4 @@ -struct QueueStatus { +struct Status { let queueId: String let queueUrlString: String let eventTargetUrl: String @@ -21,7 +21,7 @@ struct QueueStatus { } } -private extension QueueStatus { +private extension Status { enum Constants { static let KEY_QUEUE_ID = "QueueId" static let KEY_QUEUE_URL = "QueueUrl" diff --git a/Sources/QueueITLib/NewQueueTryPassResult.swift b/Sources/QueueITLib/TryPassResult.swift similarity index 85% rename from Sources/QueueITLib/NewQueueTryPassResult.swift rename to Sources/QueueITLib/TryPassResult.swift index 94405f4..30f0d98 100644 --- a/Sources/QueueITLib/NewQueueTryPassResult.swift +++ b/Sources/QueueITLib/TryPassResult.swift @@ -1,6 +1,6 @@ import Foundation -struct QueueTryPassResult { +struct TryPassResult { let queueUrl: String? let targetUrl: String? let redirectType: String diff --git a/Sources/QueueITLib/NewIOSUtils.swift b/Sources/QueueITLib/Utils.swift similarity index 95% rename from Sources/QueueITLib/NewIOSUtils.swift rename to Sources/QueueITLib/Utils.swift index 1b15bf6..2e412f4 100644 --- a/Sources/QueueITLib/NewIOSUtils.swift +++ b/Sources/QueueITLib/Utils.swift @@ -1,7 +1,7 @@ import Foundation import WebKit -enum IOSUtils { +enum Utils { static func getUserId() -> String { let device = UIDevice() if let deviceId = device.identifierForVendor { @@ -35,6 +35,6 @@ enum IOSUtils { } static func getSdkVersion() -> String { - return QueueConsts.sdkVersion + return Constants.sdkVersion } } diff --git a/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift b/Sources/QueueITLib/WaitingRoomProvider.swift similarity index 80% rename from Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift rename to Sources/QueueITLib/WaitingRoomProvider.swift index 7bbde19..33e8fb1 100644 --- a/Sources/QueueITLib/NewQueueITWaitingRoomProvider.swift +++ b/Sources/QueueITLib/WaitingRoomProvider.swift @@ -1,7 +1,7 @@ import Foundation -protocol QueueITWaitingRoomProviderDelegate: AnyObject { - func notifyProviderSuccess(queuePassResult: QueueTryPassResult) +protocol WaitingRoomProviderDelegate: AnyObject { + func notifyProviderSuccess(queuePassResult: TryPassResult) func notifyProviderFailure(errorMessage: String?, errorCode: Int) } @@ -15,27 +15,27 @@ enum QueueITRuntimeError: Int { ] } -final class QueueITWaitingRoomProvider { +final class WaitingRoomProvider { static let maxRetrySec = 10 static let initialWaitRetrySec = 1 - weak var delegate: QueueITWaitingRoomProviderDelegate? + weak var delegate: WaitingRoomProviderDelegate? private let customerId: String private let eventOrAliasId: String private let layoutName: String? private let language: String? - private var deltaSec: Int = QueueITWaitingRoomProvider.initialWaitRetrySec + private var deltaSec: Int = WaitingRoomProvider.initialWaitRetrySec private var requestInProgress: Bool = false - private let internetReachability: QueueITReachability + private let internetReachability: Reachability init(customerId: String, eventOrAliasId: String, layoutName: String? = nil, language: String? = nil) { self.customerId = customerId self.eventOrAliasId = eventOrAliasId self.layoutName = layoutName self.language = language - internetReachability = QueueITReachability.reachabilityForInternetConnection() + internetReachability = Reachability.reachabilityForInternetConnection() } func tryPass() throws { @@ -55,7 +55,7 @@ final class QueueITWaitingRoomProvider { } } -private extension QueueITWaitingRoomProvider { +private extension WaitingRoomProvider { func tryEnqueue(enqueueToken: String?, enqueueKey: String?) throws { guard checkConnection() else { throw NSError( @@ -75,7 +75,7 @@ private extension QueueITWaitingRoomProvider { requestInProgress = true - IOSUtils.getUserAgent { [weak self] userAgent in + Utils.getUserAgent { [weak self] userAgent in guard let self else { return } @@ -96,10 +96,10 @@ private extension QueueITWaitingRoomProvider { } func tryEnqueueWithUserAgent(secretAgent: String, enqueueToken: String?, enqueueKey: String?) throws { - let userId = IOSUtils.getUserId() - let userAgent = "\(secretAgent);\(IOSUtils.getLibraryVersion())" - let sdkVersion = IOSUtils.getSdkVersion() - let apiClient = QueueITApiClient.getInstance() + let userId = Utils.getUserId() + let userAgent = "\(secretAgent);\(Utils.getLibraryVersion())" + let sdkVersion = Utils.getSdkVersion() + let apiClient = ApiClient.getInstance() apiClient.enqueue( customerId: customerId, @@ -111,19 +111,19 @@ private extension QueueITWaitingRoomProvider { language: language, enqueueToken: enqueueToken, enqueueKey: enqueueKey, - success: { [weak self] queueStatus in + success: { [weak self] Status in guard let self else { return } - guard let queueStatus else { + guard let Status else { self.enqueueRetryMonitor(enqueueToken: enqueueToken, enqueueKey: enqueueKey) return } self.handleAppEnqueueResponse( - queueURL: queueStatus.queueUrlString, - eventTargetURL: queueStatus.eventTargetUrl, - queueItToken: queueStatus.queueitToken + queueURL: Status.queueUrlString, + eventTargetURL: Status.eventTargetUrl, + queueItToken: Status.queueitToken ) self.requestInProgress = false }, @@ -150,23 +150,23 @@ private extension QueueITWaitingRoomProvider { let isPassedThrough = !(queueItToken?.isEmpty ?? true) let redirectType = getRedirectType(fromToken: queueItToken) - let queueTryPassResult = QueueTryPassResult( + let TryPassResult = TryPassResult( queueUrl: queueURL, targetUrl: eventTargetURL, redirectType: redirectType, isPassedThrough: isPassedThrough, queueToken: queueItToken ) - delegate?.notifyProviderSuccess(queuePassResult: queueTryPassResult) + delegate?.notifyProviderSuccess(queuePassResult: TryPassResult) } func enqueueRetryMonitor(enqueueToken: String?, enqueueKey: String?) { - if deltaSec < QueueITWaitingRoomProvider.maxRetrySec { + if deltaSec < WaitingRoomProvider.maxRetrySec { try? tryEnqueue(enqueueToken: enqueueToken, enqueueKey: enqueueKey) Thread.sleep(forTimeInterval: TimeInterval(deltaSec)) deltaSec *= 2 } else { - deltaSec = QueueITWaitingRoomProvider.initialWaitRetrySec + deltaSec = WaitingRoomProvider.initialWaitRetrySec requestInProgress = false delegate?.notifyProviderFailure(errorMessage: "Error! Queue is unavailable.", errorCode: 3) } diff --git a/Sources/QueueITLib/NewQueueITWaitingRoomView.swift b/Sources/QueueITLib/WaitingRoomView.swift similarity index 87% rename from Sources/QueueITLib/NewQueueITWaitingRoomView.swift rename to Sources/QueueITLib/WaitingRoomView.swift index d951c29..453dd07 100644 --- a/Sources/QueueITLib/NewQueueITWaitingRoomView.swift +++ b/Sources/QueueITLib/WaitingRoomView.swift @@ -1,6 +1,6 @@ import UIKit -protocol QueueITWaitingRoomViewDelegate: AnyObject { +protocol WaitingRoomViewDelegate: AnyObject { func notifyViewUserExited() func notifyViewUserClosed() func notifyViewSessionRestart() @@ -10,10 +10,10 @@ protocol QueueITWaitingRoomViewDelegate: AnyObject { func notifyViewUpdatePageUrl(urlString: String?) } -final class QueueITWaitingRoomView { +final class WaitingRoomView { weak var host: UIViewController? - weak var delegate: QueueITWaitingRoomViewDelegate? - weak var currentWebView: QueueITWKViewController? + weak var delegate: WaitingRoomViewDelegate? + weak var currentWebView: WebViewController? private var eventId: String private var delayInterval: Int = 0 @@ -26,7 +26,7 @@ final class QueueITWaitingRoomView { func show(queueUrl: String, targetUrl: String) { raiseQueueViewWillOpen() - let queueWKVC = QueueITWKViewController( + let queueWKVC = WebViewController( queueUrl: queueUrl, eventTargetUrl: targetUrl, eventId: eventId @@ -58,7 +58,7 @@ final class QueueITWaitingRoomView { } } -extension QueueITWaitingRoomView: QueueITViewControllerDelegate { +extension WaitingRoomView: WebViewControllerDelegate { func notifyViewControllerClosed() { delegate?.notifyViewUserClosed() close() @@ -84,7 +84,7 @@ extension QueueITWaitingRoomView: QueueITViewControllerDelegate { } } -private extension QueueITWaitingRoomView { +private extension WaitingRoomView { func close(onComplete: (() -> Void)? = nil) { DispatchQueue.main.async { [weak self] in guard let self, let host else { diff --git a/Sources/QueueITLib/NewQueueITWKViewController.swift b/Sources/QueueITLib/WebViewController.swift similarity index 93% rename from Sources/QueueITLib/NewQueueITWKViewController.swift rename to Sources/QueueITLib/WebViewController.swift index bb68ca7..ee6c755 100644 --- a/Sources/QueueITLib/NewQueueITWKViewController.swift +++ b/Sources/QueueITLib/WebViewController.swift @@ -1,7 +1,7 @@ import UIKit import WebKit -protocol QueueITViewControllerDelegate: AnyObject { +protocol WebViewControllerDelegate: AnyObject { func notifyViewControllerClosed() func notifyViewControllerUserExited() func notifyViewControllerSessionRestart() @@ -9,8 +9,8 @@ protocol QueueITViewControllerDelegate: AnyObject { func notifyViewControllerPageUrlChanged(urlString: String?) } -final class QueueITWKViewController: UIViewController { - weak var delegate: QueueITViewControllerDelegate? +final class WebViewController: UIViewController { + weak var delegate: WebViewControllerDelegate? weak var webView: WKWebView? private var spinner: UIActivityIndicatorView? @@ -76,7 +76,7 @@ final class QueueITWKViewController: UIViewController { } } -extension QueueITWKViewController: WKNavigationDelegate { +extension WebViewController: WKNavigationDelegate { func webView( _: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, @@ -93,11 +93,11 @@ extension QueueITWKViewController: WKNavigationDelegate { let isNotFrame = request.url?.absoluteString == request.mainDocumentURL?.absoluteString - if url.absoluteString == QueueConsts.queueCloseUrl { + if url.absoluteString == Constants.queueCloseUrl { delegate?.notifyViewControllerClosed() decisionHandler(.cancel) return - } else if url.absoluteString == QueueConsts.queueRestartSessionUrl { + } else if url.absoluteString == Constants.queueRestartSessionUrl { delegate?.notifyViewControllerSessionRestart() decisionHandler(.cancel) return @@ -164,7 +164,7 @@ extension QueueITWKViewController: WKNavigationDelegate { } } -private extension QueueITWKViewController { +private extension WebViewController { func isTargetUrl(targetUrl: URL, destinationUrl: URL) -> Bool { return destinationUrl.host == targetUrl.host && destinationUrl.path == targetUrl.path }