修改pods
This commit is contained in:
@@ -106,8 +106,6 @@ static NSString *const kGULDidRegisterForRemoteNotificationsSEL =
|
||||
@"application:didRegisterForRemoteNotificationsWithDeviceToken:";
|
||||
static NSString *const kGULDidFailToRegisterForRemoteNotificationsSEL =
|
||||
@"application:didFailToRegisterForRemoteNotificationsWithError:";
|
||||
static NSString *const kGULDidReceiveRemoteNotificationSEL =
|
||||
@"application:didReceiveRemoteNotification:";
|
||||
static NSString *const kGULDidReceiveRemoteNotificationWithCompletionSEL =
|
||||
@"application:didReceiveRemoteNotification:fetchCompletionHandler:";
|
||||
|
||||
@@ -218,27 +216,27 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
@"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
|
||||
if (!interceptor) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling000],
|
||||
@"AppDelegateProxy cannot add nil interceptor.");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling000],
|
||||
@"AppDelegateProxy cannot add nil interceptor.");
|
||||
return nil;
|
||||
}
|
||||
if (![interceptor conformsToProtocol:@protocol(GULApplicationDelegate)]) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling001],
|
||||
@"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling001],
|
||||
@"AppDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// The ID should be the same given the same interceptor object.
|
||||
NSString *interceptorID = [NSString stringWithFormat:@"%@%p", kGULAppDelegatePrefix, interceptor];
|
||||
if (!interceptorID.length) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling002],
|
||||
@"AppDelegateProxy cannot create Interceptor ID.");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling002],
|
||||
@"AppDelegateProxy cannot create Interceptor ID.");
|
||||
return nil;
|
||||
}
|
||||
GULZeroingWeakContainer *weakObject = [[GULZeroingWeakContainer alloc] init];
|
||||
@@ -253,21 +251,21 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
@"AppDelegateProxy cannot unregister empty interceptor ID.");
|
||||
|
||||
if (!interceptorID) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling003],
|
||||
@"AppDelegateProxy cannot unregister empty interceptor ID.");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling003],
|
||||
@"AppDelegateProxy cannot unregister empty interceptor ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
GULZeroingWeakContainer *weakContainer = [GULAppDelegateSwizzler interceptors][interceptorID];
|
||||
if (!weakContainer.object) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling004],
|
||||
@"AppDelegateProxy cannot unregister interceptor that was not registered. "
|
||||
"Interceptor ID %@",
|
||||
interceptorID);
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling004],
|
||||
@"AppDelegateProxy cannot unregister interceptor that was not registered. "
|
||||
"Interceptor ID %@",
|
||||
interceptorID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -345,12 +343,13 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
[NSString stringWithFormat:@"%@-%@", classNameWithPrefix, [NSUUID UUID].UUIDString];
|
||||
|
||||
if (NSClassFromString(newClassName)) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling005],
|
||||
@"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
|
||||
@"%@, subclass: %@",
|
||||
NSStringFromClass(realClass), newClassName);
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling005],
|
||||
@"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
|
||||
@"%@, subclass: %@",
|
||||
NSStringFromClass(realClass), newClassName);
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -358,12 +357,13 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
// size.
|
||||
Class appDelegateSubClass = objc_allocateClassPair(realClass, newClassName.UTF8String, 0);
|
||||
if (appDelegateSubClass == Nil) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling006],
|
||||
@"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
|
||||
@"%@, subclass: Nil",
|
||||
NSStringFromClass(realClass));
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling006],
|
||||
@"Cannot create a proxy for App Delegate. Subclass already exists. Original Class: "
|
||||
@"%@, subclass: Nil",
|
||||
NSStringFromClass(realClass));
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -438,12 +438,13 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
// cannot have more ivars/properties than its superclass since it will cause an offset in memory
|
||||
// that can lead to overwriting the isa of an object in the next frame.
|
||||
if (class_getInstanceSize(realClass) != class_getInstanceSize(appDelegateSubClass)) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling007],
|
||||
@"Cannot create subclass of App Delegate, because the created subclass is not the "
|
||||
@"same size. %@",
|
||||
NSStringFromClass(realClass));
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling007],
|
||||
@"Cannot create subclass of App Delegate, because the created subclass is not the "
|
||||
@"same size. %@",
|
||||
NSStringFromClass(realClass));
|
||||
NSAssert(NO, @"Classes must be the same size to swizzle isa");
|
||||
return nil;
|
||||
}
|
||||
@@ -451,12 +452,12 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
// Make the newly created class to be the subclass of the real App Delegate class.
|
||||
objc_registerClassPair(appDelegateSubClass);
|
||||
if (object_setClass(appDelegate, appDelegateSubClass)) {
|
||||
GULLogDebug(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling008],
|
||||
@"Successfully created App Delegate Proxy automatically. To disable the "
|
||||
@"proxy, set the flag %@ to NO (Boolean) in the Info.plist",
|
||||
[GULAppDelegateSwizzler correctAppDelegateProxyKey]);
|
||||
GULOSLogDebug(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling008],
|
||||
@"Successfully created App Delegate Proxy automatically. To disable the "
|
||||
@"proxy, set the flag %@ to NO (Boolean) in the Info.plist",
|
||||
[GULAppDelegateSwizzler correctAppDelegateProxyKey]);
|
||||
}
|
||||
|
||||
return appDelegateSubClass;
|
||||
@@ -499,38 +500,18 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
realClass:realClass
|
||||
storeDestinationImplementationTo:realImplementationsBySelector];
|
||||
|
||||
// For application:didReceiveRemoteNotification:
|
||||
SEL didReceiveRemoteNotificationSEL = NSSelectorFromString(kGULDidReceiveRemoteNotificationSEL);
|
||||
SEL didReceiveRemoteNotificationDonorSEL = @selector(application:
|
||||
donor_didReceiveRemoteNotification:);
|
||||
|
||||
[self proxyDestinationSelector:didReceiveRemoteNotificationSEL
|
||||
implementationsFromSourceSelector:didReceiveRemoteNotificationDonorSEL
|
||||
fromClass:[GULAppDelegateSwizzler class]
|
||||
toClass:appDelegateSubClass
|
||||
realClass:realClass
|
||||
storeDestinationImplementationTo:realImplementationsBySelector];
|
||||
|
||||
// For application:didReceiveRemoteNotification:fetchCompletionHandler:
|
||||
#if !TARGET_OS_WATCH && !TARGET_OS_OSX
|
||||
SEL didReceiveRemoteNotificationWithCompletionSEL =
|
||||
NSSelectorFromString(kGULDidReceiveRemoteNotificationWithCompletionSEL);
|
||||
SEL didReceiveRemoteNotificationWithCompletionDonorSEL =
|
||||
@selector(application:donor_didReceiveRemoteNotification:fetchCompletionHandler:);
|
||||
if ([appDelegate respondsToSelector:didReceiveRemoteNotificationWithCompletionSEL]) {
|
||||
// Only add the application:didReceiveRemoteNotification:fetchCompletionHandler: method if
|
||||
// the original AppDelegate implements it.
|
||||
// This fixes a bug if an app only implements application:didReceiveRemoteNotification:
|
||||
// (if we add the method with completion, iOS sees that one exists and does not call
|
||||
// the method without the completion, which in this case is the only one the app implements).
|
||||
|
||||
[self proxyDestinationSelector:didReceiveRemoteNotificationWithCompletionSEL
|
||||
implementationsFromSourceSelector:didReceiveRemoteNotificationWithCompletionDonorSEL
|
||||
fromClass:[GULAppDelegateSwizzler class]
|
||||
toClass:appDelegateSubClass
|
||||
realClass:realClass
|
||||
storeDestinationImplementationTo:realImplementationsBySelector];
|
||||
}
|
||||
[self proxyDestinationSelector:didReceiveRemoteNotificationWithCompletionSEL
|
||||
implementationsFromSourceSelector:didReceiveRemoteNotificationWithCompletionDonorSEL
|
||||
fromClass:[GULAppDelegateSwizzler class]
|
||||
toClass:appDelegateSubClass
|
||||
realClass:realClass
|
||||
storeDestinationImplementationTo:realImplementationsBySelector];
|
||||
#endif // !TARGET_OS_WATCH && !TARGET_OS_OSX
|
||||
}
|
||||
|
||||
@@ -626,11 +607,12 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
IMP methodIMP = method_getImplementation(method);
|
||||
const char *types = method_getTypeEncoding(method);
|
||||
if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) {
|
||||
GULLogWarning(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling009],
|
||||
@"Cannot copy method to destination selector %@ as it already exists",
|
||||
NSStringFromSelector(destinationSelector));
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling009],
|
||||
@"Cannot copy method to destination selector %@ as it already exists",
|
||||
NSStringFromSelector(destinationSelector));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,8 +644,8 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
GULZeroingWeakContainer *interceptorContainer = obj;
|
||||
id interceptor = interceptorContainer.object;
|
||||
if (!interceptor) {
|
||||
GULLogWarning(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeAppDelegateSwizzling010],
|
||||
@"AppDelegateProxy cannot find interceptor with ID %@. Removing the interceptor.", key);
|
||||
@@ -720,9 +702,7 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
|
||||
#endif // TARGET_OS_IOS || TARGET_OS_TV
|
||||
|
||||
// TODO(Xcode 15): When Xcode 15 is the minimum supported Xcode version,
|
||||
// it will be unnecessary to check if `TARGET_OS_VISION` is defined.
|
||||
#if TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
- (BOOL)application:(GULApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
@@ -755,7 +735,7 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
return returnedValue;
|
||||
}
|
||||
|
||||
#endif // TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
|
||||
#endif // TARGET_OS_IOS
|
||||
|
||||
#pragma mark - [Donor Methods] Network overridden handler methods
|
||||
|
||||
@@ -765,7 +745,7 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
- (void)application:(GULApplication *)application
|
||||
handleEventsForBackgroundURLSession:(NSString *)identifier
|
||||
completionHandler:(void (^)())completionHandler API_AVAILABLE(ios(7.0)) {
|
||||
completionHandler:(void (^)())completionHandler {
|
||||
#pragma clang diagnostic pop
|
||||
SEL methodSelector = @selector(application:
|
||||
handleEventsForBackgroundURLSession:completionHandler:);
|
||||
@@ -954,35 +934,6 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
}
|
||||
#endif // !TARGET_OS_WATCH && !TARGET_OS_OSX
|
||||
|
||||
- (void)application:(GULApplication *)application
|
||||
donor_didReceiveRemoteNotification:(NSDictionary *)userInfo {
|
||||
SEL methodSelector = NSSelectorFromString(kGULDidReceiveRemoteNotificationSEL);
|
||||
NSValue *didReceiveRemoteNotificationIMPPointer =
|
||||
[GULAppDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
|
||||
GULRealDidReceiveRemoteNotificationIMP didReceiveRemoteNotificationIMP =
|
||||
[didReceiveRemoteNotificationIMPPointer pointerValue];
|
||||
|
||||
// Notify interceptors.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[GULAppDelegateSwizzler
|
||||
notifyInterceptorsWithMethodSelector:methodSelector
|
||||
callback:^(id<GULApplicationDelegate> interceptor) {
|
||||
NSInvocation *invocation = [GULAppDelegateSwizzler
|
||||
appDelegateInvocationForSelector:methodSelector];
|
||||
[invocation setTarget:interceptor];
|
||||
[invocation setSelector:methodSelector];
|
||||
[invocation setArgument:(void *)(&application) atIndex:2];
|
||||
[invocation setArgument:(void *)(&userInfo) atIndex:3];
|
||||
[invocation invoke];
|
||||
}];
|
||||
#pragma clang diagnostic pop
|
||||
// Call the real implementation if the real App Delegate has any.
|
||||
if (didReceiveRemoteNotificationIMP) {
|
||||
didReceiveRemoteNotificationIMP(self, methodSelector, application, userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
+ (nullable NSInvocation *)appDelegateInvocationForSelector:(SEL)selector {
|
||||
struct objc_method_description methodDescription =
|
||||
protocol_getMethodDescription(@protocol(GULApplicationDelegate), selector, NO, YES);
|
||||
@@ -996,8 +947,8 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
|
||||
+ (void)proxyAppDelegate:(id<GULApplicationDelegate>)appDelegate {
|
||||
if (![appDelegate conformsToProtocol:@protocol(GULApplicationDelegate)]) {
|
||||
GULLogNotice(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogNotice(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate],
|
||||
@@ -1009,20 +960,20 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
id<GULApplicationDelegate> originalDelegate = appDelegate;
|
||||
// Do not create a subclass if it is not enabled.
|
||||
if (![GULAppDelegateSwizzler isAppDelegateProxyEnabled]) {
|
||||
GULLogNotice(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling011],
|
||||
@"App Delegate Proxy is disabled. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
GULOSLogNotice(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling011],
|
||||
@"App Delegate Proxy is disabled. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
return;
|
||||
}
|
||||
// Do not accept nil delegate.
|
||||
if (!originalDelegate) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling012],
|
||||
@"Cannot create App Delegate Proxy because App Delegate instance is nil. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling012],
|
||||
@"Cannot create App Delegate Proxy because App Delegate instance is nil. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1031,11 +982,11 @@ static dispatch_once_t sProxyAppDelegateRemoteNotificationOnceToken;
|
||||
gAppDelegateSubclass = [self createSubclassWithObject:originalDelegate];
|
||||
[self reassignAppDelegate];
|
||||
} @catch (NSException *exception) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling013],
|
||||
@"Cannot create App Delegate Proxy. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeAppDelegateSwizzling013],
|
||||
@"Cannot create App Delegate Proxy. %@",
|
||||
[GULAppDelegateSwizzler correctAlternativeWhenAppDelegateProxyNotCreated]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,17 +108,19 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
@"SceneDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
|
||||
if (!interceptor) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling000],
|
||||
@"SceneDelegateProxy cannot add nil interceptor.");
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling000],
|
||||
@"SceneDelegateProxy cannot add nil interceptor.");
|
||||
return nil;
|
||||
}
|
||||
if (![interceptor conformsToProtocol:@protocol(UISceneDelegate)]) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling001],
|
||||
@"SceneDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling001],
|
||||
@"SceneDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -126,10 +128,11 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
NSString *interceptorID =
|
||||
[NSString stringWithFormat:@"%@%p", kGULSceneDelegatePrefix, interceptor];
|
||||
if (!interceptorID.length) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling002],
|
||||
@"SceneDelegateProxy cannot create Interceptor ID.");
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling002],
|
||||
@"SceneDelegateProxy cannot create Interceptor ID.");
|
||||
return nil;
|
||||
}
|
||||
GULSceneZeroingWeakContainer *weakObject = [[GULSceneZeroingWeakContainer alloc] init];
|
||||
@@ -144,22 +147,24 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
@"SceneDelegateProxy cannot unregister empty interceptor ID.");
|
||||
|
||||
if (!interceptorID) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling003],
|
||||
@"SceneDelegateProxy cannot unregister empty interceptor ID.");
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling003],
|
||||
@"SceneDelegateProxy cannot unregister empty interceptor ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
GULSceneZeroingWeakContainer *weakContainer =
|
||||
[GULSceneDelegateSwizzler interceptors][interceptorID];
|
||||
if (!weakContainer.object) {
|
||||
GULLogError(kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling004],
|
||||
@"SceneDelegateProxy cannot unregister interceptor that was not registered. "
|
||||
"Interceptor ID %@",
|
||||
interceptorID);
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling004],
|
||||
@"SceneDelegateProxy cannot unregister interceptor that was not registered. "
|
||||
"Interceptor ID %@",
|
||||
interceptorID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -247,8 +252,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
IMP methodIMP = method_getImplementation(method);
|
||||
const char *types = method_getTypeEncoding(method);
|
||||
if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) {
|
||||
GULLogWarning(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling009],
|
||||
@"Cannot copy method to destination selector %@ as it already exists",
|
||||
@@ -285,8 +290,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
GULSceneZeroingWeakContainer *interceptorContainer = obj;
|
||||
id interceptor = interceptorContainer.object;
|
||||
if (!interceptor) {
|
||||
GULLogWarning(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling010],
|
||||
@"SceneDelegateProxy cannot find interceptor with ID %@. Removing the interceptor.", key);
|
||||
@@ -351,8 +356,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
[NSString stringWithFormat:@"%@-%@", classNameWithPrefix, [NSUUID UUID].UUIDString];
|
||||
|
||||
if (NSClassFromString(newClassName)) {
|
||||
GULLogError(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
@@ -367,8 +372,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
// size.
|
||||
Class sceneDelegateSubClass = objc_allocateClassPair(realClass, newClassName.UTF8String, 0);
|
||||
if (sceneDelegateSubClass == Nil) {
|
||||
GULLogError(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
@@ -401,8 +406,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
// cannot have more ivars/properties than its superclass since it will cause an offset in memory
|
||||
// that can lead to overwriting the isa of an object in the next frame.
|
||||
if (class_getInstanceSize(realClass) != class_getInstanceSize(sceneDelegateSubClass)) {
|
||||
GULLogError(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
@@ -417,8 +422,8 @@ static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
// Make the newly created class to be the subclass of the real Scene Delegate class.
|
||||
objc_registerClassPair(sceneDelegateSubClass);
|
||||
if (object_setClass(scene.delegate, sceneDelegateSubClass)) {
|
||||
GULLogDebug(
|
||||
kGULLoggerSwizzler, NO,
|
||||
GULOSLogDebug(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TargetConditionals.h>
|
||||
|
||||
#if !TARGET_OS_OSX
|
||||
#if __has_include(<UIKit/UIKit.h>)
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif // !TARGET_OS_OSX
|
||||
#endif
|
||||
|
||||
#if ((TARGET_OS_IOS || TARGET_OS_TV) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000))
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#define UISCENE_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
#endif // TARGET_OS_IOS
|
||||
|
||||
@implementation GULAppEnvironmentUtil
|
||||
|
||||
@@ -167,7 +167,7 @@ static BOOL HasEmbeddedMobileProvision(void) {
|
||||
model = @"watchOS Simulator";
|
||||
#elif TARGET_OS_TV
|
||||
model = @"tvOS Simulator";
|
||||
#elif defined(TARGET_OS_VISION) && TARGET_OS_VISION
|
||||
#elif TARGET_OS_VISION
|
||||
model = @"visionOS Simulator";
|
||||
#elif TARGET_OS_IOS
|
||||
switch ([[UIDevice currentDevice] userInterfaceIdiom]) {
|
||||
@@ -195,8 +195,7 @@ static BOOL HasEmbeddedMobileProvision(void) {
|
||||
+ (NSString *)systemVersion {
|
||||
#if TARGET_OS_IOS
|
||||
return [UIDevice currentDevice].systemVersion;
|
||||
#elif TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH || \
|
||||
(defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|
||||
#elif TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_VISION
|
||||
// Assemble the systemVersion, excluding the patch version if it's 0.
|
||||
NSOperatingSystemVersion osVersion = [NSProcessInfo processInfo].operatingSystemVersion;
|
||||
NSMutableString *versionString = [[NSMutableString alloc]
|
||||
@@ -218,22 +217,23 @@ static BOOL HasEmbeddedMobileProvision(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)isIOS7OrHigher {
|
||||
return YES;
|
||||
+ (BOOL)isAppClip {
|
||||
#if TARGET_OS_IOS
|
||||
// Documented by <a
|
||||
// href="https://developer.apple.com/documentation/bundleresources/information-property-list/nsappclip">Apple</a>
|
||||
// App clips have an NSAppClip entry in the top level of their Info.plist.
|
||||
NSDictionary *appClipEntry = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSAppClip"];
|
||||
return appClipEntry != nil;
|
||||
#elif TARGET_OS_OSX || TARGET_OS_TV || TARGET_OS_WATCH || TARGET_OS_VISION
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)hasSwiftRuntime {
|
||||
// The class
|
||||
// [Swift._SwiftObject](https://github.com/apple/swift/blob/5eac3e2818eb340b11232aff83edfbd1c307fa03/stdlib/public/runtime/SwiftObject.h#L35)
|
||||
// is a part of Swift runtime, so it should be present if Swift runtime is available.
|
||||
|
||||
BOOL hasSwiftRuntime =
|
||||
objc_lookUpClass("Swift._SwiftObject") != nil ||
|
||||
// Swift object class name before
|
||||
// https://github.com/apple/swift/commit/9637b4a6e11ddca72f5f6dbe528efc7c92f14d01
|
||||
objc_getClass("_TtCs12_SwiftObject") != nil;
|
||||
|
||||
return hasSwiftRuntime;
|
||||
+ (BOOL)supportsBackgroundURLSessionUploads {
|
||||
// Neither app extensions nor App Clips support background uploads.
|
||||
BOOL isExtension = self.isAppExtension;
|
||||
BOOL isAppClip = self.isAppClip;
|
||||
return !(isExtension || isAppClip);
|
||||
}
|
||||
|
||||
+ (NSString *)applePlatform {
|
||||
@@ -243,8 +243,7 @@ static BOOL HasEmbeddedMobileProvision(void) {
|
||||
// `true`, which means the condition list is order-sensitive.
|
||||
#if TARGET_OS_MACCATALYST
|
||||
applePlatform = @"maccatalyst";
|
||||
#elif TARGET_OS_IOS && (!defined(TARGET_OS_VISION) || !TARGET_OS_VISION)
|
||||
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
|
||||
#elif TARGET_OS_IOS
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// Early iOS 14 betas do not include isiOSAppOnMac (#6969)
|
||||
applePlatform = ([[NSProcessInfo processInfo] respondsToSelector:@selector(isiOSAppOnMac)] &&
|
||||
@@ -254,17 +253,13 @@ static BOOL HasEmbeddedMobileProvision(void) {
|
||||
} else {
|
||||
applePlatform = @"ios";
|
||||
}
|
||||
#else // defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
|
||||
applePlatform = @"ios";
|
||||
#endif // defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 140000
|
||||
|
||||
#elif TARGET_OS_TV
|
||||
applePlatform = @"tvos";
|
||||
#elif TARGET_OS_OSX
|
||||
applePlatform = @"macos";
|
||||
#elif TARGET_OS_WATCH
|
||||
applePlatform = @"watchos";
|
||||
#elif defined(TARGET_OS_VISION) && TARGET_OS_VISION
|
||||
#elif TARGET_OS_VISION
|
||||
applePlatform = @"visionos";
|
||||
#endif // TARGET_OS_MACCATALYST
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Google
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULHeartbeatDateStorage.h"
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULSecureCoding.h"
|
||||
|
||||
NSString *const kGULHeartbeatStorageDirectory = @"Google/FIRApp";
|
||||
|
||||
@interface GULHeartbeatDateStorage ()
|
||||
|
||||
/** The name of the file that stores heartbeat information. */
|
||||
@property(nonatomic, readonly) NSString *fileName;
|
||||
@end
|
||||
|
||||
@implementation GULHeartbeatDateStorage
|
||||
|
||||
@synthesize fileURL = _fileURL;
|
||||
|
||||
- (instancetype)initWithFileName:(NSString *)fileName {
|
||||
if (fileName == nil) return nil;
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_fileName = fileName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/** Lazy getter for fileURL.
|
||||
* @return fileURL where heartbeat information is stored.
|
||||
*/
|
||||
- (NSURL *)fileURL {
|
||||
if (!_fileURL) {
|
||||
NSURL *directoryURL = [self directoryPathURL];
|
||||
[self checkAndCreateDirectory:directoryURL];
|
||||
_fileURL = [directoryURL URLByAppendingPathComponent:_fileName];
|
||||
}
|
||||
return _fileURL;
|
||||
}
|
||||
|
||||
/** Returns the URL path of the directory for heartbeat storage data.
|
||||
* @return the URL path of the directory for heartbeat storage data.
|
||||
*/
|
||||
- (NSURL *)directoryPathURL {
|
||||
NSArray<NSString *> *paths;
|
||||
#if TARGET_OS_TV
|
||||
paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
#else
|
||||
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
#endif // TARGET_OS_TV
|
||||
NSString *rootPath = [paths lastObject];
|
||||
NSURL *rootURL = [NSURL fileURLWithPath:rootPath];
|
||||
NSURL *directoryURL = [rootURL URLByAppendingPathComponent:kGULHeartbeatStorageDirectory];
|
||||
return directoryURL;
|
||||
}
|
||||
|
||||
/** Check for the existence of the directory specified by the URL, and create it if it does not
|
||||
* exist.
|
||||
* @param directoryPathURL The path to the directory that needs to exist.
|
||||
*/
|
||||
- (void)checkAndCreateDirectory:(NSURL *)directoryPathURL {
|
||||
NSError *error;
|
||||
if (![directoryPathURL checkResourceIsReachableAndReturnError:&error]) {
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] createDirectoryAtURL:directoryPathURL
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&error];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag {
|
||||
@synchronized(self.class) {
|
||||
NSDictionary *heartbeatDictionary = [self heartbeatDictionaryWithFileURL:self.fileURL];
|
||||
NSDate *heartbeatDate = heartbeatDictionary[tag];
|
||||
|
||||
// Validate the value type. If the storage file was corrupted or updated with a different format
|
||||
// by a newer SDK version the value type may be different.
|
||||
if (![heartbeatDate isKindOfClass:[NSDate class]]) {
|
||||
heartbeatDate = nil;
|
||||
}
|
||||
|
||||
return heartbeatDate;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag {
|
||||
// Synchronize on the class to ensure that the different instances of the class will not access
|
||||
// the same file concurrently.
|
||||
// TODO: Consider a different synchronization strategy here and in `-heartbeatDateForTag:` method.
|
||||
// Currently no heartbeats can be read/written concurrently even if they are in different files.
|
||||
@synchronized(self.class) {
|
||||
NSMutableDictionary *heartbeatDictionary =
|
||||
[[self heartbeatDictionaryWithFileURL:self.fileURL] mutableCopy];
|
||||
heartbeatDictionary[tag] = date;
|
||||
NSError *error;
|
||||
BOOL isSuccess = [self writeDictionary:[heartbeatDictionary copy]
|
||||
forWritingURL:self.fileURL
|
||||
error:&error];
|
||||
return isSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)heartbeatDictionaryWithFileURL:(NSURL *)readingFileURL {
|
||||
NSDictionary *heartbeatDictionary;
|
||||
|
||||
NSError *error;
|
||||
NSData *objectData = [NSData dataWithContentsOfURL:readingFileURL options:0 error:&error];
|
||||
|
||||
if (objectData.length > 0 && error == nil) {
|
||||
NSSet<Class> *objectClasses =
|
||||
[NSSet setWithArray:@[ NSDictionary.class, NSDate.class, NSString.class ]];
|
||||
heartbeatDictionary = [GULSecureCoding unarchivedObjectOfClasses:objectClasses
|
||||
fromData:objectData
|
||||
error:&error];
|
||||
}
|
||||
|
||||
if (heartbeatDictionary.count == 0 || error != nil) {
|
||||
heartbeatDictionary = [NSDictionary dictionary];
|
||||
}
|
||||
|
||||
return heartbeatDictionary;
|
||||
}
|
||||
|
||||
- (BOOL)writeDictionary:(NSDictionary *)dictionary
|
||||
forWritingURL:(NSURL *)writingFileURL
|
||||
error:(NSError **)outError {
|
||||
// Archive a mutable copy `dictionary` for writing to disk. This is done for
|
||||
// backwards compatibility. See Google Utilities issue #36 for more context.
|
||||
// TODO: Remove usage of mutable copy in a future version of Google Utilities.
|
||||
NSData *data = [GULSecureCoding archivedDataWithRootObject:[dictionary mutableCopy]
|
||||
error:outError];
|
||||
if (data.length == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [data writeToURL:writingFileURL atomically:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULHeartbeatDateStorageUserDefaults.h"
|
||||
|
||||
@interface GULHeartbeatDateStorageUserDefaults ()
|
||||
|
||||
/** The storage to store the date of the last sent heartbeat. */
|
||||
@property(nonatomic, readonly) NSUserDefaults *userDefaults;
|
||||
|
||||
/** The key for user defaults to store heartbeat information. */
|
||||
@property(nonatomic, readonly) NSString *key;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GULHeartbeatDateStorageUserDefaults
|
||||
|
||||
- (instancetype)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_userDefaults = defaults;
|
||||
_key = key;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)heartbeatDictionaryFromDefaults {
|
||||
NSDictionary *heartbeatDict = [self.userDefaults objectForKey:self.key];
|
||||
if (heartbeatDict != nil) {
|
||||
return [heartbeatDict mutableCopy];
|
||||
} else {
|
||||
return [NSMutableDictionary dictionary];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag {
|
||||
NSDate *date = nil;
|
||||
@synchronized(self.userDefaults) {
|
||||
NSMutableDictionary *dict = [self heartbeatDictionaryFromDefaults];
|
||||
date = dict[tag];
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag {
|
||||
@synchronized(self.userDefaults) {
|
||||
NSMutableDictionary *dict = [self heartbeatDictionaryFromDefaults];
|
||||
dict[tag] = date;
|
||||
[self.userDefaults setObject:dict forKey:self.key];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright 2019 Google
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULSecureCoding.h"
|
||||
|
||||
NSString *const kGULSecureCodingError = @"GULSecureCodingError";
|
||||
|
||||
@implementation GULSecureCoding
|
||||
|
||||
+ (nullable id)unarchivedObjectOfClasses:(NSSet<Class> *)classes
|
||||
fromData:(NSData *)data
|
||||
error:(NSError **)outError {
|
||||
id object;
|
||||
#if __has_builtin(__builtin_available)
|
||||
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
|
||||
object = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:data error:outError];
|
||||
} else
|
||||
#endif // __has_builtin(__builtin_available)
|
||||
{
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
|
||||
#pragma clang diagnostic pop
|
||||
unarchiver.requiresSecureCoding = YES;
|
||||
|
||||
object = [unarchiver decodeObjectOfClasses:classes forKey:NSKeyedArchiveRootObjectKey];
|
||||
} @catch (NSException *exception) {
|
||||
if (outError) {
|
||||
*outError = [self archivingErrorWithException:exception];
|
||||
}
|
||||
}
|
||||
|
||||
if (object == nil && outError && *outError == nil) {
|
||||
NSString *failureReason = @"NSKeyedUnarchiver failed to unarchive data.";
|
||||
*outError = [NSError errorWithDomain:kGULSecureCodingError
|
||||
code:-1
|
||||
userInfo:@{NSLocalizedFailureReasonErrorKey : failureReason}];
|
||||
}
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
+ (nullable id)unarchivedObjectOfClass:(Class)class
|
||||
fromData:(NSData *)data
|
||||
error:(NSError **)outError {
|
||||
return [self unarchivedObjectOfClasses:[NSSet setWithObject:class] fromData:data error:outError];
|
||||
}
|
||||
|
||||
+ (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError {
|
||||
NSData *archiveData;
|
||||
#if __has_builtin(__builtin_available)
|
||||
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)) {
|
||||
archiveData = [NSKeyedArchiver archivedDataWithRootObject:object
|
||||
requiringSecureCoding:YES
|
||||
error:outError];
|
||||
} else
|
||||
#endif // __has_builtin(__builtin_available)
|
||||
{
|
||||
@try {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
|
||||
#pragma clang diagnostic pop
|
||||
archiver.requiresSecureCoding = YES;
|
||||
|
||||
[archiver encodeObject:object forKey:NSKeyedArchiveRootObjectKey];
|
||||
[archiver finishEncoding];
|
||||
|
||||
archiveData = [data copy];
|
||||
} @catch (NSException *exception) {
|
||||
if (outError) {
|
||||
*outError = [self archivingErrorWithException:exception];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return archiveData;
|
||||
}
|
||||
|
||||
+ (NSError *)archivingErrorWithException:(NSException *)exception {
|
||||
NSString *failureReason = [NSString
|
||||
stringWithFormat:@"NSKeyedArchiver exception with name: %@, reason: %@, userInfo: %@",
|
||||
exception.name, exception.reason, exception.userInfo];
|
||||
NSDictionary *errorUserInfo = @{NSLocalizedFailureReasonErrorKey : failureReason};
|
||||
|
||||
return [NSError errorWithDomain:kGULSecureCodingError code:-1 userInfo:errorUserInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,7 +20,6 @@
|
||||
#if __has_include("CoreTelephony/CTTelephonyNetworkInfo.h") && !TARGET_OS_MACCATALYST && \
|
||||
!TARGET_OS_OSX && !TARGET_OS_TV && !TARGET_OS_WATCH
|
||||
#define TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
#import <CoreTelephony/CTCarrier.h>
|
||||
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
@@ -38,61 +37,6 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (NSString *_Nullable)getNetworkMobileCountryCode {
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
CTTelephonyNetworkInfo *networkInfo = [GULNetworkInfo getNetworkInfo];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CTCarrier *provider = networkInfo.subscriberCellularProvider;
|
||||
#pragma clang diagnostic push
|
||||
return provider.mobileCountryCode;
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString *_Nullable)getNetworkMobileNetworkCode {
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
CTTelephonyNetworkInfo *networkInfo = [GULNetworkInfo getNetworkInfo];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
CTCarrier *provider = networkInfo.subscriberCellularProvider;
|
||||
#pragma clang diagnostic push
|
||||
return provider.mobileNetworkCode;
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted MccMnc if the inputs are valid, otherwise nil
|
||||
* @param mcc The Mobile Country Code returned from `getNetworkMobileCountryCode`
|
||||
* @param mnc The Mobile Network Code returned from `getNetworkMobileNetworkCode`
|
||||
* @returns A string with the concatenated mccMnc if both inputs are valid, otherwise nil
|
||||
*/
|
||||
+ (NSString *_Nullable)formatMcc:(NSString *)mcc andMNC:(NSString *)mnc {
|
||||
// These are both nil if the target does not support mobile connectivity
|
||||
if (mcc == nil && mnc == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (mcc.length != 3 || mnc.length < 2 || mnc.length > 3) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// If the resulting appended mcc + mnc contains characters that are not
|
||||
// decimal digits, return nil
|
||||
static NSCharacterSet *notDigits;
|
||||
static dispatch_once_t token;
|
||||
dispatch_once(&token, ^{
|
||||
notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
|
||||
});
|
||||
NSString *mccMnc = [mcc stringByAppendingString:mnc];
|
||||
if ([mccMnc rangeOfCharacterFromSet:notDigits].location != NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return mccMnc;
|
||||
}
|
||||
|
||||
+ (GULNetworkType)getNetworkType {
|
||||
GULNetworkType networkType = GULNetworkTypeNone;
|
||||
|
||||
@@ -126,13 +70,11 @@
|
||||
+ (NSString *)getNetworkRadioType {
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
CTTelephonyNetworkInfo *networkInfo = [GULNetworkInfo getNetworkInfo];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return networkInfo.currentRadioAccessTechnology;
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
return @"";
|
||||
if (networkInfo.serviceCurrentRadioAccessTechnology.count) {
|
||||
return networkInfo.serviceCurrentRadioAccessTechnology.allValues[0] ?: @"";
|
||||
}
|
||||
#endif
|
||||
return @"";
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -46,12 +46,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// Indicates whether it is running inside an extension or an app.
|
||||
+ (BOOL)isAppExtension;
|
||||
|
||||
/// @return Returns @YES when is run on iOS version greater or equal to 7.0
|
||||
+ (BOOL)isIOS7OrHigher DEPRECATED_MSG_ATTRIBUTE(
|
||||
"Always `YES` because only iOS 8 and higher supported. The method will be removed.");
|
||||
/// Indicates whether it is running inside an app clip or a full app.
|
||||
+ (BOOL)isAppClip;
|
||||
|
||||
/// @return YES if Swift runtime detected in the app.
|
||||
+ (BOOL)hasSwiftRuntime __deprecated;
|
||||
/// Indicates whether the current target supports background URL session uploads.
|
||||
/// App extensions and app clips do not support background URL sessions.
|
||||
+ (BOOL)supportsBackgroundURLSessionUploads;
|
||||
|
||||
/// @return An Apple platform. Possible values "ios", "tvos", "macos", "watchos", "maccatalyst", and
|
||||
/// "visionos".
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Describes an object that can store and fetch heartbeat dates for given tags.
|
||||
*/
|
||||
@protocol GULHeartbeatDateStorable <NSObject>
|
||||
|
||||
/**
|
||||
* Reads the date from the specified file for the given tag.
|
||||
* @return Returns date if exists, otherwise `nil`.
|
||||
*/
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag;
|
||||
|
||||
/**
|
||||
* Saves the date for the specified tag in the specified file.
|
||||
* @return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Google
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "GULHeartbeatDateStorable.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The name of the directory where the heartbeat data is stored.
|
||||
extern NSString *const kGULHeartbeatStorageDirectory;
|
||||
|
||||
/// Stores either a date or a dictionary to a specified file.
|
||||
@interface GULHeartbeatDateStorage : NSObject <GULHeartbeatDateStorable>
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@property(nonatomic, readonly) NSURL *fileURL;
|
||||
|
||||
/**
|
||||
* Default initializer.
|
||||
* @param fileName The name of the file to store the date information.
|
||||
* exist, it will be created if needed.
|
||||
*/
|
||||
- (instancetype)initWithFileName:(NSString *)fileName;
|
||||
|
||||
/**
|
||||
* Reads the date from the specified file for the given tag.
|
||||
* @return Returns date if exists, otherwise `nil`.
|
||||
*/
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag;
|
||||
|
||||
/**
|
||||
* Saves the date for the specified tag in the specified file.
|
||||
* @return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "GULHeartbeatDateStorable.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Stores either a date or a dictionary to a specified file.
|
||||
@interface GULHeartbeatDateStorageUserDefaults : NSObject <GULHeartbeatDateStorable>
|
||||
|
||||
/**
|
||||
* Default initializer. tvOS can only write to the cache directory and
|
||||
* there are no guarantees that the directory will persist. User defaults will
|
||||
* be retained, so that should be used instead.
|
||||
* @param defaults User defaults instance to store the heartbeat information.
|
||||
* @param key The key to be used with the user defaults instance.
|
||||
*/
|
||||
- (instancetype)initWithDefaults:(NSUserDefaults *)defaults key:(NSString *)key;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Reads the date from the specified file for the given tag.
|
||||
* @return Returns date if exists, otherwise `nil`.
|
||||
*/
|
||||
- (nullable NSDate *)heartbeatDateForTag:(NSString *)tag;
|
||||
|
||||
/**
|
||||
* Saves the date for the specified tag in the specified file.
|
||||
* @return YES on success, NO otherwise.
|
||||
*/
|
||||
- (BOOL)setHearbeatDate:(NSDate *)date forTag:(NSString *)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FBLPromise<ValueType>;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The class provides a convenient, multiplatform abstraction of the Keychain.
|
||||
@@ -34,42 +32,46 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (instancetype)initWithService:(NSString *)service;
|
||||
|
||||
/**
|
||||
* Get an object by key.
|
||||
* @param key The key.
|
||||
* @param objectClass The expected object class required by `NSSecureCoding`.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns a promise. It is resolved with an object stored by key if exists. It is resolved
|
||||
* with `nil` when the object not found. It fails on a Keychain error.
|
||||
*/
|
||||
- (FBLPromise<id<NSSecureCoding>> *)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
/// Get an object by key.
|
||||
/// @param key The key.
|
||||
/// @param objectClass The expected object class required by `NSSecureCoding`.
|
||||
/// @param accessGroup The Keychain Access Group.
|
||||
/// @param completionHandler The completion handler to call when the
|
||||
/// synchronized keychain read is complete. An error is passed to the
|
||||
/// completion handler if the keychain read fails. Else, the object stored in
|
||||
/// the keychain, or `nil` if it does not exist, is passed to the completion
|
||||
/// handler.
|
||||
- (void)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:
|
||||
(void (^)(id<NSSecureCoding> _Nullable obj, NSError *_Nullable error))completionHandler;
|
||||
|
||||
/**
|
||||
* Saves the given object by the given key.
|
||||
* @param object The object to store.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
/// Saves the given object by the given key.
|
||||
/// @param object The object to store.
|
||||
/// @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
/// overridden.
|
||||
/// @param accessGroup The Keychain Access Group.
|
||||
/// @param completionHandler The completion handler to call when the
|
||||
/// synchronized keychain write is complete. An error is passed to the
|
||||
/// completion handler if the keychain read fails. Else, the object written to
|
||||
/// the keychain is passed to the completion handler.
|
||||
- (void)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:
|
||||
(void (^)(id<NSSecureCoding> _Nullable obj, NSError *_Nullable error))completionHandler;
|
||||
|
||||
/**
|
||||
* Removes the object by the given key.
|
||||
* @param key The key to store the object. If there is an existing object by the key, it will be
|
||||
* overridden.
|
||||
* @param accessGroup The Keychain Access Group.
|
||||
*
|
||||
* @return Returns which is resolved with `[NSNull null]` on success.
|
||||
*/
|
||||
- (FBLPromise<NSNull *> *)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup;
|
||||
/// Removes the object by the given key.
|
||||
/// @param key The key to store the object. If there is an existing object by
|
||||
/// the key, it will be overridden.
|
||||
/// @param accessGroup The Keychain Access Group.
|
||||
/// @param completionHandler The completion handler to call when the
|
||||
/// synchronized keychain removal is complete. An error is passed to the
|
||||
/// completion handler if the keychain removal fails.
|
||||
- (void)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:(void (^)(NSError *_Nullable error))completionHandler;
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
/// If not `nil`, then only this keychain will be used to save and read data (see
|
||||
|
||||
@@ -27,20 +27,6 @@ typedef NS_ENUM(NSInteger, GULNetworkType) {
|
||||
/// Collection of utilities to read network status information
|
||||
@interface GULNetworkInfo : NSObject
|
||||
|
||||
/// Returns the cellular mobile country code (mcc) if CoreTelephony is supported, otherwise nil
|
||||
+ (NSString *_Nullable)getNetworkMobileCountryCode;
|
||||
|
||||
/// Returns the cellular mobile network code (mnc) if CoreTelephony is supported, otherwise nil
|
||||
+ (NSString *_Nullable)getNetworkMobileNetworkCode;
|
||||
|
||||
/**
|
||||
* Returns the formatted MccMnc if the inputs are valid, otherwise nil
|
||||
* @param mcc The Mobile Country Code returned from `getNetworkMobileCountryCode`
|
||||
* @param mnc The Mobile Network Code returned from `getNetworkMobileNetworkCode`
|
||||
* @returns A string with the concatenated mccMnc if both inputs are valid, otherwise nil
|
||||
*/
|
||||
+ (NSString *_Nullable)formatMcc:(NSString *_Nullable)mcc andMNC:(NSString *_Nullable)mnc;
|
||||
|
||||
/// Returns an enum indicating the network type. The enum values should be easily transferrable to
|
||||
/// the NetworkType value in android/play/playlog/proto/clientanalytics.proto. Right now this always
|
||||
/// returns None on platforms other than iOS. This should be updated in the future to return Wi-Fi
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright 2019 Google
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** The class wraps `NSKeyedArchiver` and `NSKeyedUnarchiver` API to provide a unified secure coding
|
||||
* methods for iOS versions before and after 11.
|
||||
*/
|
||||
@interface GULSecureCoding : NSObject
|
||||
|
||||
+ (nullable id)unarchivedObjectOfClasses:(NSSet<Class> *)classes
|
||||
fromData:(NSData *)data
|
||||
error:(NSError **)outError;
|
||||
|
||||
+ (nullable id)unarchivedObjectOfClass:(Class)class
|
||||
fromData:(NSData *)data
|
||||
error:(NSError **)outError;
|
||||
|
||||
+ (nullable NSData *)archivedDataWithRootObject:(id<NSCoding>)object error:(NSError **)outError;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** The class represents HTTP response received from `NSURLSession`. */
|
||||
@interface GULURLSessionDataResponse : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSHTTPURLResponse *HTTPResponse;
|
||||
@property(nonatomic, nullable, readonly) NSData *HTTPBody;
|
||||
|
||||
- (instancetype)initWithResponse:(NSHTTPURLResponse *)response HTTPBody:(nullable NSData *)body;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FBLPromise<Value>;
|
||||
@class GULURLSessionDataResponse;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** Promise based API for `NSURLSession`. */
|
||||
@interface NSURLSession (GULPromises)
|
||||
|
||||
/** Creates a promise wrapping `-[NSURLSession dataTaskWithRequest:completionHandler:]` method.
|
||||
* @param URLRequest The request to create a data task with.
|
||||
* @return A promise that is fulfilled when an HTTP response is received (with any response code),
|
||||
* or is rejected with the error passed to the task completion.
|
||||
*/
|
||||
- (FBLPromise<GULURLSessionDataResponse *> *)gul_dataTaskPromiseWithRequest:
|
||||
(NSURLRequest *)URLRequest;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -17,14 +17,7 @@
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainStorage.h"
|
||||
#import <Security/Security.h>
|
||||
|
||||
#if __has_include(<FBLPromises/FBLPromises.h>)
|
||||
#import <FBLPromises/FBLPromises.h>
|
||||
#else
|
||||
#import "FBLPromises.h"
|
||||
#endif
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainUtils.h"
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULSecureCoding.h"
|
||||
|
||||
@interface GULKeychainStorage ()
|
||||
@property(nonatomic, readonly) dispatch_queue_t keychainQueue;
|
||||
@@ -57,108 +50,113 @@
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (FBLPromise<id<NSSecureCoding>> *)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup {
|
||||
return [FBLPromise onQueue:self.inMemoryCacheQueue
|
||||
do:^id _Nullable {
|
||||
// Return cached object or fail otherwise.
|
||||
id object = [self.inMemoryCache objectForKey:key];
|
||||
return object
|
||||
?: [[NSError alloc]
|
||||
initWithDomain:FBLPromiseErrorDomain
|
||||
code:FBLPromiseErrorCodeValidationFailure
|
||||
userInfo:nil];
|
||||
}]
|
||||
.recover(^id _Nullable(NSError *error) {
|
||||
// Look for the object in the keychain.
|
||||
return [self getObjectFromKeychainForKey:key
|
||||
objectClass:objectClass
|
||||
accessGroup:accessGroup];
|
||||
});
|
||||
- (void)getObjectForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:
|
||||
(void (^)(id<NSSecureCoding> _Nullable obj, NSError *_Nullable error))completionHandler {
|
||||
dispatch_async(self.inMemoryCacheQueue, ^{
|
||||
// Return cached object or fail otherwise.
|
||||
id object = [self.inMemoryCache objectForKey:key];
|
||||
if (object) {
|
||||
completionHandler(object, nil);
|
||||
} else {
|
||||
// Look for the object in the keychain.
|
||||
[self getObjectFromKeychainForKey:key
|
||||
objectClass:objectClass
|
||||
accessGroup:accessGroup
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (FBLPromise<NSNull *> *)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup {
|
||||
return [FBLPromise onQueue:self.inMemoryCacheQueue
|
||||
do:^id _Nullable {
|
||||
// Save to the in-memory cache first.
|
||||
[self.inMemoryCache setObject:object forKey:[key copy]];
|
||||
return [NSNull null];
|
||||
}]
|
||||
.thenOn(self.keychainQueue, ^id(id result) {
|
||||
// Then store the object to the keychain.
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
NSError *error;
|
||||
NSData *encodedObject = [GULSecureCoding archivedDataWithRootObject:object error:&error];
|
||||
if (!encodedObject) {
|
||||
return error;
|
||||
}
|
||||
- (void)setObject:(id<NSSecureCoding>)object
|
||||
forKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:
|
||||
(void (^)(id<NSSecureCoding> _Nullable obj, NSError *_Nullable error))completionHandler {
|
||||
dispatch_async(self.inMemoryCacheQueue, ^{
|
||||
// Save to the in-memory cache first.
|
||||
[self.inMemoryCache setObject:object forKey:[key copy]];
|
||||
|
||||
if (![GULKeychainUtils setItem:encodedObject withQuery:query error:&error]) {
|
||||
return error;
|
||||
}
|
||||
dispatch_async(self.keychainQueue, ^{
|
||||
// Then store the object to the keychain.
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
NSError *error;
|
||||
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object
|
||||
requiringSecureCoding:YES
|
||||
error:&error];
|
||||
if (!encodedObject) {
|
||||
completionHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
return [NSNull null];
|
||||
});
|
||||
if (![GULKeychainUtils setItem:encodedObject withQuery:query error:&error]) {
|
||||
completionHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
completionHandler(object, nil);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (FBLPromise<NSNull *> *)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup {
|
||||
return [FBLPromise onQueue:self.inMemoryCacheQueue
|
||||
do:^id _Nullable {
|
||||
[self.inMemoryCache removeObjectForKey:key];
|
||||
return nil;
|
||||
}]
|
||||
.thenOn(self.keychainQueue, ^id(id result) {
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
- (void)removeObjectForKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:(void (^)(NSError *_Nullable error))completionHandler {
|
||||
dispatch_async(self.inMemoryCacheQueue, ^{
|
||||
[self.inMemoryCache removeObjectForKey:key];
|
||||
dispatch_async(self.keychainQueue, ^{
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
|
||||
NSError *error;
|
||||
if (![GULKeychainUtils removeItemWithQuery:query error:&error]) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return [NSNull null];
|
||||
});
|
||||
NSError *error;
|
||||
if (![GULKeychainUtils removeItemWithQuery:query error:&error]) {
|
||||
completionHandler(error);
|
||||
} else {
|
||||
completionHandler(nil);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (FBLPromise<id<NSSecureCoding>> *)getObjectFromKeychainForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup {
|
||||
- (void)getObjectFromKeychainForKey:(NSString *)key
|
||||
objectClass:(Class)objectClass
|
||||
accessGroup:(nullable NSString *)accessGroup
|
||||
completionHandler:(void (^)(id<NSSecureCoding> _Nullable obj,
|
||||
NSError *_Nullable error))completionHandler {
|
||||
// Look for the object in the keychain.
|
||||
return [FBLPromise
|
||||
onQueue:self.keychainQueue
|
||||
do:^id {
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
NSError *error;
|
||||
NSData *encodedObject = [GULKeychainUtils getItemWithQuery:query error:&error];
|
||||
dispatch_async(self.keychainQueue, ^{
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
NSError *error;
|
||||
NSData *encodedObject = [GULKeychainUtils getItemWithQuery:query error:&error];
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (!encodedObject) {
|
||||
return nil;
|
||||
}
|
||||
id object = [GULSecureCoding unarchivedObjectOfClass:objectClass
|
||||
fromData:encodedObject
|
||||
error:&error];
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (error) {
|
||||
completionHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
if (!encodedObject) {
|
||||
completionHandler(nil, nil);
|
||||
return;
|
||||
}
|
||||
id object = [NSKeyedUnarchiver unarchivedObjectOfClass:objectClass
|
||||
fromData:encodedObject
|
||||
error:&error];
|
||||
if (error) {
|
||||
completionHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
return object;
|
||||
}]
|
||||
.thenOn(self.inMemoryCacheQueue,
|
||||
^id<NSSecureCoding> _Nullable(id<NSSecureCoding> _Nullable object) {
|
||||
// Save object to the in-memory cache if exists and return the object.
|
||||
if (object) {
|
||||
[self.inMemoryCache setObject:object forKey:[key copy]];
|
||||
}
|
||||
return object;
|
||||
});
|
||||
dispatch_async(self.inMemoryCacheQueue, ^{
|
||||
// Save object to the in-memory cache if exists and return the object.
|
||||
if (object) {
|
||||
[self.inMemoryCache setObject:object forKey:[key copy]];
|
||||
}
|
||||
|
||||
completionHandler(object, nil);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)resetInMemoryCache {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULURLSessionDataResponse.h"
|
||||
|
||||
@implementation GULURLSessionDataResponse
|
||||
|
||||
- (instancetype)initWithResponse:(NSHTTPURLResponse *)response HTTPBody:(NSData *)body {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_HTTPResponse = response;
|
||||
_HTTPBody = body;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/NSURLSession+GULPromises.h"
|
||||
|
||||
#if __has_include(<FBLPromises/FBLPromises.h>)
|
||||
#import <FBLPromises/FBLPromises.h>
|
||||
#else
|
||||
#import "FBLPromises.h"
|
||||
#endif
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULURLSessionDataResponse.h"
|
||||
|
||||
@implementation NSURLSession (GULPromises)
|
||||
|
||||
- (FBLPromise<GULURLSessionDataResponse *> *)gul_dataTaskPromiseWithRequest:
|
||||
(NSURLRequest *)URLRequest {
|
||||
return [FBLPromise async:^(FBLPromiseFulfillBlock fulfill, FBLPromiseRejectBlock reject) {
|
||||
[[self dataTaskWithRequest:URLRequest
|
||||
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
|
||||
NSError *_Nullable error) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
fulfill([[GULURLSessionDataResponse alloc]
|
||||
initWithResponse:(NSHTTPURLResponse *)response
|
||||
HTTPBody:data]);
|
||||
}
|
||||
}] resume];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
130
Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m
generated
130
Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m
generated
@@ -14,18 +14,13 @@
|
||||
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
#include <asl.h>
|
||||
#import <os/log.h>
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h"
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLoggerLevel.h"
|
||||
|
||||
/// ASL client facility name used by GULLogger.
|
||||
const char *kGULLoggerASLClientFacilityName = "com.google.utilities.logger";
|
||||
|
||||
static dispatch_once_t sGULLoggerOnceToken;
|
||||
|
||||
static aslclient sGULLoggerClient;
|
||||
|
||||
static dispatch_queue_t sGULClientQueue;
|
||||
|
||||
static BOOL sGULLoggerDebugMode;
|
||||
@@ -35,42 +30,26 @@ static GULLoggerLevel sGULLoggerMaximumLevel;
|
||||
// Allow clients to register a version to include in the log.
|
||||
static NSString *sVersion = @"";
|
||||
|
||||
NSString *const kGULLogSubsystem = @"com.google.utilities.logger";
|
||||
|
||||
static GULLoggerService kGULLoggerLogger = @"[GULLogger]";
|
||||
|
||||
static NSMutableDictionary<NSString *, NSMutableDictionary<GULLoggerService, os_log_t> *>
|
||||
*sGULServiceLogs;
|
||||
|
||||
#ifdef DEBUG
|
||||
/// The regex pattern for the message code.
|
||||
static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
|
||||
static NSRegularExpression *sMessageCodeRegex;
|
||||
#endif
|
||||
|
||||
void GULLoggerInitializeASL(void) {
|
||||
void GULLoggerInitialize(void) {
|
||||
dispatch_once(&sGULLoggerOnceToken, ^{
|
||||
NSInteger majorOSVersion = [[GULAppEnvironmentUtil systemVersion] integerValue];
|
||||
uint32_t aslOptions = ASL_OPT_STDERR;
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// The iOS 11 simulator doesn't need the ASL_OPT_STDERR flag.
|
||||
if (majorOSVersion >= 11) {
|
||||
aslOptions = 0;
|
||||
}
|
||||
#else
|
||||
// Devices running iOS 10 or higher don't need the ASL_OPT_STDERR flag.
|
||||
if (majorOSVersion >= 10) {
|
||||
aslOptions = 0;
|
||||
}
|
||||
#endif // TARGET_OS_SIMULATOR
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated
|
||||
// Initialize the ASL client handle.
|
||||
sGULLoggerClient = asl_open(NULL, kGULLoggerASLClientFacilityName, aslOptions);
|
||||
sGULLoggerMaximumLevel = GULLoggerLevelNotice;
|
||||
|
||||
// Set the filter used by system/device log. Initialize in default mode.
|
||||
asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE));
|
||||
|
||||
sGULClientQueue = dispatch_queue_create("GULLoggingClientQueue", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(sGULClientQueue,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
sGULServiceLogs = [NSMutableDictionary dictionary];
|
||||
#ifdef DEBUG
|
||||
sMessageCodeRegex = [NSRegularExpression regularExpressionWithPattern:kMessageCodePattern
|
||||
options:0
|
||||
@@ -79,10 +58,6 @@ void GULLoggerInitializeASL(void) {
|
||||
});
|
||||
}
|
||||
|
||||
void GULLoggerEnableSTDERR(void) {
|
||||
asl_add_log_file(sGULLoggerClient, STDERR_FILENO);
|
||||
}
|
||||
|
||||
void GULLoggerForceDebug(void) {
|
||||
// We should enable debug mode if we're not running from App Store.
|
||||
if (![GULAppEnvironmentUtil isFromAppStore]) {
|
||||
@@ -97,27 +72,24 @@ GULLoggerLevel GULGetLoggerLevel(void) {
|
||||
|
||||
__attribute__((no_sanitize("thread"))) void GULSetLoggerLevel(GULLoggerLevel loggerLevel) {
|
||||
if (loggerLevel < GULLoggerLevelMin || loggerLevel > GULLoggerLevelMax) {
|
||||
GULLogError(kGULLoggerLogger, NO, @"I-COR000023", @"Invalid logger level, %ld",
|
||||
(long)loggerLevel);
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerLogger, YES, @"I-COR000023",
|
||||
@"Invalid logger level, %ld", (long)loggerLevel);
|
||||
return;
|
||||
}
|
||||
GULLoggerInitializeASL();
|
||||
GULLoggerInitialize();
|
||||
// We should not raise the logger level if we are running from App Store.
|
||||
if (loggerLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) {
|
||||
return;
|
||||
}
|
||||
|
||||
sGULLoggerMaximumLevel = loggerLevel;
|
||||
dispatch_async(sGULClientQueue, ^{
|
||||
asl_set_filter(sGULLoggerClient, ASL_FILTER_MASK_UPTO(loggerLevel));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the level is high enough to be loggable.
|
||||
*/
|
||||
__attribute__((no_sanitize("thread"))) BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel) {
|
||||
GULLoggerInitializeASL();
|
||||
GULLoggerInitialize();
|
||||
if (sGULLoggerDebugMode) {
|
||||
return YES;
|
||||
}
|
||||
@@ -131,10 +103,6 @@ void GULResetLogger(void) {
|
||||
sGULLoggerMaximumLevel = GULLoggerLevelNotice;
|
||||
}
|
||||
|
||||
aslclient getGULLoggerClient(void) {
|
||||
return sGULLoggerClient;
|
||||
}
|
||||
|
||||
dispatch_queue_t getGULClientQueue(void) {
|
||||
return sGULClientQueue;
|
||||
}
|
||||
@@ -148,13 +116,28 @@ void GULLoggerRegisterVersion(NSString *version) {
|
||||
sVersion = version;
|
||||
}
|
||||
|
||||
void GULLogBasic(GULLoggerLevel level,
|
||||
GULLoggerService service,
|
||||
BOOL forceLog,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
va_list args_ptr) {
|
||||
GULLoggerInitializeASL();
|
||||
os_log_type_t GULLoggerLevelToOSLogType(GULLoggerLevel level) {
|
||||
switch (level) {
|
||||
case GULLoggerLevelError:
|
||||
return OS_LOG_TYPE_ERROR;
|
||||
case GULLoggerLevelWarning:
|
||||
case GULLoggerLevelNotice:
|
||||
return OS_LOG_TYPE_DEFAULT;
|
||||
case GULLoggerLevelInfo:
|
||||
return OS_LOG_TYPE_INFO;
|
||||
case GULLoggerLevelDebug:
|
||||
return OS_LOG_TYPE_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
void GULOSLogBasic(GULLoggerLevel level,
|
||||
NSString *subsystem,
|
||||
NSString *category,
|
||||
BOOL forceLog,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
va_list args_ptr) {
|
||||
GULLoggerInitialize();
|
||||
if (!(level <= sGULLoggerMaximumLevel || sGULLoggerDebugMode || forceLog)) {
|
||||
return;
|
||||
}
|
||||
@@ -172,12 +155,23 @@ void GULLogBasic(GULLoggerLevel level,
|
||||
} else {
|
||||
logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr];
|
||||
}
|
||||
logMsg = [NSString stringWithFormat:@"%@ - %@[%@] %@", sVersion, service, messageCode, logMsg];
|
||||
logMsg = [NSString stringWithFormat:@"%@ - %@[%@] %@", sVersion, category, messageCode, logMsg];
|
||||
dispatch_async(sGULClientQueue, ^{
|
||||
asl_log(sGULLoggerClient, NULL, (int)level, "%s", logMsg.UTF8String);
|
||||
NSMutableDictionary<GULLoggerService, os_log_t> *subsystemLogs = sGULServiceLogs[subsystem];
|
||||
if (!subsystemLogs) {
|
||||
subsystemLogs = [NSMutableDictionary dictionary];
|
||||
sGULServiceLogs[subsystem] = subsystemLogs;
|
||||
}
|
||||
|
||||
os_log_t serviceLog = [subsystemLogs objectForKey:subsystem];
|
||||
if (!serviceLog) {
|
||||
serviceLog = os_log_create(subsystem.UTF8String, category.UTF8String);
|
||||
subsystemLogs[category] = serviceLog;
|
||||
}
|
||||
|
||||
os_log_with_type(serviceLog, GULLoggerLevelToOSLogType(level), "%{public}@", logMsg);
|
||||
});
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/**
|
||||
* Generates the logging functions using macros.
|
||||
@@ -187,13 +181,14 @@ void GULLogBasic(GULLoggerLevel level,
|
||||
* Calling GULLogDebug({service}, @"I-XYZ000001", @"Configure succeed.") shows:
|
||||
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [{service}][I-XYZ000001] Configure succeed.
|
||||
*/
|
||||
#define GUL_LOGGING_FUNCTION(level) \
|
||||
void GULLog##level(GULLoggerService service, BOOL force, NSString *messageCode, \
|
||||
NSString *message, ...) { \
|
||||
va_list args_ptr; \
|
||||
va_start(args_ptr, message); \
|
||||
GULLogBasic(GULLoggerLevel##level, service, force, messageCode, message, args_ptr); \
|
||||
va_end(args_ptr); \
|
||||
#define GUL_LOGGING_FUNCTION(level) \
|
||||
void GULOSLog##level(NSString *subsystem, NSString *category, BOOL force, NSString *messageCode, \
|
||||
NSString *message, ...) { \
|
||||
va_list args_ptr; \
|
||||
va_start(args_ptr, message); \
|
||||
GULOSLogBasic(GULLoggerLevel##level, subsystem, category, force, messageCode, message, \
|
||||
args_ptr); \
|
||||
va_end(args_ptr); \
|
||||
}
|
||||
|
||||
GUL_LOGGING_FUNCTION(Error)
|
||||
@@ -202,18 +197,27 @@ GUL_LOGGING_FUNCTION(Notice)
|
||||
GUL_LOGGING_FUNCTION(Info)
|
||||
GUL_LOGGING_FUNCTION(Debug)
|
||||
|
||||
#undef GUL_MAKE_LOGGER
|
||||
#undef GUL_LOGGING_FUNCTION
|
||||
|
||||
#pragma mark - GULLoggerWrapper
|
||||
|
||||
@implementation GULLoggerWrapper
|
||||
|
||||
+ (void)logWithLevel:(GULLoggerLevel)level
|
||||
subsystem:(NSString *)subsystem
|
||||
category:(GULLoggerService)category
|
||||
messageCode:(NSString *)messageCode
|
||||
message:(NSString *)message
|
||||
arguments:(va_list)args {
|
||||
GULOSLogBasic(level, subsystem, category, NO, messageCode, message, args);
|
||||
}
|
||||
|
||||
+ (void)logWithLevel:(GULLoggerLevel)level
|
||||
withService:(GULLoggerService)service
|
||||
withCode:(NSString *)messageCode
|
||||
withMessage:(NSString *)message
|
||||
withArgs:(va_list)args {
|
||||
GULLogBasic(level, service, NO, messageCode, message, args);
|
||||
GULOSLogBasic(level, kGULLogSubsystem, service, NO, messageCode, message, args);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,6 +22,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The services used in the logger.
|
||||
*
|
||||
* DEPRECATED; use NSString instead.
|
||||
*/
|
||||
typedef NSString *const GULLoggerService;
|
||||
|
||||
@@ -29,24 +31,16 @@ typedef NSString *const GULLoggerService;
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
* Initialize GULLogger.
|
||||
*/
|
||||
extern void GULLoggerInitializeASL(void);
|
||||
/// Used for other GoogleUtilities logging.
|
||||
extern NSString *const kGULLogSubsystem;
|
||||
|
||||
/**
|
||||
* Override log level to Debug.
|
||||
*/
|
||||
/// Initialize GULLogger.
|
||||
extern void GULLoggerInitialize(void);
|
||||
|
||||
/// Override log level to Debug.
|
||||
void GULLoggerForceDebug(void);
|
||||
|
||||
/**
|
||||
* Turn on logging to STDERR.
|
||||
*/
|
||||
extern void GULLoggerEnableSTDERR(void);
|
||||
|
||||
/**
|
||||
* Gets the current GULLoggerLevel.
|
||||
*/
|
||||
/// Gets the current `GULLoggerLevel`.
|
||||
extern GULLoggerLevel GULGetLoggerLevel(void);
|
||||
|
||||
/**
|
||||
@@ -81,17 +75,18 @@ extern void GULLoggerRegisterVersion(NSString *version);
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
extern void GULLogBasic(GULLoggerLevel level,
|
||||
GULLoggerService service,
|
||||
BOOL forceLog,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
extern void GULOSLogBasic(GULLoggerLevel level,
|
||||
NSString *subsystem,
|
||||
NSString *category,
|
||||
BOOL forceLog,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
|
||||
// See: http://stackoverflow.com/q/29095469
|
||||
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
|
||||
va_list args_ptr
|
||||
va_list args_ptr
|
||||
#else
|
||||
va_list _Nullable args_ptr
|
||||
va_list _Nullable args_ptr
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -108,31 +103,36 @@ extern void GULLogBasic(GULLoggerLevel level,
|
||||
* Example usage:
|
||||
* GULLogError(kGULLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||
*/
|
||||
extern void GULLogError(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogWarning(GULLoggerService service,
|
||||
extern void GULOSLogError(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogNotice(GULLoggerService service,
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
extern void GULOSLogWarning(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
extern void GULOSLogNotice(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
extern void GULOSLogInfo(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogInfo(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
extern void GULLogDebug(GULLoggerService service,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(4, 5);
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
extern void GULOSLogDebug(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
@@ -140,24 +140,25 @@ extern void GULLogDebug(GULLoggerService service,
|
||||
|
||||
@interface GULLoggerWrapper : NSObject
|
||||
|
||||
/**
|
||||
* Objective-C wrapper for GULLogBasic to allow weak linking to GULLogger
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
* (required) service name of type GULLoggerService.
|
||||
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
|
||||
/// Objective-C wrapper for `GULOSLogBasic` to allow weak linking to `GULLogger`.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - level: The log level (one of the `GULLoggerLevel` enum values).
|
||||
/// - subsystem: An identifier for the subsystem performing logging, e.g., `com.example.logger`.
|
||||
/// - category: The category name within the `subsystem` to group related messages, e.g.,
|
||||
/// `[GoogleUtilities/Example]`.
|
||||
/// - messageCode: The message code starting with "I-" which means iOS, followed by a capitalized
|
||||
/// three-character service identifier and a six digit integer message ID that is unique within
|
||||
/// the service. An example of the message code is @"I-COR000001".
|
||||
/// - message: The message to log, which may be a format string.
|
||||
/// - args: The variable arguments list obtained from calling va_start, used when message is
|
||||
/// a format string; optional if `message` is not a format string.
|
||||
+ (void)logWithLevel:(GULLoggerLevel)level
|
||||
withService:(GULLoggerService)service
|
||||
withCode:(NSString *)messageCode
|
||||
withMessage:(NSString *)message
|
||||
withArgs:(va_list)args;
|
||||
subsystem:(NSString *)subsystem
|
||||
category:(NSString *)category
|
||||
messageCode:(NSString *)messageCode
|
||||
message:(NSString *)message
|
||||
arguments:(va_list)args;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -16,22 +16,32 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* The log levels used by internal logging.
|
||||
*/
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The log levels used by internal logging.
|
||||
typedef NS_ENUM(NSInteger, GULLoggerLevel) {
|
||||
/** Error level, matches ASL_LEVEL_ERR. */
|
||||
GULLoggerLevelError = 3,
|
||||
/** Warning level, matches ASL_LEVEL_WARNING. */
|
||||
GULLoggerLevelWarning = 4,
|
||||
/** Notice level, matches ASL_LEVEL_NOTICE. */
|
||||
GULLoggerLevelNotice = 5,
|
||||
/** Info level, matches ASL_LEVEL_INFO. */
|
||||
GULLoggerLevelInfo = 6,
|
||||
/** Debug level, matches ASL_LEVEL_DEBUG. */
|
||||
GULLoggerLevelDebug = 7,
|
||||
/** Minimum log level. */
|
||||
/// Error level, corresponding to `OS_LOG_TYPE_ERROR`.
|
||||
GULLoggerLevelError = 3, // For backwards compatibility, the enum value matches `ASL_LEVEL_ERR`.
|
||||
|
||||
/// Warning level, corresponding to `OS_LOG_TYPE_DEFAULT`.
|
||||
///
|
||||
/// > Note: Since OSLog doesn't have a WARNING type, this is equivalent to `GULLoggerLevelNotice`.
|
||||
GULLoggerLevelWarning = 4, // For backwards compatibility, the value matches `ASL_LEVEL_WARNING`.
|
||||
|
||||
/// Notice level, corresponding to `OS_LOG_TYPE_DEFAULT`.
|
||||
GULLoggerLevelNotice = 5, // For backwards compatibility, the value matches `ASL_LEVEL_NOTICE`.
|
||||
|
||||
/// Info level, corresponding to `OS_LOG_TYPE_INFO`.
|
||||
GULLoggerLevelInfo = 6, // For backwards compatibility, the enum value matches `ASL_LEVEL_INFO`.
|
||||
|
||||
/// Debug level, corresponding to `OS_LOG_TYPE_DEBUG`.
|
||||
GULLoggerLevelDebug = 7, // For backwards compatibility, the value matches `ASL_LEVEL_DEBUG`.
|
||||
|
||||
/// The minimum (most severe) supported logging level.
|
||||
GULLoggerLevelMin = GULLoggerLevelError,
|
||||
/** Maximum log level. */
|
||||
|
||||
/// The maximum (least severe) supported logging level.
|
||||
GULLoggerLevelMax = GULLoggerLevelDebug
|
||||
} NS_SWIFT_NAME(GoogleLoggerLevel);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -25,7 +25,7 @@ NSString *const GULNSDataZlibRemainingBytesKey = @"GULNSDataZlibRemainingBytesKe
|
||||
|
||||
@implementation NSData (GULGzip)
|
||||
|
||||
+ (NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error {
|
||||
+ (nullable NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error {
|
||||
const void *bytes = [data bytes];
|
||||
NSUInteger length = [data length];
|
||||
if (!bytes || !length) {
|
||||
@@ -119,7 +119,7 @@ NSString *const GULNSDataZlibRemainingBytesKey = @"GULNSDataZlibRemainingBytesKe
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error {
|
||||
+ (nullable NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error {
|
||||
const void *bytes = [data bytes];
|
||||
NSUInteger length = [data length];
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// This is a copy of Google Toolbox for Mac library to avoid creating an extra framework.
|
||||
|
||||
// NOTE: For 64bit, none of these apis handle input sizes >32bits, they will return nil when given
|
||||
@@ -24,11 +26,11 @@
|
||||
|
||||
/// Returns an data as the result of decompressing the payload of |data|.The data to decompress must
|
||||
/// be a gzipped payloads.
|
||||
+ (NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error;
|
||||
+ (nullable NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error;
|
||||
|
||||
/// Returns an compressed data with the result of gzipping the payload of |data|. Uses the default
|
||||
/// compression level.
|
||||
+ (NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error;
|
||||
+ (nullable NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorDomain;
|
||||
FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorKey; // NSNumber
|
||||
@@ -47,3 +49,5 @@ typedef NS_ENUM(NSInteger, GULNSDataZlibError) {
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -90,11 +90,11 @@ static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (NSString *)postURL:(NSURL *)url
|
||||
payload:(NSData *)payload
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
- (nullable NSString *)postURL:(NSURL *)url
|
||||
payload:(NSData *)payload
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
return [self postURL:url
|
||||
headers:nil
|
||||
payload:payload
|
||||
@@ -103,12 +103,12 @@ static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
completionHandler:handler];
|
||||
}
|
||||
|
||||
- (NSString *)postURL:(NSURL *)url
|
||||
headers:(NSDictionary *)headers
|
||||
payload:(NSData *)payload
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
- (nullable NSString *)postURL:(NSURL *)url
|
||||
headers:(nullable NSDictionary *)headers
|
||||
payload:(NSData *)payload
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
if (!url.absoluteString.length) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
|
||||
return nil;
|
||||
@@ -189,11 +189,11 @@ static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
return requestID;
|
||||
}
|
||||
|
||||
- (NSString *)getURL:(NSURL *)url
|
||||
headers:(NSDictionary *)headers
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
- (nullable NSString *)getURL:(NSURL *)url
|
||||
headers:(nullable NSDictionary *)headers
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler {
|
||||
if (!url.absoluteString.length) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
|
||||
return nil;
|
||||
@@ -279,10 +279,11 @@ static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
messageCode:message:context:)] ||
|
||||
![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
|
||||
messageCode:message:)]) {
|
||||
GULLogError(kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork002],
|
||||
@"Cannot set the network logger delegate: delegate does not conform to the network "
|
||||
"logger protocol.");
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork002],
|
||||
@"Cannot set the network logger delegate: delegate does not conform to the network "
|
||||
"logger protocol.");
|
||||
return;
|
||||
}
|
||||
_loggerDelegate = loggerDelegate;
|
||||
@@ -329,9 +330,9 @@ static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
logLevel == kGULNetworkLogLevelWarning || logLevel == kGULNetworkLogLevelInfo) {
|
||||
NSString *formattedMessage = GULStringWithLogMessage(message, logLevel, contexts);
|
||||
NSLog(@"%@", formattedMessage);
|
||||
GULLogBasic((GULLoggerLevel)logLevel, kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
|
||||
NULL);
|
||||
GULOSLogBasic((GULLoggerLevel)logLevel, kGULLogSubsystem, kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,11 @@
|
||||
/// Session ID generated randomly with a fixed prefix.
|
||||
NSString *_sessionID;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||
/// The session configuration. NSURLSessionConfiguration' is only available on iOS 7.0 or newer.
|
||||
/// The session configuration.
|
||||
NSURLSessionConfiguration *_sessionConfig;
|
||||
|
||||
/// The current NSURLSession.
|
||||
NSURLSession *__weak _Nullable _URLSession;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
/// The path to the directory where all temporary files are stored before uploading.
|
||||
NSURL *_networkDirectoryURL;
|
||||
@@ -96,20 +93,19 @@
|
||||
if (fetcher != nil) {
|
||||
[fetcher addSystemCompletionHandler:systemCompletionHandler forSession:sessionID];
|
||||
} else {
|
||||
GULLogError(kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork003],
|
||||
@"Failed to retrieve background session with ID %@ after app is relaunched.",
|
||||
sessionID);
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork003],
|
||||
@"Failed to retrieve background session with ID %@ after app is relaunched.",
|
||||
sessionID);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - External Methods
|
||||
|
||||
/// Sends an async POST request using NSURLSession for iOS >= 7.0, and returns an ID of the
|
||||
/// connection.
|
||||
/// Sends an async POST request using `NSURLSession`, and returns an ID of the connection.
|
||||
- (nullable NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler
|
||||
API_AVAILABLE(ios(7.0)) {
|
||||
completionHandler:
|
||||
(GULNetworkURLSessionCompletionHandler)handler {
|
||||
// NSURLSessionUploadTask does not work with NSData in the background.
|
||||
// To avoid this issue, write the data to a temporary file to upload it.
|
||||
// Make a temporary file with the data subset.
|
||||
@@ -145,25 +141,25 @@
|
||||
[self excludeFromBackupForURL:_uploadingFileURL];
|
||||
|
||||
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||
session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:[NSOperationQueue mainQueue]];
|
||||
postRequestTask = [session uploadTaskWithRequest:request fromFile:_uploadingFileURL];
|
||||
} else {
|
||||
// If we cannot write to file, just send it in the foreground.
|
||||
_sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||
session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:[NSOperationQueue mainQueue]];
|
||||
// To avoid a runtime warning in Xcode 15 Beta 4, the given `URLRequest`
|
||||
// should have a nil `HTTPBody`. To workaround this, the given `URLRequest`
|
||||
// is copied and the `HTTPBody` data is removed.
|
||||
NSData *givenRequestHTTPBody = [request.HTTPBody copy];
|
||||
NSMutableURLRequest *requestWithoutHTTPBody = [request mutableCopy];
|
||||
requestWithoutHTTPBody.HTTPBody = nil;
|
||||
}
|
||||
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||
session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:[NSOperationQueue mainQueue]];
|
||||
// To avoid a runtime warning in Xcode 15 Beta 4, the given `URLRequest`
|
||||
// should have a nil `HTTPBody`. To workaround this, the given `URLRequest`
|
||||
// is copied and the `HTTPBody` data is removed.
|
||||
NSData *givenRequestHTTPBody = [request.HTTPBody copy];
|
||||
NSMutableURLRequest *requestWithoutHTTPBody = [request mutableCopy];
|
||||
requestWithoutHTTPBody.HTTPBody = nil;
|
||||
|
||||
if (didWriteFile) {
|
||||
postRequestTask = [session uploadTaskWithRequest:requestWithoutHTTPBody
|
||||
fromFile:_uploadingFileURL];
|
||||
} else {
|
||||
postRequestTask = [session uploadTaskWithRequest:requestWithoutHTTPBody
|
||||
fromData:givenRequestHTTPBody];
|
||||
}
|
||||
@@ -192,10 +188,9 @@
|
||||
return _sessionID;
|
||||
}
|
||||
|
||||
/// Sends an async GET request using NSURLSession for iOS >= 7.0, and returns an ID of the session.
|
||||
/// Sends an async GET request using `NSURLSession`, and returns an ID of the session.
|
||||
- (nullable NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler
|
||||
API_AVAILABLE(ios(7.0)) {
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler {
|
||||
if (_backgroundNetworkEnabled) {
|
||||
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||
} else {
|
||||
@@ -260,7 +255,7 @@
|
||||
/// be called with the downloaded data.
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
downloadTask:(NSURLSessionDownloadTask *)task
|
||||
didFinishDownloadingToURL:(NSURL *)url API_AVAILABLE(ios(7.0)) {
|
||||
didFinishDownloadingToURL:(NSURL *)url {
|
||||
if (!url.path) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
@@ -283,8 +278,7 @@
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
|
||||
API_AVAILABLE(ios(7.0)) {
|
||||
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession003
|
||||
message:@"Background session finished"
|
||||
@@ -295,7 +289,7 @@
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error API_AVAILABLE(ios(7.0)) {
|
||||
didCompleteWithError:(NSError *)error {
|
||||
// Avoid any chance of recursive behavior leading to it being used repeatedly.
|
||||
GULNetworkURLSessionCompletionHandler handler = _completionHandler;
|
||||
_completionHandler = nil;
|
||||
@@ -340,8 +334,7 @@
|
||||
task:(NSURLSessionTask *)task
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *credential))completionHandler
|
||||
API_AVAILABLE(ios(7.0)) {
|
||||
NSURLCredential *credential))completionHandler {
|
||||
// The handling is modeled after GTMSessionFetcher.
|
||||
if ([challenge.protectionSpace.authenticationMethod
|
||||
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
@@ -394,28 +387,20 @@
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
dispatch_async(evaluateBackgroundQueue, ^{
|
||||
SecTrustResultType trustEval = kSecTrustResultInvalid;
|
||||
BOOL shouldAllow;
|
||||
OSStatus trustError;
|
||||
CFErrorRef errorRef = NULL;
|
||||
|
||||
@synchronized([GULNetworkURLSession class]) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
trustError = SecTrustEvaluate(serverTrust, &trustEval);
|
||||
#pragma clang diagnostic pop
|
||||
shouldAllow = SecTrustEvaluateWithError(serverTrust, &errorRef);
|
||||
}
|
||||
|
||||
if (trustError != errSecSuccess) {
|
||||
[self->_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession008
|
||||
message:@"Cannot evaluate server trust. Error, host"
|
||||
contexts:@[ @(trustError), self->_request.URL ]];
|
||||
shouldAllow = NO;
|
||||
} else {
|
||||
// Having a trust level "unspecified" by the user is the usual result, described at
|
||||
// https://developer.apple.com/library/mac/qa/qa1360
|
||||
shouldAllow =
|
||||
(trustEval == kSecTrustResultUnspecified || trustEval == kSecTrustResultProceed);
|
||||
if (errorRef) {
|
||||
[self->_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession008
|
||||
message:@"Cannot evaluate server trust. Error, host"
|
||||
contexts:@[ @((int)CFErrorGetCode(errorRef)), self->_request.URL ]];
|
||||
CFRelease(errorRef);
|
||||
}
|
||||
|
||||
// Call the call back with the permission.
|
||||
@@ -662,7 +647,7 @@
|
||||
task:(NSURLSessionTask *)task
|
||||
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLRequest *))completionHandler API_AVAILABLE(ios(7.0)) {
|
||||
completionHandler:(void (^)(NSURLRequest *))completionHandler {
|
||||
NSArray *nonAllowedRedirectionCodes = @[
|
||||
@(kGULNetworkHTTPStatusCodeFound), @(kGULNetworkHTTPStatusCodeMovedPermanently),
|
||||
@(kGULNetworkHTTPStatusCodeMovedTemporarily), @(kGULNetworkHTTPStatusCodeMultipleChoices)
|
||||
@@ -734,7 +719,7 @@
|
||||
|
||||
// Always use the request parameters even if the default session configuration is more restrictive.
|
||||
- (void)populateSessionConfig:(NSURLSessionConfiguration *)sessionConfig
|
||||
withRequest:(NSURLRequest *)request API_AVAILABLE(ios(7.0)) {
|
||||
withRequest:(NSURLRequest *)request {
|
||||
sessionConfig.HTTPAdditionalHeaders = request.allHTTPHeaderFields;
|
||||
sessionConfig.timeoutIntervalForRequest = request.timeoutInterval;
|
||||
sessionConfig.timeoutIntervalForResource = request.timeoutInterval;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// A mutable dictionary that provides atomic accessor and mutators.
|
||||
@interface GULMutableDictionary : NSObject
|
||||
|
||||
@@ -44,3 +46,5 @@
|
||||
- (NSDictionary *)dictionary;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#import "GULNetworkLoggerProtocol.h"
|
||||
#import "GULNetworkURLSession.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Delegate protocol for GULNetwork events.
|
||||
@protocol GULNetworkReachabilityDelegate
|
||||
|
||||
@@ -69,29 +71,31 @@
|
||||
/// Compresses and sends a POST request with the provided data to the URL. The session will be
|
||||
/// background session if usingBackgroundSession is YES. Otherwise, the POST session is default
|
||||
/// session. Returns a session ID or nil if an error occurs.
|
||||
- (NSString *)postURL:(NSURL *)url
|
||||
payload:(NSData *)payload
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
- (nullable NSString *)postURL:(NSURL *)url
|
||||
payload:(NSData *)payload
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
|
||||
/// Compresses and sends a POST request with the provided headers and data to the URL. The session
|
||||
/// will be background session if usingBackgroundSession is YES. Otherwise, the POST session is
|
||||
/// default session. Returns a session ID or nil if an error occurs.
|
||||
- (NSString *)postURL:(NSURL *)url
|
||||
headers:(NSDictionary *)headers
|
||||
payload:(NSData *)payload
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
- (nullable NSString *)postURL:(NSURL *)url
|
||||
headers:(nullable NSDictionary *)headers
|
||||
payload:(NSData *)payload
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
|
||||
/// Sends a GET request with the provided data to the URL. The session will be background session
|
||||
/// if usingBackgroundSession is YES. Otherwise, the GET session is default session. Returns a
|
||||
/// session ID or nil if an error occurs.
|
||||
- (NSString *)getURL:(NSURL *)url
|
||||
headers:(NSDictionary *)headers
|
||||
queue:(dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
- (nullable NSString *)getURL:(NSURL *)url
|
||||
headers:(nullable NSDictionary *)headers
|
||||
queue:(nullable dispatch_queue_t)queue
|
||||
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||
completionHandler:(GULNetworkCompletionHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Error codes in Firebase Network error domain.
|
||||
/// Note: these error codes should never change. It would make it harder to decode the errors if
|
||||
/// we inadvertently altered any of these codes in a future SDK version.
|
||||
@@ -69,3 +71,5 @@ extern const int kGULNetworkHTTPStatusCodeMovedTemporarily;
|
||||
extern const int kGULNetworkHTTPStatusCodeNotFound;
|
||||
extern const int kGULNetworkHTTPStatusCodeCannotAcceptTraffic;
|
||||
extern const int kGULNetworkHTTPStatusCodeUnavailable;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#import "GULNetworkMessageCode.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The log levels used by GULNetworkLogger.
|
||||
typedef NS_ENUM(NSInteger, GULNetworkLogLevel) {
|
||||
kGULNetworkLogLevelError = 3,
|
||||
@@ -47,3 +49,5 @@ typedef NS_ENUM(NSInteger, GULNetworkLogLevel) {
|
||||
message:(NSString *)message;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
|
||||
typedef NS_ENUM(NSInteger, GULNetworkMessageCode) {
|
||||
// GULNetwork.m
|
||||
@@ -45,3 +47,5 @@ typedef NS_ENUM(NSInteger, GULNetworkMessageCode) {
|
||||
kGULNetworkMessageCodeURLSession018 = 901018, // I-NET901018
|
||||
kGULNetworkMessageCodeURLSession019 = 901019, // I-NET901019
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>1C8F.1</string>
|
||||
<string>C56D.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
@@ -64,10 +64,10 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi {
|
||||
#if !TARGET_OS_WATCH
|
||||
if (reachability_) {
|
||||
GULLogError(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode000],
|
||||
@"Cannot change reachability API while reachability is running. "
|
||||
@"Call stop first.");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode000],
|
||||
@"Cannot change reachability API while reachability is running. "
|
||||
@"Call stop first.");
|
||||
return;
|
||||
}
|
||||
reachabilityApi_ = reachabilityApi;
|
||||
@@ -89,9 +89,9 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
- (void)setReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate {
|
||||
if (reachabilityDelegate &&
|
||||
(![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(GULReachabilityDelegate)])) {
|
||||
GULLogError(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULReachabilityMessageCode005],
|
||||
@"Reachability delegate doesn't conform to Reachability protocol.");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULReachabilityMessageCode005],
|
||||
@"Reachability delegate doesn't conform to Reachability protocol.");
|
||||
return;
|
||||
}
|
||||
reachabilityDelegate_ = reachabilityDelegate;
|
||||
@@ -102,9 +102,9 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
self = [super init];
|
||||
|
||||
if (!host || !host.length) {
|
||||
GULLogError(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode001],
|
||||
@"Invalid host specified");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode001],
|
||||
@"Invalid host specified");
|
||||
return nil;
|
||||
}
|
||||
if (self) {
|
||||
@@ -147,15 +147,15 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
reachabilityApi_->releaseFn(reachability_);
|
||||
reachability_ = nil;
|
||||
|
||||
GULLogError(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode002],
|
||||
@"Failed to start reachability handle");
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode002],
|
||||
@"Failed to start reachability handle");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
GULLogDebug(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode003],
|
||||
@"Monitoring the network status");
|
||||
GULOSLogDebug(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode003],
|
||||
@"Monitoring the network status");
|
||||
return YES;
|
||||
#endif
|
||||
}
|
||||
@@ -180,7 +180,7 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
// Reachable flag is set. Check further flags.
|
||||
if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
|
||||
// Connection required flag is not set, so we have connectivity.
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
|
||||
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
|
||||
: kGULReachabilityViaWifi;
|
||||
#elif TARGET_OS_OSX
|
||||
@@ -191,7 +191,7 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
!(flags & kSCNetworkReachabilityFlagsInterventionRequired)) {
|
||||
// If the connection on demand or connection on traffic flag is set, and user intervention
|
||||
// is not required, we have connectivity.
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || (defined(TARGET_OS_VISION) && TARGET_OS_VISION)
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
|
||||
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
|
||||
: kGULReachabilityViaWifi;
|
||||
#elif TARGET_OS_OSX
|
||||
@@ -214,10 +214,10 @@ static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
: kGULReachabilityConnectedStatus;
|
||||
}
|
||||
|
||||
GULLogDebug(kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode004],
|
||||
@"Network status has changed. Code:%@, status:%@", @(status),
|
||||
reachabilityStatusString);
|
||||
GULOSLogDebug(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode004],
|
||||
@"Network status has changed. Code:%@, status:%@", @(status),
|
||||
reachabilityStatusString);
|
||||
reachabilityStatus_ = status;
|
||||
[reachabilityDelegate_ reachability:self statusChanged:reachabilityStatus_];
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Reachability Status
|
||||
typedef enum {
|
||||
kGULReachabilityUnknown, ///< Have not yet checked or been notified whether host is reachable.
|
||||
@@ -77,3 +79,5 @@ const NSString *GULReachabilityStatusString(GULReachabilityStatus status);
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSTimeInterval const kGULSynchronizeInterval = 1.0;
|
||||
|
||||
static NSString *const kGULLogFormat = @"I-GUL%06ld";
|
||||
|
||||
static GULLoggerService kGULLogUserDefaultsService = @"[GoogleUtilities/UserDefaults]";
|
||||
@@ -33,18 +31,11 @@ typedef NS_ENUM(NSInteger, GULUDMessageCode) {
|
||||
|
||||
@interface GULUserDefaults ()
|
||||
|
||||
/// Equivalent to the suite name for NSUserDefaults.
|
||||
@property(readonly) CFStringRef appNameRef;
|
||||
|
||||
@property(atomic) BOOL isPreferenceFileExcluded;
|
||||
@property(nonatomic, readonly) NSUserDefaults *userDefaults;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GULUserDefaults {
|
||||
// The application name is the same with the suite name of the NSUserDefaults, and it is used for
|
||||
// preferences.
|
||||
CFStringRef _appNameRef;
|
||||
}
|
||||
@implementation GULUserDefaults
|
||||
|
||||
+ (GULUserDefaults *)standardUserDefaults {
|
||||
static GULUserDefaults *standardUserDefaults;
|
||||
@@ -65,49 +56,35 @@ typedef NS_ENUM(NSInteger, GULUDMessageCode) {
|
||||
NSString *name = [suiteName copy];
|
||||
|
||||
if (self) {
|
||||
// `kCFPreferencesCurrentApplication` maps to the same defaults database as
|
||||
// `[NSUserDefaults standardUserDefaults]`.
|
||||
_appNameRef =
|
||||
name.length ? (__bridge_retained CFStringRef)name : kCFPreferencesCurrentApplication;
|
||||
_userDefaults = name.length ? [[NSUserDefaults alloc] initWithSuiteName:name]
|
||||
: [NSUserDefaults standardUserDefaults];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// If we're using a custom `_appNameRef` it needs to be released. If it's a constant, it shouldn't
|
||||
// need to be released since we don't own it.
|
||||
if (CFStringCompare(_appNameRef, kCFPreferencesCurrentApplication, 0) != kCFCompareEqualTo) {
|
||||
CFRelease(_appNameRef);
|
||||
}
|
||||
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
||||
selector:@selector(synchronize)
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (nullable id)objectForKey:(NSString *)defaultName {
|
||||
NSString *key = [defaultName copy];
|
||||
if (![key isKindOfClass:[NSString class]] || !key.length) {
|
||||
GULLogWarning(@"<GoogleUtilities>", NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeyGet],
|
||||
@"Cannot get object for invalid user default key.");
|
||||
GULOSLogWarning(kGULLogSubsystem, @"<GoogleUtilities>", NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeyGet],
|
||||
@"Cannot get object for invalid user default key.");
|
||||
return nil;
|
||||
}
|
||||
return (__bridge_transfer id)CFPreferencesCopyAppValue((__bridge CFStringRef)key, _appNameRef);
|
||||
|
||||
return [self.userDefaults objectForKey:key];
|
||||
}
|
||||
|
||||
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName {
|
||||
NSString *key = [defaultName copy];
|
||||
if (![key isKindOfClass:[NSString class]] || !key.length) {
|
||||
GULLogWarning(kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeySet],
|
||||
@"Cannot set object for invalid user default key.");
|
||||
GULOSLogWarning(kGULLogSubsystem, kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeySet],
|
||||
@"Cannot set object for invalid user default key.");
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
CFPreferencesSetAppValue((__bridge CFStringRef)key, NULL, _appNameRef);
|
||||
[self scheduleSynchronize];
|
||||
[self.userDefaults removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
BOOL isAcceptableValue =
|
||||
@@ -115,16 +92,16 @@ typedef NS_ENUM(NSInteger, GULUDMessageCode) {
|
||||
[value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]] ||
|
||||
[value isKindOfClass:[NSDate class]] || [value isKindOfClass:[NSData class]];
|
||||
if (!isAcceptableValue) {
|
||||
GULLogWarning(kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidObjectSet],
|
||||
@"Cannot set invalid object to user defaults. Must be a string, number, array, "
|
||||
@"dictionary, date, or data. Value: %@",
|
||||
value);
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidObjectSet],
|
||||
@"Cannot set invalid object to user defaults. Must be a string, number, array, "
|
||||
@"dictionary, date, or data. Value: %@",
|
||||
value);
|
||||
return;
|
||||
}
|
||||
|
||||
CFPreferencesSetAppValue((__bridge CFStringRef)key, (__bridge CFStringRef)value, _appNameRef);
|
||||
[self scheduleSynchronize];
|
||||
[self.userDefaults setObject:value forKey:key];
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key {
|
||||
@@ -183,31 +160,6 @@ typedef NS_ENUM(NSInteger, GULUDMessageCode) {
|
||||
[self setObject:@(boolValue) forKey:defaultName];
|
||||
}
|
||||
|
||||
#pragma mark - Save data
|
||||
|
||||
- (void)synchronize {
|
||||
if (!CFPreferencesAppSynchronize(_appNameRef)) {
|
||||
GULLogError(kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeSynchronizeFailed],
|
||||
@"Cannot synchronize user defaults to disk");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
- (void)scheduleSynchronize {
|
||||
// Synchronize data using a timer so that multiple set... calls can be coalesced under one
|
||||
// synchronize.
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
||||
selector:@selector(synchronize)
|
||||
object:nil];
|
||||
// This method may be called on multiple queues (due to set... methods can be called on any queue)
|
||||
// synchronize can be scheduled on different queues, so make sure that it does not crash. If this
|
||||
// instance goes away, self will be released also, no one will retain it and the schedule won't be
|
||||
// called.
|
||||
[self performSelector:@selector(synchronize) withObject:nil afterDelay:kGULSynchronizeInterval];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -100,11 +100,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// Equivalent to -[... setObject:nil forKey:defaultName]
|
||||
- (void)removeObjectForKey:(NSString *)defaultName;
|
||||
|
||||
#pragma mark - Save data
|
||||
|
||||
/// Blocks the calling thread until all in-progress set operations have completed.
|
||||
- (void)synchronize;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
19
Pods/GoogleUtilities/README.md
generated
19
Pods/GoogleUtilities/README.md
generated
@@ -38,7 +38,7 @@ After the CI is green:
|
||||
<summary>Push to <b>SpecsStaging</b></summary>
|
||||
|
||||
```console
|
||||
pod repo push --skip-tests staging GoogleUtilities.podspec
|
||||
pod repo push --skip-tests --use-json staging GoogleUtilities.podspec
|
||||
```
|
||||
|
||||
If the command fails with `Unable to find the 'staging' repo.`, add the staging repo with:
|
||||
@@ -51,7 +51,7 @@ After the CI is green:
|
||||
<summary>Push to <b>SpecsDev</b></summary>
|
||||
|
||||
```console
|
||||
pod repo push --skip-tests dev GoogleUtilities.podspec
|
||||
pod repo push --skip-tests --use-json dev GoogleUtilities.podspec
|
||||
```
|
||||
|
||||
If the command fails with `Unable to find the 'dev' repo.`, add the dev repo with:
|
||||
@@ -89,7 +89,7 @@ The release process is as follows:
|
||||
|
||||
It's recommended to point to the `GoogleUtilities.podspec` in `staging` to make sure the correct spec is being published.
|
||||
```console
|
||||
pod trunk push ~/.cocoapods/repos/staging/GoogleUtilities/{version}/GoogleUtilities.podspec
|
||||
pod trunk push ~/.cocoapods/repos/staging/GoogleUtilities/{version}/GoogleUtilities.podspec.json
|
||||
```
|
||||
*Note: In some cases, it may be acceptable to `pod trunk push` with the `--skip-tests` flag. Please double check with
|
||||
the maintainers before doing so.*
|
||||
@@ -160,17 +160,7 @@ Alternatively disable signing in each target:
|
||||
|
||||
### Code Formatting
|
||||
|
||||
To ensure that the code is formatted consistently, run the script
|
||||
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/main/scripts/check.sh)
|
||||
before creating a PR.
|
||||
|
||||
GitHub Actions will verify that any code changes are done in a style compliant
|
||||
way. Install `clang-format` and `mint`:
|
||||
|
||||
```console
|
||||
brew install clang-format@17
|
||||
brew install mint
|
||||
```
|
||||
See [firebase-ios-sdk's code formatting docs][firebase-ios-sdk-formatting].
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
@@ -187,3 +177,4 @@ The contents of this repository is licensed under the
|
||||
|
||||
[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions
|
||||
[gh-google-utilities-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities/badge.svg
|
||||
[firebase-ios-sdk-formatting]: https://github.com/firebase/firebase-ios-sdk?tab=readme-ov-file#code-formatting
|
||||
|
||||
Reference in New Issue
Block a user