firebase log level
This commit is contained in:
1023
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m
generated
Normal file
1023
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULAppDelegateSwizzler.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
444
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULSceneDelegateSwizzler.m
generated
Normal file
444
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/GULSceneDelegateSwizzler.m
generated
Normal file
@ -0,0 +1,444 @@
|
||||
// Copyright 2019 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 <TargetConditionals.h>
|
||||
|
||||
#import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULSceneDelegateSwizzler.h"
|
||||
|
||||
#import "GoogleUtilities/AppDelegateSwizzler/Internal/GULSceneDelegateSwizzler_Private.h"
|
||||
#import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h"
|
||||
#import "GoogleUtilities/Common/GULLoggerCodes.h"
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h"
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#if UISCENE_SUPPORTED
|
||||
API_AVAILABLE(ios(13.0), tvos(13.0))
|
||||
typedef void (*GULOpenURLContextsIMP)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *);
|
||||
|
||||
API_AVAILABLE(ios(13.0), tvos(13.0))
|
||||
typedef void (^GULSceneDelegateInterceptorCallback)(id<UISceneDelegate>);
|
||||
|
||||
// The strings below are the keys for associated objects.
|
||||
static char const *const kGULRealIMPBySelectorKey = "GUL_realIMPBySelector";
|
||||
static char const *const kGULRealClassKey = "GUL_realClass";
|
||||
#endif // UISCENE_SUPPORTED
|
||||
|
||||
static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilities/SceneDelegateSwizzler]";
|
||||
|
||||
// Since Firebase SDKs also use this for app delegate proxying, in order to not be a breaking change
|
||||
// we disable App Delegate proxying when either of these two flags are set to NO.
|
||||
|
||||
/** Plist key that allows Firebase developers to disable App and Scene Delegate Proxying. */
|
||||
static NSString *const kGULFirebaseSceneDelegateProxyEnabledPlistKey =
|
||||
@"FirebaseAppDelegateProxyEnabled";
|
||||
|
||||
/** Plist key that allows developers not using Firebase to disable App and Scene Delegate Proxying.
|
||||
*/
|
||||
static NSString *const kGULGoogleUtilitiesSceneDelegateProxyEnabledPlistKey =
|
||||
@"GoogleUtilitiesAppDelegateProxyEnabled";
|
||||
|
||||
/** The prefix of the Scene Delegate. */
|
||||
static NSString *const kGULSceneDelegatePrefix = @"GUL_";
|
||||
|
||||
/**
|
||||
* This class is necessary to store the delegates in an NSArray without retaining them.
|
||||
* [NSValue valueWithNonRetainedObject] also provides this functionality, but does not provide a
|
||||
* zeroing pointer. This will cause EXC_BAD_ACCESS when trying to access the object after it is
|
||||
* dealloced. Instead, this container stores a weak, zeroing reference to the object, which
|
||||
* automatically is set to nil by the runtime when the object is dealloced.
|
||||
*/
|
||||
@interface GULSceneZeroingWeakContainer : NSObject
|
||||
|
||||
/** Stores a weak object. */
|
||||
@property(nonatomic, weak) id object;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GULSceneZeroingWeakContainer
|
||||
@end
|
||||
|
||||
@implementation GULSceneDelegateSwizzler
|
||||
|
||||
#pragma mark - Public methods
|
||||
|
||||
+ (BOOL)isSceneDelegateProxyEnabled {
|
||||
return [GULAppDelegateSwizzler isAppDelegateProxyEnabled];
|
||||
}
|
||||
|
||||
+ (void)proxyOriginalSceneDelegate {
|
||||
#if UISCENE_SUPPORTED
|
||||
if ([GULAppEnvironmentUtil isAppExtension]) {
|
||||
return;
|
||||
}
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
if (![GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]) {
|
||||
return;
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(handleSceneWillConnectToNotification:)
|
||||
name:UISceneWillConnectNotification
|
||||
object:nil];
|
||||
}
|
||||
});
|
||||
#endif // UISCENE_SUPPORTED
|
||||
}
|
||||
|
||||
#if UISCENE_SUPPORTED
|
||||
+ (GULSceneDelegateInterceptorID)registerSceneDelegateInterceptor:(id<UISceneDelegate>)interceptor {
|
||||
NSAssert(interceptor, @"SceneDelegateProxy cannot add nil interceptor");
|
||||
NSAssert([interceptor conformsToProtocol:@protocol(UISceneDelegate)],
|
||||
@"SceneDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
|
||||
if (!interceptor) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling000],
|
||||
@"SceneDelegateProxy cannot add nil interceptor.");
|
||||
return nil;
|
||||
}
|
||||
if (![interceptor conformsToProtocol:@protocol(UISceneDelegate)]) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling001],
|
||||
@"SceneDelegateProxy interceptor does not conform to UIApplicationDelegate");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// The ID should be the same given the same interceptor object.
|
||||
NSString *interceptorID =
|
||||
[NSString stringWithFormat:@"%@%p", kGULSceneDelegatePrefix, interceptor];
|
||||
if (!interceptorID.length) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling002],
|
||||
@"SceneDelegateProxy cannot create Interceptor ID.");
|
||||
return nil;
|
||||
}
|
||||
GULSceneZeroingWeakContainer *weakObject = [[GULSceneZeroingWeakContainer alloc] init];
|
||||
weakObject.object = interceptor;
|
||||
[GULSceneDelegateSwizzler interceptors][interceptorID] = weakObject;
|
||||
return interceptorID;
|
||||
}
|
||||
|
||||
+ (void)unregisterSceneDelegateInterceptorWithID:(GULSceneDelegateInterceptorID)interceptorID {
|
||||
NSAssert(interceptorID, @"SceneDelegateProxy cannot unregister nil interceptor ID.");
|
||||
NSAssert(((NSString *)interceptorID).length != 0,
|
||||
@"SceneDelegateProxy cannot unregister empty interceptor ID.");
|
||||
|
||||
if (!interceptorID) {
|
||||
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) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling004],
|
||||
@"SceneDelegateProxy cannot unregister interceptor that was not registered. "
|
||||
"Interceptor ID %@",
|
||||
interceptorID);
|
||||
return;
|
||||
}
|
||||
|
||||
[[GULSceneDelegateSwizzler interceptors] removeObjectForKey:interceptorID];
|
||||
}
|
||||
|
||||
#pragma mark - Helper methods
|
||||
|
||||
+ (GULMutableDictionary *)interceptors {
|
||||
static dispatch_once_t onceToken;
|
||||
static GULMutableDictionary *sInterceptors;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sInterceptors = [[GULMutableDictionary alloc] init];
|
||||
});
|
||||
return sInterceptors;
|
||||
}
|
||||
|
||||
+ (void)clearInterceptors {
|
||||
[[self interceptors] removeAllObjects];
|
||||
}
|
||||
|
||||
+ (nullable NSValue *)originalImplementationForSelector:(SEL)selector object:(id)object {
|
||||
NSDictionary *realImplementationBySelector =
|
||||
objc_getAssociatedObject(object, &kGULRealIMPBySelectorKey);
|
||||
return realImplementationBySelector[NSStringFromSelector(selector)];
|
||||
}
|
||||
|
||||
+ (void)proxyDestinationSelector:(SEL)destinationSelector
|
||||
implementationsFromSourceSelector:(SEL)sourceSelector
|
||||
fromClass:(Class)sourceClass
|
||||
toClass:(Class)destinationClass
|
||||
realClass:(Class)realClass
|
||||
storeDestinationImplementationTo:
|
||||
(NSMutableDictionary<NSString *, NSValue *> *)destinationImplementationsBySelector {
|
||||
[self addInstanceMethodWithDestinationSelector:destinationSelector
|
||||
withImplementationFromSourceSelector:sourceSelector
|
||||
fromClass:sourceClass
|
||||
toClass:destinationClass];
|
||||
IMP sourceImplementation =
|
||||
[GULSceneDelegateSwizzler implementationOfMethodSelector:destinationSelector
|
||||
fromClass:realClass];
|
||||
NSValue *sourceImplementationPointer = [NSValue valueWithPointer:sourceImplementation];
|
||||
|
||||
NSString *destinationSelectorString = NSStringFromSelector(destinationSelector);
|
||||
destinationImplementationsBySelector[destinationSelectorString] = sourceImplementationPointer;
|
||||
}
|
||||
|
||||
/** Copies a method identified by the methodSelector from one class to the other. After this method
|
||||
* is called, performing [toClassInstance methodSelector] will be similar to calling
|
||||
* [fromClassInstance methodSelector]. This method does nothing if toClass already has a method
|
||||
* identified by methodSelector.
|
||||
*
|
||||
* @param methodSelector The SEL that identifies both the method on the fromClass as well as the
|
||||
* one on the toClass.
|
||||
* @param fromClass The class from which a method is sourced.
|
||||
* @param toClass The class to which the method is added. If the class already has a method with
|
||||
* the same selector, this has no effect.
|
||||
*/
|
||||
+ (void)addInstanceMethodWithSelector:(SEL)methodSelector
|
||||
fromClass:(Class)fromClass
|
||||
toClass:(Class)toClass {
|
||||
[self addInstanceMethodWithDestinationSelector:methodSelector
|
||||
withImplementationFromSourceSelector:methodSelector
|
||||
fromClass:fromClass
|
||||
toClass:toClass];
|
||||
}
|
||||
|
||||
/** Copies a method identified by the sourceSelector from the fromClass as a method for the
|
||||
* destinationSelector on the toClass. After this method is called, performing
|
||||
* [toClassInstance destinationSelector] will be similar to calling
|
||||
* [fromClassInstance sourceSelector]. This method does nothing if toClass already has a method
|
||||
* identified by destinationSelector.
|
||||
*
|
||||
* @param destinationSelector The SEL that identifies the method on the toClass.
|
||||
* @param sourceSelector The SEL that identifies the method on the fromClass.
|
||||
* @param fromClass The class from which a method is sourced.
|
||||
* @param toClass The class to which the method is added. If the class already has a method with
|
||||
* the same selector, this has no effect.
|
||||
*/
|
||||
+ (void)addInstanceMethodWithDestinationSelector:(SEL)destinationSelector
|
||||
withImplementationFromSourceSelector:(SEL)sourceSelector
|
||||
fromClass:(Class)fromClass
|
||||
toClass:(Class)toClass {
|
||||
Method method = class_getInstanceMethod(fromClass, sourceSelector);
|
||||
IMP methodIMP = method_getImplementation(method);
|
||||
const char *types = method_getTypeEncoding(method);
|
||||
if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) {
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeSceneDelegateSwizzling009],
|
||||
@"Cannot copy method to destination selector %@ as it already exists",
|
||||
NSStringFromSelector(destinationSelector));
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the IMP of the instance method on the class identified by the selector.
|
||||
*
|
||||
* @param selector The selector of which the IMP is to be fetched.
|
||||
* @param aClass The class from which the IMP is to be fetched.
|
||||
* @return The IMP of the instance method identified by selector and aClass.
|
||||
*/
|
||||
+ (IMP)implementationOfMethodSelector:(SEL)selector fromClass:(Class)aClass {
|
||||
Method aMethod = class_getInstanceMethod(aClass, selector);
|
||||
return method_getImplementation(aMethod);
|
||||
}
|
||||
|
||||
/** Enumerates through all the interceptors and if they respond to a given selector, executes a
|
||||
* GULSceneDelegateInterceptorCallback with the interceptor.
|
||||
*
|
||||
* @param methodSelector The SEL to check if an interceptor responds to.
|
||||
* @param callback the GULSceneDelegateInterceptorCallback.
|
||||
*/
|
||||
+ (void)notifyInterceptorsWithMethodSelector:(SEL)methodSelector
|
||||
callback:(GULSceneDelegateInterceptorCallback)callback
|
||||
API_AVAILABLE(ios(13.0)) {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *interceptors = [GULSceneDelegateSwizzler interceptors].dictionary;
|
||||
[interceptors enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
GULSceneZeroingWeakContainer *interceptorContainer = obj;
|
||||
id interceptor = interceptorContainer.object;
|
||||
if (!interceptor) {
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)kGULSwizzlerMessageCodeSceneDelegateSwizzling010],
|
||||
@"SceneDelegateProxy cannot find interceptor with ID %@. Removing the interceptor.", key);
|
||||
[[GULSceneDelegateSwizzler interceptors] removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
if ([interceptor respondsToSelector:methodSelector]) {
|
||||
callback(interceptor);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)handleSceneWillConnectToNotification:(NSNotification *)notification {
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
if ([notification.object isKindOfClass:[UIScene class]]) {
|
||||
UIScene *scene = (UIScene *)notification.object;
|
||||
[GULSceneDelegateSwizzler proxySceneDelegateIfNeeded:scene];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - [Donor Methods] UISceneDelegate URL handler
|
||||
|
||||
- (void)scene:(UIScene *)scene
|
||||
openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts API_AVAILABLE(ios(13.0), tvos(13.0)) {
|
||||
if (@available(iOS 13.0, tvOS 13.0, *)) {
|
||||
SEL methodSelector = @selector(scene:openURLContexts:);
|
||||
// Call the real implementation if the real Scene Delegate has any.
|
||||
NSValue *openURLContextsIMPPointer =
|
||||
[GULSceneDelegateSwizzler originalImplementationForSelector:methodSelector object:self];
|
||||
GULOpenURLContextsIMP openURLContextsIMP = [openURLContextsIMPPointer pointerValue];
|
||||
|
||||
[GULSceneDelegateSwizzler
|
||||
notifyInterceptorsWithMethodSelector:methodSelector
|
||||
callback:^(id<UISceneDelegate> interceptor) {
|
||||
if ([interceptor
|
||||
conformsToProtocol:@protocol(UISceneDelegate)]) {
|
||||
id<UISceneDelegate> sceneInterceptor =
|
||||
(id<UISceneDelegate>)interceptor;
|
||||
[sceneInterceptor scene:scene openURLContexts:URLContexts];
|
||||
}
|
||||
}];
|
||||
|
||||
if (openURLContextsIMP) {
|
||||
openURLContextsIMP(self, methodSelector, scene, URLContexts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)proxySceneDelegateIfNeeded:(UIScene *)scene {
|
||||
Class realClass = [scene.delegate class];
|
||||
NSString *className = NSStringFromClass(realClass);
|
||||
|
||||
// Skip proxying if failed to get the delegate class name for some reason (e.g. `delegate == nil`)
|
||||
// or the class has a prefix of kGULAppDelegatePrefix, which means it has been proxied before.
|
||||
if (className == nil || [className hasPrefix:kGULSceneDelegatePrefix]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *classNameWithPrefix = [kGULSceneDelegatePrefix stringByAppendingString:className];
|
||||
NSString *newClassName =
|
||||
[NSString stringWithFormat:@"%@-%@", classNameWithPrefix, [NSUUID UUID].UUIDString];
|
||||
|
||||
if (NSClassFromString(newClassName)) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzlingInvalidSceneDelegate],
|
||||
@"Cannot create a proxy for Scene Delegate. Subclass already exists. Original Class"
|
||||
@": %@, subclass: %@",
|
||||
className, newClassName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the new class as subclass of the real one. Do not allocate more than the real class
|
||||
// size.
|
||||
Class sceneDelegateSubClass = objc_allocateClassPair(realClass, newClassName.UTF8String, 0);
|
||||
if (sceneDelegateSubClass == Nil) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzlingInvalidSceneDelegate],
|
||||
@"Cannot create a proxy for Scene Delegate. Subclass already exists. Original Class"
|
||||
@": %@, subclass: Nil",
|
||||
className);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableDictionary<NSString *, NSValue *> *realImplementationsBySelector =
|
||||
[[NSMutableDictionary alloc] init];
|
||||
|
||||
// For scene:openURLContexts:
|
||||
SEL openURLContextsSEL = @selector(scene:openURLContexts:);
|
||||
[self proxyDestinationSelector:openURLContextsSEL
|
||||
implementationsFromSourceSelector:openURLContextsSEL
|
||||
fromClass:[GULSceneDelegateSwizzler class]
|
||||
toClass:sceneDelegateSubClass
|
||||
realClass:realClass
|
||||
storeDestinationImplementationTo:realImplementationsBySelector];
|
||||
|
||||
// Store original implementations to a fake property of the original delegate.
|
||||
objc_setAssociatedObject(scene.delegate, &kGULRealIMPBySelectorKey,
|
||||
[realImplementationsBySelector copy], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
objc_setAssociatedObject(scene.delegate, &kGULRealClassKey, realClass,
|
||||
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
|
||||
// The subclass size has to be exactly the same size with the original class size. The subclass
|
||||
// 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)) {
|
||||
GULOSLogError(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzlingInvalidSceneDelegate],
|
||||
@"Cannot create subclass of Scene Delegate, because the created subclass is not the "
|
||||
@"same size. %@",
|
||||
className);
|
||||
NSAssert(NO, @"Classes must be the same size to swizzle isa");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the newly created class to be the subclass of the real Scene Delegate class.
|
||||
objc_registerClassPair(sceneDelegateSubClass);
|
||||
if (object_setClass(scene.delegate, sceneDelegateSubClass)) {
|
||||
GULOSLogDebug(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld",
|
||||
(long)
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzlingInvalidSceneDelegate],
|
||||
@"Successfully created Scene Delegate Proxy automatically. To disable the "
|
||||
@"proxy, set the flag %@ to NO (Boolean) in the Info.plist",
|
||||
[GULSceneDelegateSwizzler correctSceneDelegateProxyKey]);
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)correctSceneDelegateProxyKey {
|
||||
return NSClassFromString(@"FIRCore") ? kGULFirebaseSceneDelegateProxyEnabledPlistKey
|
||||
: kGULGoogleUtilitiesSceneDelegateProxyEnabledPlistKey;
|
||||
}
|
||||
|
||||
#endif // UISCENE_SUPPORTED
|
||||
|
||||
@end
|
||||
55
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h
generated
Normal file
55
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULAppDelegateSwizzler_Private.h
generated
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2018 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 "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
|
||||
@class GULApplication;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GULAppDelegateSwizzler ()
|
||||
|
||||
/** ISA Swizzles the given appDelegate as the original app delegate would be.
|
||||
*
|
||||
* @param appDelegate The object that needs to be isa swizzled. This should conform to the
|
||||
* application delegate protocol.
|
||||
*/
|
||||
+ (void)proxyAppDelegate:(id<GULApplicationDelegate>)appDelegate;
|
||||
|
||||
/** Returns a dictionary containing interceptor IDs mapped to a GULZeroingWeakContainer.
|
||||
*
|
||||
* @return A dictionary of the form {NSString : GULZeroingWeakContainer}, where the NSString is
|
||||
* the interceptorID.
|
||||
*/
|
||||
+ (GULMutableDictionary *)interceptors;
|
||||
|
||||
/** Deletes all the registered interceptors. */
|
||||
+ (void)clearInterceptors;
|
||||
|
||||
/** Resets the token that prevents the app delegate proxy from being isa swizzled multiple times. */
|
||||
+ (void)resetProxyOriginalDelegateOnceToken;
|
||||
|
||||
/** Returns the original app delegate that was proxied.
|
||||
*
|
||||
* @return The original app delegate instance that was proxied.
|
||||
*/
|
||||
+ (id<GULApplicationDelegate>)originalDelegate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
48
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULSceneDelegateSwizzler_Private.h
generated
Normal file
48
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Internal/GULSceneDelegateSwizzler_Private.h
generated
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019 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 "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULSceneDelegateSwizzler.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GULSceneDelegateSwizzler ()
|
||||
|
||||
#if UISCENE_SUPPORTED
|
||||
|
||||
/** Returns a dictionary containing interceptor IDs mapped to a GULZeroingWeakContainer.
|
||||
*
|
||||
* @return A dictionary of the form {NSString : GULZeroingWeakContainer}, where the NSString is
|
||||
* the interceptorID.
|
||||
*/
|
||||
+ (GULMutableDictionary *)interceptors;
|
||||
|
||||
/** Deletes all the registered interceptors. */
|
||||
+ (void)clearInterceptors;
|
||||
|
||||
/** ISA Swizzles the given appDelegate as the original app delegate would be.
|
||||
*
|
||||
* @param scene The scene whose delegate needs to be isa swizzled. This should conform to the
|
||||
* scene delegate protocol.
|
||||
*/
|
||||
+ (void)proxySceneDelegateIfNeeded:(UIScene *)scene API_AVAILABLE(ios(13.0), tvos(13.0));
|
||||
|
||||
#endif // UISCENE_SUPPORTED
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
107
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h
generated
Normal file
107
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h
generated
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2018 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 "GULApplication.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NSString *const GULAppDelegateInterceptorID;
|
||||
|
||||
/** This class contains methods that isa swizzle the app delegate. */
|
||||
@interface GULAppDelegateSwizzler : NSProxy
|
||||
|
||||
/** Registers an app delegate interceptor whose methods will be invoked as they're invoked on the
|
||||
* original app delegate.
|
||||
*
|
||||
* @param interceptor An instance of a class that conforms to the application delegate protocol.
|
||||
* The interceptor is NOT retained.
|
||||
* @return A unique GULAppDelegateInterceptorID if interceptor was successfully registered; nil
|
||||
* if it fails.
|
||||
*/
|
||||
+ (nullable GULAppDelegateInterceptorID)registerAppDelegateInterceptor:
|
||||
(id<GULApplicationDelegate>)interceptor;
|
||||
|
||||
/** Unregisters an interceptor with the given ID if it exists.
|
||||
*
|
||||
* @param interceptorID The object that was generated when the interceptor was registered.
|
||||
*/
|
||||
+ (void)unregisterAppDelegateInterceptorWithID:(GULAppDelegateInterceptorID)interceptorID;
|
||||
|
||||
/** This method ensures that the original app delegate has been proxied. Call this before
|
||||
* registering your interceptor. This method is safe to call multiple times (but it only proxies
|
||||
* the app delegate once).
|
||||
*
|
||||
* This method doesn't proxy APNS related methods:
|
||||
* @code
|
||||
* - application:didRegisterForRemoteNotificationsWithDeviceToken:
|
||||
* - application:didFailToRegisterForRemoteNotificationsWithError:
|
||||
* - application:didReceiveRemoteNotification:fetchCompletionHandler:
|
||||
* - application:didReceiveRemoteNotification:
|
||||
* @endcode
|
||||
*
|
||||
* To proxy these methods use +[GULAppDelegateSwizzler
|
||||
* proxyOriginalDelegateIncludingAPNSMethods]. The methods have to be proxied separately to
|
||||
* avoid potential warnings from Apple review about missing Push Notification Entitlement (e.g.
|
||||
* https://github.com/firebase/firebase-ios-sdk/issues/2807)
|
||||
*
|
||||
* The method has no effect for extensions.
|
||||
*
|
||||
* @see proxyOriginalDelegateIncludingAPNSMethods
|
||||
*/
|
||||
+ (void)proxyOriginalDelegate;
|
||||
|
||||
/** This method ensures that the original app delegate has been proxied including APNS related
|
||||
* methods. Call this before registering your interceptor. This method is safe to call multiple
|
||||
* times (but it only proxies the app delegate once) or
|
||||
* after +[GULAppDelegateSwizzler proxyOriginalDelegate]
|
||||
*
|
||||
* This method calls +[GULAppDelegateSwizzler proxyOriginalDelegate] under the hood.
|
||||
* After calling this method the following App Delegate methods will be proxied in addition to
|
||||
* the methods proxied by proxyOriginalDelegate:
|
||||
* @code
|
||||
* - application:didRegisterForRemoteNotificationsWithDeviceToken:
|
||||
* - application:didFailToRegisterForRemoteNotificationsWithError:
|
||||
* - application:didReceiveRemoteNotification:fetchCompletionHandler:
|
||||
* - application:didReceiveRemoteNotification:
|
||||
* @endcode
|
||||
*
|
||||
* The method has no effect for extensions.
|
||||
*
|
||||
* @see proxyOriginalDelegate
|
||||
*/
|
||||
+ (void)proxyOriginalDelegateIncludingAPNSMethods;
|
||||
|
||||
/** Indicates whether app delegate proxy is explicitly disabled or enabled. Enabled by default.
|
||||
*
|
||||
* @return YES if AppDelegateProxy is Enabled, NO otherwise.
|
||||
*/
|
||||
+ (BOOL)isAppDelegateProxyEnabled;
|
||||
|
||||
/** Returns the current sharedApplication.
|
||||
*
|
||||
* @return the current application instance if in an app, or nil if in extension or if it doesn't
|
||||
* exist.
|
||||
*/
|
||||
+ (nullable GULApplication *)sharedApplication;
|
||||
|
||||
/** Do not initialize this class. */
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@end
|
||||
50
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULApplication.h
generated
Normal file
50
Pods/GoogleUtilities/GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULApplication.h
generated
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2019 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>
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define GULApplication UIApplication
|
||||
#define GULApplicationDelegate UIApplicationDelegate
|
||||
#define GULUserActivityRestoring UIUserActivityRestoring
|
||||
|
||||
static NSString *const kGULApplicationClassName = @"UIApplication";
|
||||
|
||||
#elif TARGET_OS_OSX
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#define GULApplication NSApplication
|
||||
#define GULApplicationDelegate NSApplicationDelegate
|
||||
#define GULUserActivityRestoring NSUserActivityRestoring
|
||||
|
||||
static NSString *const kGULApplicationClassName = @"NSApplication";
|
||||
|
||||
#elif TARGET_OS_WATCH
|
||||
|
||||
#import <WatchKit/WatchKit.h>
|
||||
|
||||
// We match the according watchOS API but swizzling should not work in watch
|
||||
#define GULApplication WKExtension
|
||||
#define GULApplicationDelegate WKExtensionDelegate
|
||||
#define GULUserActivityRestoring NSUserActivityRestoring
|
||||
|
||||
static NSString *const kGULApplicationClassName = @"WKExtension";
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2019 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 <TargetConditionals.h>
|
||||
|
||||
#if __has_include(<UIKit/UIKit.h>)
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#define UISCENE_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NSString *const GULSceneDelegateInterceptorID;
|
||||
|
||||
/** This class contains methods that isa swizzle the scene delegate. */
|
||||
@interface GULSceneDelegateSwizzler : NSProxy
|
||||
|
||||
#if UISCENE_SUPPORTED
|
||||
|
||||
/** Registers a scene delegate interceptor whose methods will be invoked as they're invoked on the
|
||||
* original scene delegate.
|
||||
*
|
||||
* @param interceptor An instance of a class that conforms to the application delegate protocol.
|
||||
* The interceptor is NOT retained.
|
||||
* @return A unique GULSceneDelegateInterceptorID if interceptor was successfully registered; nil
|
||||
* if it fails.
|
||||
*/
|
||||
+ (nullable GULSceneDelegateInterceptorID)registerSceneDelegateInterceptor:
|
||||
(id<UISceneDelegate>)interceptor API_AVAILABLE(ios(13.0), tvos(13.0));
|
||||
|
||||
/** Unregisters an interceptor with the given ID if it exists.
|
||||
*
|
||||
* @param interceptorID The object that was generated when the interceptor was registered.
|
||||
*/
|
||||
+ (void)unregisterSceneDelegateInterceptorWithID:(GULSceneDelegateInterceptorID)interceptorID
|
||||
API_AVAILABLE(ios(13.0), tvos(13.0));
|
||||
|
||||
/** Do not initialize this class. */
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
#endif // UISCENE_SUPPORTED
|
||||
|
||||
/** This method ensures that the original scene delegate has been proxied. Call this before
|
||||
* registering your interceptor. This method is safe to call multiple times (but it only proxies
|
||||
* the scene delegate once).
|
||||
*
|
||||
* The method has no effect for extensions.
|
||||
*/
|
||||
+ (void)proxyOriginalSceneDelegate;
|
||||
|
||||
/** Indicates whether scene delegate proxy is explicitly disabled or enabled. Enabled by default.
|
||||
*
|
||||
* @return YES if SceneDelegateProxy is Enabled, NO otherwise.
|
||||
*/
|
||||
+ (BOOL)isSceneDelegateProxyEnabled;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
56
Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h
generated
Normal file
56
Pods/GoogleUtilities/GoogleUtilities/Common/GULLoggerCodes.h
generated
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
typedef NS_ENUM(NSInteger, GULSwizzlerMessageCode) {
|
||||
// App Delegate Swizzling.
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling000 = 1000, // I-SWZ001000
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling001 = 1001, // I-SWZ001001
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling002 = 1002, // I-SWZ001002
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling003 = 1003, // I-SWZ001003
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling004 = 1004, // I-SWZ001004
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling005 = 1005, // I-SWZ001005
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling006 = 1006, // I-SWZ001006
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling007 = 1007, // I-SWZ001007
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling008 = 1008, // I-SWZ001008
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling009 = 1009, // I-SWZ001009
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling010 = 1010, // I-SWZ001010
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling011 = 1011, // I-SWZ001011
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling012 = 1012, // I-SWZ001012
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzling013 = 1013, // I-SWZ001013
|
||||
kGULSwizzlerMessageCodeAppDelegateSwizzlingInvalidAppDelegate = 1014, // I-SWZ001014
|
||||
|
||||
// Scene Delegate Swizzling.
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling000 = 1100, // I-SWZ001100
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling001 = 1101, // I-SWZ001101
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling002 = 1102, // I-SWZ001102
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling003 = 1103, // I-SWZ001103
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling004 = 1104, // I-SWZ001104
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling005 = 1105, // I-SWZ001105
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling006 = 1106, // I-SWZ001106
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling007 = 1107, // I-SWZ001107
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling008 = 1108, // I-SWZ001108
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling009 = 1109, // I-SWZ001109
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling010 = 1110, // I-SWZ001110
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling011 = 1111, // I-SWZ001111
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling012 = 1112, // I-SWZ001112
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzling013 = 1113, // I-SWZ001113
|
||||
kGULSwizzlerMessageCodeSceneDelegateSwizzlingInvalidSceneDelegate = 1114, // I-SWZ001114
|
||||
|
||||
// Method Swizzling.
|
||||
kGULSwizzlerMessageCodeMethodSwizzling000 = 2000, // I-SWZ002000
|
||||
};
|
||||
281
Pods/GoogleUtilities/GoogleUtilities/Environment/GULAppEnvironmentUtil.m
generated
Normal file
281
Pods/GoogleUtilities/GoogleUtilities/Environment/GULAppEnvironmentUtil.m
generated
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2017 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/GULAppEnvironmentUtil.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <sys/sysctl.h>
|
||||
#import <sys/utsname.h>
|
||||
|
||||
#import "third_party/IsAppEncrypted/Public/IsAppEncrypted.h"
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif // TARGET_OS_IOS
|
||||
|
||||
@implementation GULAppEnvironmentUtil
|
||||
|
||||
/// A key for the Info.plist to enable or disable checking if the App Store is running in a sandbox.
|
||||
/// This will affect your data integrity when using Firebase Analytics, as it will disable some
|
||||
/// necessary checks.
|
||||
static NSString *const kFIRAppStoreReceiptURLCheckEnabledKey =
|
||||
@"FirebaseAppStoreReceiptURLCheckEnabled";
|
||||
|
||||
/// The file name of the sandbox receipt. This is available on iOS >= 8.0
|
||||
static NSString *const kFIRAIdentitySandboxReceiptFileName = @"sandboxReceipt";
|
||||
|
||||
static BOOL HasSCInfoFolder(void) {
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
NSString *bundlePath = [NSBundle mainBundle].bundlePath;
|
||||
NSString *scInfoPath = [bundlePath stringByAppendingPathComponent:@"SC_Info"];
|
||||
return [[NSFileManager defaultManager] fileExistsAtPath:scInfoPath];
|
||||
#elif TARGET_OS_OSX
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
static BOOL HasEmbeddedMobileProvision(void) {
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
return [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"].length > 0;
|
||||
#elif TARGET_OS_OSX
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)isFromAppStore {
|
||||
static dispatch_once_t isEncryptedOnce;
|
||||
static BOOL isEncrypted = NO;
|
||||
|
||||
dispatch_once(&isEncryptedOnce, ^{
|
||||
isEncrypted = IsAppEncrypted();
|
||||
});
|
||||
|
||||
if ([GULAppEnvironmentUtil isSimulator]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// If an app contain the sandboxReceipt file, it means its coming from TestFlight
|
||||
// This must be checked before the SCInfo Folder check below since TestFlight apps may
|
||||
// also have an SCInfo folder.
|
||||
if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (HasSCInfoFolder()) {
|
||||
// When iTunes downloads a .ipa, it also gets a customized .sinf file which is added to the
|
||||
// main SC_Info directory.
|
||||
return YES;
|
||||
}
|
||||
|
||||
// For iOS >= 8.0, iTunesMetadata.plist is moved outside of the sandbox. Any attempt to read
|
||||
// the iTunesMetadata.plist outside of the sandbox will be rejected by Apple.
|
||||
// If the app does not contain the embedded.mobileprovision which is stripped out by Apple when
|
||||
// the app is submitted to store, then it is highly likely that it is from Apple Store.
|
||||
return isEncrypted && !HasEmbeddedMobileProvision();
|
||||
}
|
||||
|
||||
+ (BOOL)isAppStoreReceiptSandbox {
|
||||
// Since checking the App Store's receipt URL can be memory intensive, check the option in the
|
||||
// Info.plist if developers opted out of this check.
|
||||
id enableSandboxCheck =
|
||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:kFIRAppStoreReceiptURLCheckEnabledKey];
|
||||
if (enableSandboxCheck && [enableSandboxCheck isKindOfClass:[NSNumber class]] &&
|
||||
![enableSandboxCheck boolValue]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSURL *appStoreReceiptURL = [NSBundle mainBundle].appStoreReceiptURL;
|
||||
NSString *appStoreReceiptFileName = appStoreReceiptURL.lastPathComponent;
|
||||
return [appStoreReceiptFileName isEqualToString:kFIRAIdentitySandboxReceiptFileName];
|
||||
}
|
||||
|
||||
+ (BOOL)isSimulator {
|
||||
#if TARGET_OS_SIMULATOR
|
||||
return YES;
|
||||
#elif TARGET_OS_MACCATALYST
|
||||
return NO;
|
||||
#elif TARGET_OS_IOS || TARGET_OS_TV
|
||||
NSString *platform = [GULAppEnvironmentUtil deviceModel];
|
||||
return [platform isEqual:@"x86_64"] || [platform isEqual:@"i386"];
|
||||
#elif TARGET_OS_OSX
|
||||
return NO;
|
||||
#endif
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)getSysctlEntry:(const char *)sysctlKey {
|
||||
static NSString *entryValue;
|
||||
size_t size;
|
||||
sysctlbyname(sysctlKey, NULL, &size, NULL, 0);
|
||||
if (size > 0) {
|
||||
char *entryValueCStr = malloc(size);
|
||||
sysctlbyname(sysctlKey, entryValueCStr, &size, NULL, 0);
|
||||
entryValue = [NSString stringWithCString:entryValueCStr encoding:NSUTF8StringEncoding];
|
||||
free(entryValueCStr);
|
||||
return entryValue;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *)deviceModel {
|
||||
static dispatch_once_t once;
|
||||
static NSString *deviceModel;
|
||||
|
||||
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
||||
dispatch_once(&once, ^{
|
||||
// The `uname` function only returns x86_64 for Macs. Use `sysctlbyname` instead, but fall back
|
||||
// to the `uname` function if it fails.
|
||||
deviceModel = [GULAppEnvironmentUtil getSysctlEntry:"hw.model"];
|
||||
if (deviceModel.length == 0) {
|
||||
struct utsname systemInfo;
|
||||
if (uname(&systemInfo) == 0) {
|
||||
deviceModel = [NSString stringWithUTF8String:systemInfo.machine];
|
||||
}
|
||||
}
|
||||
});
|
||||
#else
|
||||
dispatch_once(&once, ^{
|
||||
struct utsname systemInfo;
|
||||
if (uname(&systemInfo) == 0) {
|
||||
deviceModel = [NSString stringWithUTF8String:systemInfo.machine];
|
||||
}
|
||||
});
|
||||
#endif // TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
||||
return deviceModel;
|
||||
}
|
||||
|
||||
+ (NSString *)deviceSimulatorModel {
|
||||
static dispatch_once_t once;
|
||||
static NSString *model = nil;
|
||||
|
||||
dispatch_once(&once, ^{
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#if TARGET_OS_WATCH
|
||||
model = @"watchOS Simulator";
|
||||
#elif TARGET_OS_TV
|
||||
model = @"tvOS Simulator";
|
||||
#elif TARGET_OS_VISION
|
||||
model = @"visionOS Simulator";
|
||||
#elif TARGET_OS_IOS
|
||||
switch ([[UIDevice currentDevice] userInterfaceIdiom]) {
|
||||
case UIUserInterfaceIdiomPhone:
|
||||
model = @"iOS Simulator (iPhone)";
|
||||
break;
|
||||
case UIUserInterfaceIdiomPad:
|
||||
model = @"iOS Simulator (iPad)";
|
||||
break;
|
||||
default:
|
||||
model = @"iOS Simulator (Unknown)";
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#elif TARGET_OS_EMBEDDED
|
||||
model = [GULAppEnvironmentUtil getSysctlEntry:"hw.machine"];
|
||||
#else
|
||||
model = [GULAppEnvironmentUtil getSysctlEntry:"hw.model"];
|
||||
#endif
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
+ (NSString *)systemVersion {
|
||||
#if TARGET_OS_IOS
|
||||
return [UIDevice currentDevice].systemVersion;
|
||||
#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]
|
||||
initWithFormat:@"%ld.%ld", (long)osVersion.majorVersion, (long)osVersion.minorVersion];
|
||||
if (osVersion.patchVersion != 0) {
|
||||
[versionString appendFormat:@".%ld", (long)osVersion.patchVersion];
|
||||
}
|
||||
return versionString;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (BOOL)isAppExtension {
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
||||
// Documented by <a href="https://goo.gl/RRB2Up">Apple</a>
|
||||
BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
|
||||
return appExtension;
|
||||
#elif TARGET_OS_OSX
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSString *)applePlatform {
|
||||
NSString *applePlatform = @"unknown";
|
||||
|
||||
// When a Catalyst app is run on macOS then both `TARGET_OS_MACCATALYST` and `TARGET_OS_IOS` are
|
||||
// `true`, which means the condition list is order-sensitive.
|
||||
#if TARGET_OS_MACCATALYST
|
||||
applePlatform = @"maccatalyst";
|
||||
#elif TARGET_OS_IOS
|
||||
if (@available(iOS 14.0, *)) {
|
||||
// Early iOS 14 betas do not include isiOSAppOnMac (#6969)
|
||||
applePlatform = ([[NSProcessInfo processInfo] respondsToSelector:@selector(isiOSAppOnMac)] &&
|
||||
[NSProcessInfo processInfo].isiOSAppOnMac)
|
||||
? @"ios_on_mac"
|
||||
: @"ios";
|
||||
} else {
|
||||
applePlatform = @"ios";
|
||||
}
|
||||
#elif TARGET_OS_TV
|
||||
applePlatform = @"tvos";
|
||||
#elif TARGET_OS_OSX
|
||||
applePlatform = @"macos";
|
||||
#elif TARGET_OS_WATCH
|
||||
applePlatform = @"watchos";
|
||||
#elif TARGET_OS_VISION
|
||||
applePlatform = @"visionos";
|
||||
#endif // TARGET_OS_MACCATALYST
|
||||
|
||||
return applePlatform;
|
||||
}
|
||||
|
||||
+ (NSString *)appleDevicePlatform {
|
||||
NSString *firebasePlatform = [GULAppEnvironmentUtil applePlatform];
|
||||
#if TARGET_OS_IOS
|
||||
// This check is necessary because iOS-only apps running on iPad
|
||||
// will report UIUserInterfaceIdiomPhone via UI_USER_INTERFACE_IDIOM().
|
||||
if ([firebasePlatform isEqualToString:@"ios"] &&
|
||||
([[UIDevice currentDevice].model.lowercaseString containsString:@"ipad"] ||
|
||||
[[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)) {
|
||||
return @"ipados";
|
||||
}
|
||||
#endif
|
||||
|
||||
return firebasePlatform;
|
||||
}
|
||||
|
||||
+ (NSString *)deploymentType {
|
||||
#if SWIFT_PACKAGE
|
||||
NSString *deploymentType = @"swiftpm";
|
||||
#elif FIREBASE_BUILD_CARTHAGE
|
||||
NSString *deploymentType = @"carthage";
|
||||
#elif FIREBASE_BUILD_ZIP_FILE
|
||||
NSString *deploymentType = @"zip";
|
||||
#elif COCOAPODS
|
||||
NSString *deploymentType = @"cocoapods";
|
||||
#else
|
||||
NSString *deploymentType = @"unknown";
|
||||
#endif
|
||||
|
||||
return deploymentType;
|
||||
}
|
||||
|
||||
@end
|
||||
80
Pods/GoogleUtilities/GoogleUtilities/Environment/NetworkInfo/GULNetworkInfo.m
generated
Normal file
80
Pods/GoogleUtilities/GoogleUtilities/Environment/NetworkInfo/GULNetworkInfo.m
generated
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2022 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/GULNetworkInfo.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#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/CTTelephonyNetworkInfo.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#endif
|
||||
|
||||
@implementation GULNetworkInfo
|
||||
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
+ (CTTelephonyNetworkInfo *)getNetworkInfo {
|
||||
static CTTelephonyNetworkInfo *networkInfo;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
networkInfo = [[CTTelephonyNetworkInfo alloc] init];
|
||||
});
|
||||
return networkInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
+ (GULNetworkType)getNetworkType {
|
||||
GULNetworkType networkType = GULNetworkTypeNone;
|
||||
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
static SCNetworkReachabilityRef reachabilityRef = 0;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorSystemDefault, "google.com");
|
||||
});
|
||||
|
||||
if (!reachabilityRef) {
|
||||
return GULNetworkTypeNone;
|
||||
}
|
||||
|
||||
SCNetworkReachabilityFlags reachabilityFlags = 0;
|
||||
SCNetworkReachabilityGetFlags(reachabilityRef, &reachabilityFlags);
|
||||
|
||||
// Parse the network flags to set the network type.
|
||||
if (reachabilityFlags & kSCNetworkReachabilityFlagsReachable) {
|
||||
if (reachabilityFlags & kSCNetworkReachabilityFlagsIsWWAN) {
|
||||
networkType = GULNetworkTypeMobile;
|
||||
} else {
|
||||
networkType = GULNetworkTypeWIFI;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return networkType;
|
||||
}
|
||||
|
||||
+ (NSString *)getNetworkRadioType {
|
||||
#ifdef TARGET_HAS_MOBILE_CONNECTIVITY
|
||||
CTTelephonyNetworkInfo *networkInfo = [GULNetworkInfo getNetworkInfo];
|
||||
if (networkInfo.serviceCurrentRadioAccessTechnology.count) {
|
||||
return networkInfo.serviceCurrentRadioAccessTechnology.allValues[0] ?: @"";
|
||||
}
|
||||
#endif
|
||||
return @"";
|
||||
}
|
||||
|
||||
@end
|
||||
62
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h
generated
Normal file
62
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h
generated
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
|
||||
@interface GULAppEnvironmentUtil : NSObject
|
||||
|
||||
/// Indicates whether the app is from Apple Store or not. Returns NO if the app is on simulator,
|
||||
/// development environment or sideloaded.
|
||||
+ (BOOL)isFromAppStore;
|
||||
|
||||
/// Indicates whether the app is a Testflight app. Returns YES if the app has sandbox receipt.
|
||||
/// Returns NO otherwise.
|
||||
+ (BOOL)isAppStoreReceiptSandbox;
|
||||
|
||||
/// Indicates whether the app is on simulator or not at runtime depending on the device
|
||||
/// architecture.
|
||||
+ (BOOL)isSimulator;
|
||||
|
||||
/// The current device model. Returns an empty string if device model cannot be retrieved.
|
||||
+ (nullable NSString *)deviceModel;
|
||||
|
||||
/// The current device model, with simulator-specific values. Returns an empty string if device
|
||||
/// model cannot be retrieved.
|
||||
+ (nullable NSString *)deviceSimulatorModel;
|
||||
|
||||
/// The current operating system version. Returns an empty string if the system version cannot be
|
||||
/// retrieved.
|
||||
+ (NSString *)systemVersion;
|
||||
|
||||
/// Indicates whether it is running inside an extension or an app.
|
||||
+ (BOOL)isAppExtension;
|
||||
|
||||
/// @return An Apple platform. Possible values "ios", "tvos", "macos", "watchos", "maccatalyst", and
|
||||
/// "visionos".
|
||||
+ (NSString *)applePlatform;
|
||||
|
||||
/// @return An Apple Device platform. Same possible values as `applePlatform`, with the addition of
|
||||
/// "ipados".
|
||||
+ (NSString *)appleDevicePlatform;
|
||||
|
||||
/// @return The way the library was added to the app, e.g. "swiftpm", "cocoapods", etc.
|
||||
+ (NSString *)deploymentType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
84
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainStorage.h
generated
Normal file
84
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainStorage.h
generated
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 provides a convenient, multiplatform abstraction of the Keychain.
|
||||
///
|
||||
/// When using this API on macOS, the corresponding target must be signed with a provisioning
|
||||
/// profile that has the Keychain Sharing capability enabled.
|
||||
@interface GULKeychainStorage : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/** Initializes the keychain storage with Keychain Service name.
|
||||
* @param service A Keychain Service name that will be used to store and retrieve objects. See also
|
||||
* `kSecAttrService`.
|
||||
*/
|
||||
- (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.
|
||||
/// @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.
|
||||
/// @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.
|
||||
/// @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
|
||||
/// `kSecMatchSearchList` and `kSecUseKeychain`. It is mostly intended to be used by unit tests.
|
||||
@property(nonatomic, nullable) SecKeychainRef keychainRef;
|
||||
#endif // TARGET_OS_OSX
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
64
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainUtils.h
generated
Normal file
64
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainUtils.h
generated
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kGULKeychainUtilsErrorDomain;
|
||||
|
||||
/// A collection of helper functions that abstract away common Keychain operations.
|
||||
///
|
||||
/// When using this API on macOS, the corresponding target must be signed with a provisioning
|
||||
/// profile that has the Keychain Sharing capability enabled.
|
||||
@interface GULKeychainUtils : NSObject
|
||||
|
||||
/** Fetches a keychain item data matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemCopyMatching` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns Data for the first Keychain Item matching the provided query or `nil` if there is not
|
||||
* such an item (`outError` will be `nil` in this case) or an error occurred.
|
||||
*/
|
||||
+ (nullable NSData *)getItemWithQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Stores data to a Keychain Item matching to the provided query. An existing Keychain Item
|
||||
* matching the query parameters will be updated or a new will be created.
|
||||
* @param item A Keychain Item data to store.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemAdd` and
|
||||
* `SecItemUpdate` for details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` when data was successfully stored, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)setItem:(NSData *)item
|
||||
withQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
/** Removes a Keychain Item matching to the provided query.
|
||||
* @param query A dictionary with Keychain query parameters. See docs for `SecItemDelete` for
|
||||
* details.
|
||||
* @param outError A pointer to `NSError` instance or `NULL`. The instance at `outError` will be
|
||||
* assigned with an error if there is.
|
||||
* @returns `YES` if the item was removed successfully or doesn't exist, `NO` otherwise.
|
||||
*/
|
||||
+ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
43
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULNetworkInfo.h
generated
Normal file
43
Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GoogleUtilities/GULNetworkInfo.h
generated
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2022 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 type of network that the device is running with. Values should correspond to the NetworkType
|
||||
/// values in android/play/playlog/proto/clientanalytics.proto
|
||||
typedef NS_ENUM(NSInteger, GULNetworkType) {
|
||||
GULNetworkTypeNone = -1,
|
||||
GULNetworkTypeMobile = 0,
|
||||
GULNetworkTypeWIFI = 1,
|
||||
};
|
||||
|
||||
/// Collection of utilities to read network status information
|
||||
@interface GULNetworkInfo : NSObject
|
||||
|
||||
/// 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
|
||||
/// values for the other platforms when applicable.
|
||||
+ (GULNetworkType)getNetworkType;
|
||||
|
||||
/// Returns a string indicating the radio access technology used by the app. The return value will
|
||||
/// be one of CTRadioAccess constants defined in
|
||||
/// https://developer.apple.com/documentation/coretelephony/cttelephonynetworkinfo/radio_access_technology_constants
|
||||
+ (NSString *)getNetworkRadioType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
196
Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainStorage.m
generated
Normal file
196
Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainStorage.m
generated
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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/GULKeychainStorage.h"
|
||||
#import <Security/Security.h>
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULKeychainUtils.h"
|
||||
|
||||
@interface GULKeychainStorage ()
|
||||
@property(nonatomic, readonly) dispatch_queue_t keychainQueue;
|
||||
@property(nonatomic, readonly) dispatch_queue_t inMemoryCacheQueue;
|
||||
@property(nonatomic, readonly) NSString *service;
|
||||
@property(nonatomic, readonly) NSCache<NSString *, id<NSSecureCoding>> *inMemoryCache;
|
||||
@end
|
||||
|
||||
@implementation GULKeychainStorage
|
||||
|
||||
- (instancetype)initWithService:(NSString *)service {
|
||||
NSCache *cache = [[NSCache alloc] init];
|
||||
// Cache up to 5 installations.
|
||||
cache.countLimit = 5;
|
||||
return [self initWithService:service cache:cache];
|
||||
}
|
||||
|
||||
- (instancetype)initWithService:(NSString *)service cache:(NSCache *)cache {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_keychainQueue =
|
||||
dispatch_queue_create("com.gul.KeychainStorage.Keychain", DISPATCH_QUEUE_SERIAL);
|
||||
_inMemoryCacheQueue =
|
||||
dispatch_queue_create("com.gul.KeychainStorage.InMemoryCache", DISPATCH_QUEUE_SERIAL);
|
||||
_service = [service copy];
|
||||
_inMemoryCache = cache;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (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]];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (![GULKeychainUtils setItem:encodedObject withQuery:query error:&error]) {
|
||||
completionHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
completionHandler(object, nil);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (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]) {
|
||||
completionHandler(error);
|
||||
} else {
|
||||
completionHandler(nil);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (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.
|
||||
dispatch_async(self.keychainQueue, ^{
|
||||
NSDictionary *query = [self keychainQueryWithKey:key accessGroup:accessGroup];
|
||||
NSError *error;
|
||||
NSData *encodedObject = [GULKeychainUtils getItemWithQuery:query error:&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;
|
||||
}
|
||||
|
||||
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 {
|
||||
[self.inMemoryCache removeAllObjects];
|
||||
}
|
||||
|
||||
#pragma mark - Keychain
|
||||
|
||||
- (NSMutableDictionary<NSString *, id> *)keychainQueryWithKey:(NSString *)key
|
||||
accessGroup:(nullable NSString *)accessGroup {
|
||||
NSMutableDictionary<NSString *, id> *query = [NSMutableDictionary dictionary];
|
||||
|
||||
query[(__bridge NSString *)kSecClass] = (__bridge NSString *)kSecClassGenericPassword;
|
||||
query[(__bridge NSString *)kSecAttrService] = self.service;
|
||||
query[(__bridge NSString *)kSecAttrAccount] = key;
|
||||
|
||||
if (accessGroup) {
|
||||
query[(__bridge NSString *)kSecAttrAccessGroup] = accessGroup;
|
||||
}
|
||||
|
||||
if (@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)) {
|
||||
// Ensures that the keychain query behaves the same across all platforms.
|
||||
// See go/firebase-macos-keychain-popups for details.
|
||||
query[(__bridge id)kSecUseDataProtectionKeychain] = (__bridge id)kCFBooleanTrue;
|
||||
}
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
if (self.keychainRef) {
|
||||
query[(__bridge NSString *)kSecUseKeychain] = (__bridge id)(self.keychainRef);
|
||||
query[(__bridge NSString *)kSecMatchSearchList] = @[ (__bridge id)(self.keychainRef) ];
|
||||
}
|
||||
#endif // TARGET_OS_OSX
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@end
|
||||
133
Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainUtils.m
generated
Normal file
133
Pods/GoogleUtilities/GoogleUtilities/Environment/SecureStorage/GULKeychainUtils.m
generated
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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/GULKeychainUtils.h"
|
||||
|
||||
NSString *const kGULKeychainUtilsErrorDomain = @"com.gul.keychain.ErrorDomain";
|
||||
|
||||
@implementation GULKeychainUtils
|
||||
|
||||
+ (nullable NSData *)getItemWithQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError {
|
||||
NSMutableDictionary *mutableGetItemQuery =
|
||||
[[[self class] multiplatformQueryWithQuery:query] mutableCopy];
|
||||
|
||||
mutableGetItemQuery[(__bridge id)kSecReturnData] = @YES;
|
||||
mutableGetItemQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
|
||||
|
||||
CFDataRef result = NULL;
|
||||
OSStatus status =
|
||||
SecItemCopyMatching((__bridge CFDictionaryRef)mutableGetItemQuery, (CFTypeRef *)&result);
|
||||
|
||||
if (status == errSecSuccess && result != NULL) {
|
||||
if (outError) {
|
||||
*outError = nil;
|
||||
}
|
||||
|
||||
return (__bridge_transfer NSData *)result;
|
||||
}
|
||||
|
||||
if (status == errSecItemNotFound) {
|
||||
if (outError) {
|
||||
*outError = nil;
|
||||
}
|
||||
} else {
|
||||
if (outError) {
|
||||
*outError = [self keychainErrorWithFunction:@"SecItemCopyMatching" status:status];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (BOOL)setItem:(NSData *)item
|
||||
withQuery:(NSDictionary *)query
|
||||
error:(NSError *_Nullable *_Nullable)outError {
|
||||
NSDictionary *multiplatformQuery = [[self class] multiplatformQueryWithQuery:query];
|
||||
|
||||
NSData *existingItem = [self getItemWithQuery:multiplatformQuery error:outError];
|
||||
if (outError && *outError) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
OSStatus status;
|
||||
if (!existingItem) {
|
||||
NSMutableDictionary *mutableAddItemQuery = [multiplatformQuery mutableCopy];
|
||||
mutableAddItemQuery[(__bridge id)kSecAttrAccessible] =
|
||||
(__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
||||
mutableAddItemQuery[(__bridge id)kSecValueData] = item;
|
||||
|
||||
status = SecItemAdd((__bridge CFDictionaryRef)mutableAddItemQuery, NULL);
|
||||
} else {
|
||||
NSDictionary *attributes = @{(__bridge id)kSecValueData : item};
|
||||
status = SecItemUpdate((__bridge CFDictionaryRef)multiplatformQuery,
|
||||
(__bridge CFDictionaryRef)attributes);
|
||||
}
|
||||
|
||||
if (status == noErr) {
|
||||
if (outError) {
|
||||
*outError = nil;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSString *function = existingItem ? @"SecItemUpdate" : @"SecItemAdd";
|
||||
if (outError) {
|
||||
*outError = [self keychainErrorWithFunction:function status:status];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)removeItemWithQuery:(NSDictionary *)query error:(NSError *_Nullable *_Nullable)outError {
|
||||
NSDictionary *deleteItemQuery = [[self class] multiplatformQueryWithQuery:query];
|
||||
|
||||
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)deleteItemQuery);
|
||||
|
||||
if (status == noErr || status == errSecItemNotFound) {
|
||||
if (outError) {
|
||||
*outError = nil;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (outError) {
|
||||
*outError = [self keychainErrorWithFunction:@"SecItemDelete" status:status];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
/// Returns a `NSDictionary` query that behaves the same across all platforms.
|
||||
/// - Note: In practice, this API only makes a difference to keychain queries on macOS.
|
||||
/// See go/firebase-macos-keychain-popups for details.
|
||||
/// - Parameter query: A query to create the protected keychain query with.
|
||||
+ (NSDictionary *)multiplatformQueryWithQuery:(NSDictionary *)query {
|
||||
NSMutableDictionary *multiplatformQuery = [query mutableCopy];
|
||||
if (@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)) {
|
||||
multiplatformQuery[(__bridge id)kSecUseDataProtectionKeychain] = (__bridge id)kCFBooleanTrue;
|
||||
}
|
||||
return [multiplatformQuery copy];
|
||||
}
|
||||
|
||||
#pragma mark - Errors
|
||||
|
||||
+ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status {
|
||||
NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status];
|
||||
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey : failureReason};
|
||||
return [NSError errorWithDomain:kGULKeychainUtilsErrorDomain code:0 userInfo:userInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
223
Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m
generated
Normal file
223
Pods/GoogleUtilities/GoogleUtilities/Logger/GULLogger.m
generated
Normal file
@ -0,0 +1,223 @@
|
||||
// Copyright 2018 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/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
#import <os/log.h>
|
||||
|
||||
#import "GoogleUtilities/Environment/Public/GoogleUtilities/GULAppEnvironmentUtil.h"
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLoggerLevel.h"
|
||||
|
||||
static dispatch_once_t sGULLoggerOnceToken;
|
||||
|
||||
static dispatch_queue_t sGULClientQueue;
|
||||
|
||||
static BOOL sGULLoggerDebugMode;
|
||||
|
||||
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 GULLoggerInitialize(void) {
|
||||
dispatch_once(&sGULLoggerOnceToken, ^{
|
||||
sGULLoggerMaximumLevel = GULLoggerLevelNotice;
|
||||
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
|
||||
error:NULL];
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
void GULLoggerForceDebug(void) {
|
||||
// We should enable debug mode if we're not running from App Store.
|
||||
if (![GULAppEnvironmentUtil isFromAppStore]) {
|
||||
sGULLoggerDebugMode = YES;
|
||||
GULSetLoggerLevel(GULLoggerLevelDebug);
|
||||
}
|
||||
}
|
||||
|
||||
GULLoggerLevel GULGetLoggerLevel(void) {
|
||||
return sGULLoggerMaximumLevel;
|
||||
}
|
||||
|
||||
__attribute__((no_sanitize("thread"))) void GULSetLoggerLevel(GULLoggerLevel loggerLevel) {
|
||||
if (loggerLevel < GULLoggerLevelMin || loggerLevel > GULLoggerLevelMax) {
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerLogger, YES, @"I-COR000023",
|
||||
@"Invalid logger level, %ld", (long)loggerLevel);
|
||||
return;
|
||||
}
|
||||
GULLoggerInitialize();
|
||||
// We should not raise the logger level if we are running from App Store.
|
||||
if (loggerLevel >= GULLoggerLevelNotice && [GULAppEnvironmentUtil isFromAppStore]) {
|
||||
return;
|
||||
}
|
||||
|
||||
sGULLoggerMaximumLevel = loggerLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the level is high enough to be loggable.
|
||||
*/
|
||||
__attribute__((no_sanitize("thread"))) BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel) {
|
||||
GULLoggerInitialize();
|
||||
if (sGULLoggerDebugMode) {
|
||||
return YES;
|
||||
}
|
||||
return (BOOL)(loggerLevel <= sGULLoggerMaximumLevel);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void GULResetLogger(void) {
|
||||
sGULLoggerOnceToken = 0;
|
||||
sGULLoggerDebugMode = NO;
|
||||
sGULLoggerMaximumLevel = GULLoggerLevelNotice;
|
||||
}
|
||||
|
||||
dispatch_queue_t getGULClientQueue(void) {
|
||||
return sGULClientQueue;
|
||||
}
|
||||
|
||||
BOOL getGULLoggerDebugMode(void) {
|
||||
return sGULLoggerDebugMode;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GULLoggerRegisterVersion(NSString *version) {
|
||||
sVersion = version;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
NSCAssert(messageCode.length == 11, @"Incorrect message code length.");
|
||||
NSRange messageCodeRange = NSMakeRange(0, messageCode.length);
|
||||
NSUInteger __unused numberOfMatches =
|
||||
[sMessageCodeRegex numberOfMatchesInString:messageCode options:0 range:messageCodeRange];
|
||||
NSCAssert(numberOfMatches == 1, @"Incorrect message code format.");
|
||||
#endif
|
||||
NSString *logMsg;
|
||||
if (args_ptr == NULL) {
|
||||
logMsg = message;
|
||||
} else {
|
||||
logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr];
|
||||
}
|
||||
logMsg = [NSString stringWithFormat:@"%@ - %@[%@] %@", sVersion, category, messageCode, logMsg];
|
||||
dispatch_async(sGULClientQueue, ^{
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the logging functions using macros.
|
||||
*
|
||||
* Calling GULLogError({service}, @"I-XYZ000001", @"Configure %@ failed.", @"blah") shows:
|
||||
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [{service}][I-XYZ000001] Configure blah failed.
|
||||
* 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 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)
|
||||
GUL_LOGGING_FUNCTION(Warning)
|
||||
GUL_LOGGING_FUNCTION(Notice)
|
||||
GUL_LOGGING_FUNCTION(Info)
|
||||
GUL_LOGGING_FUNCTION(Debug)
|
||||
|
||||
#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 {
|
||||
GULOSLogBasic(level, kGULLogSubsystem, service, NO, messageCode, message, args);
|
||||
}
|
||||
|
||||
@end
|
||||
165
Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h
generated
Normal file
165
Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h
generated
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2018 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 "GULLoggerLevel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The services used in the logger.
|
||||
*
|
||||
* DEPRECATED; use NSString instead.
|
||||
*/
|
||||
typedef NSString *const GULLoggerService;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/// Used for other GoogleUtilities logging.
|
||||
extern NSString *const kGULLogSubsystem;
|
||||
|
||||
/// Initialize GULLogger.
|
||||
extern void GULLoggerInitialize(void);
|
||||
|
||||
/// Override log level to Debug.
|
||||
void GULLoggerForceDebug(void);
|
||||
|
||||
/// Gets the current `GULLoggerLevel`.
|
||||
extern GULLoggerLevel GULGetLoggerLevel(void);
|
||||
|
||||
/**
|
||||
* Changes the default logging level of GULLoggerLevelNotice to a user-specified level.
|
||||
* The default level cannot be set above GULLoggerLevelNotice if the app is running from App Store.
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
*/
|
||||
extern void GULSetLoggerLevel(GULLoggerLevel loggerLevel);
|
||||
|
||||
/**
|
||||
* Checks if the specified logger level is loggable given the current settings.
|
||||
* (required) log level (one of the GULLoggerLevel enum values).
|
||||
*/
|
||||
extern BOOL GULIsLoggableLevel(GULLoggerLevel loggerLevel);
|
||||
|
||||
/**
|
||||
* Register version to include in logs.
|
||||
* (required) version
|
||||
*/
|
||||
extern void GULLoggerRegisterVersion(NSString *version);
|
||||
|
||||
/**
|
||||
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||
* not log any messages with a level higher than GULLoggerLevelNotice to avoid log spamming.
|
||||
* (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.
|
||||
*/
|
||||
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
|
||||
#else
|
||||
va_list _Nullable args_ptr
|
||||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
* The following functions accept the following parameters in order:
|
||||
* (required) service name of type GULLoggerService.
|
||||
* (required) message code starting from "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".
|
||||
* See go/firebase-log-proposal for details.
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) the list of arguments to substitute into the format string.
|
||||
* Example usage:
|
||||
* GULLogError(kGULLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||
*/
|
||||
extern void GULOSLogError(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) 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(5, 6);
|
||||
extern void GULOSLogDebug(NSString *subsystem,
|
||||
GULLoggerService category,
|
||||
BOOL force,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
...) NS_FORMAT_FUNCTION(5, 6);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@interface GULLoggerWrapper : NSObject
|
||||
|
||||
/// 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.
|
||||
/// - arguments: 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
|
||||
subsystem:(NSString *)subsystem
|
||||
category:(NSString *)category
|
||||
messageCode:(NSString *)messageCode
|
||||
message:(NSString *)message
|
||||
arguments:(va_list)args;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
47
Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GoogleUtilities/GULLoggerLevel.h
generated
Normal file
47
Pods/GoogleUtilities/GoogleUtilities/Logger/Public/GoogleUtilities/GULLoggerLevel.h
generated
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 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 log levels used by internal logging.
|
||||
typedef NS_ENUM(NSInteger, GULLoggerLevel) {
|
||||
/// 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,
|
||||
|
||||
/// The maximum (least severe) supported logging level.
|
||||
GULLoggerLevelMax = GULLoggerLevelDebug
|
||||
} NS_SWIFT_NAME(GoogleLoggerLevel);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
154
Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/GULSwizzler.m
generated
Normal file
154
Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/GULSwizzler.m
generated
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2018 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/MethodSwizzler/Public/GoogleUtilities/GULSwizzler.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#import "GoogleUtilities/Common/GULLoggerCodes.h"
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
static GULLoggerService kGULLoggerSwizzler = @"[GoogleUtilities/MethodSwizzler]";
|
||||
#endif
|
||||
|
||||
dispatch_queue_t GetGULSwizzlingQueue(void) {
|
||||
static dispatch_queue_t queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = dispatch_queue_create("com.google.GULSwizzler", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
return queue;
|
||||
}
|
||||
|
||||
@implementation GULSwizzler
|
||||
|
||||
+ (void)swizzleClass:(Class)aClass
|
||||
selector:(SEL)selector
|
||||
isClassSelector:(BOOL)isClassSelector
|
||||
withBlock:(nullable id)block {
|
||||
dispatch_sync(GetGULSwizzlingQueue(), ^{
|
||||
NSAssert(selector, @"The selector cannot be NULL");
|
||||
NSAssert(aClass, @"The class cannot be Nil");
|
||||
Class resolvedClass = aClass;
|
||||
Method method = nil;
|
||||
if (isClassSelector) {
|
||||
method = class_getClassMethod(aClass, selector);
|
||||
resolvedClass = object_getClass(aClass);
|
||||
} else {
|
||||
method = class_getInstanceMethod(aClass, selector);
|
||||
}
|
||||
NSAssert(method, @"You're attempting to swizzle a method that doesn't exist. (%@, %@)",
|
||||
NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
|
||||
IMP newImp = imp_implementationWithBlock(block);
|
||||
#ifdef DEBUG
|
||||
IMP currentImp = class_getMethodImplementation(resolvedClass, selector);
|
||||
Class class = NSClassFromString(@"GULSwizzlingCache");
|
||||
if (class) {
|
||||
SEL cacheSelector = NSSelectorFromString(@"cacheCurrentIMP:forNewIMP:forClass:withSelector:");
|
||||
NSMethodSignature *methodSignature = [class methodSignatureForSelector:cacheSelector];
|
||||
if (methodSignature != nil) {
|
||||
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[inv setSelector:cacheSelector];
|
||||
[inv setTarget:class];
|
||||
[inv setArgument:&(currentImp) atIndex:2];
|
||||
[inv setArgument:&(newImp) atIndex:3];
|
||||
[inv setArgument:&(resolvedClass) atIndex:4];
|
||||
[inv setArgument:(void *_Nonnull)&(selector) atIndex:5];
|
||||
[inv invoke];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *typeEncoding = method_getTypeEncoding(method);
|
||||
__unused IMP originalImpOfClass =
|
||||
class_replaceMethod(resolvedClass, selector, newImp, typeEncoding);
|
||||
|
||||
#ifdef DEBUG
|
||||
// If !originalImpOfClass, then the IMP came from a superclass.
|
||||
if (originalImpOfClass) {
|
||||
SEL selector = NSSelectorFromString(@"originalIMPOfCurrentIMP:");
|
||||
NSMethodSignature *methodSignature = [class methodSignatureForSelector:selector];
|
||||
if (methodSignature != nil) {
|
||||
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[inv setSelector:selector];
|
||||
[inv setTarget:class];
|
||||
[inv setArgument:&(currentImp) atIndex:2];
|
||||
[inv invoke];
|
||||
IMP testOriginal;
|
||||
[inv getReturnValue:&testOriginal];
|
||||
if (originalImpOfClass != testOriginal) {
|
||||
GULOSLogWarning(
|
||||
kGULLogSubsystem, kGULLoggerSwizzler, NO,
|
||||
[NSString
|
||||
stringWithFormat:@"I-SWZ%06ld", (long)kGULSwizzlerMessageCodeMethodSwizzling000],
|
||||
@"Swizzling class: %@ SEL:%@ after it has been previously been swizzled.",
|
||||
NSStringFromClass(resolvedClass), NSStringFromSelector(selector));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
+ (nullable IMP)currentImplementationForClass:(Class)aClass
|
||||
selector:(SEL)selector
|
||||
isClassSelector:(BOOL)isClassSelector {
|
||||
NSAssert(selector, @"The selector cannot be NULL");
|
||||
NSAssert(aClass, @"The class cannot be Nil");
|
||||
if (selector == NULL || aClass == nil) {
|
||||
return nil;
|
||||
}
|
||||
__block IMP currentIMP = nil;
|
||||
dispatch_sync(GetGULSwizzlingQueue(), ^{
|
||||
Method method = nil;
|
||||
if (isClassSelector) {
|
||||
method = class_getClassMethod(aClass, selector);
|
||||
} else {
|
||||
method = class_getInstanceMethod(aClass, selector);
|
||||
}
|
||||
NSAssert(method, @"The Method for this class/selector combo doesn't exist (%@, %@).",
|
||||
NSStringFromClass(aClass), NSStringFromSelector(selector));
|
||||
if (method == nil) {
|
||||
return;
|
||||
}
|
||||
currentIMP = method_getImplementation(method);
|
||||
NSAssert(currentIMP, @"The IMP for this class/selector combo doesn't exist (%@, %@).",
|
||||
NSStringFromClass(aClass), NSStringFromSelector(selector));
|
||||
});
|
||||
return currentIMP;
|
||||
}
|
||||
|
||||
+ (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector {
|
||||
Method method = isClassSelector ? class_getClassMethod(aClass, selector)
|
||||
: class_getInstanceMethod(aClass, selector);
|
||||
return method != nil;
|
||||
}
|
||||
|
||||
+ (NSArray<id> *)ivarObjectsForObject:(id)object {
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
unsigned int count;
|
||||
Ivar *vars = class_copyIvarList([object class], &count);
|
||||
for (NSUInteger i = 0; i < count; i++) {
|
||||
const char *typeEncoding = ivar_getTypeEncoding(vars[i]);
|
||||
// Check to see if the ivar is an object.
|
||||
if (strncmp(typeEncoding, "@", 1) == 0) {
|
||||
id ivarObject = object_getIvar(object, vars[i]);
|
||||
[array addObject:ivarObject];
|
||||
}
|
||||
}
|
||||
free(vars);
|
||||
return array;
|
||||
}
|
||||
@end
|
||||
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
|
||||
/**
|
||||
* GULOriginalIMPConvenienceMacros.h
|
||||
*
|
||||
* This header contains convenience macros for invoking the original IMP of a swizzled method.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes no arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP0(__receivingObject, __swizzledSEL, __returnType, __originalIMP) \
|
||||
((__returnType(*)(id, SEL))__originalIMP)(__receivingObject, __swizzledSEL)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 1 argument.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP1(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1)))__originalIMP)(__receivingObject, __swizzledSEL, \
|
||||
__arg1)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 2 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP2(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 3 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP3(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), \
|
||||
__typeof__(__arg3)))__originalIMP)(__receivingObject, __swizzledSEL, __arg1, \
|
||||
__arg2, __arg3)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 4 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP4(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4)))__originalIMP)(__receivingObject, __swizzledSEL, __arg1, \
|
||||
__arg2, __arg3, __arg4)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 5 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
* @param __arg5 The fifth argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP5(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4, __arg5) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4), __typeof__(__arg5)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 6 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
* @param __arg5 The fifth argument.
|
||||
* @param __arg6 The sixth argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP6(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4, __arg5, __arg6) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 7 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
* @param __arg5 The fifth argument.
|
||||
* @param __arg6 The sixth argument.
|
||||
* @param __arg7 The seventh argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP7(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6), \
|
||||
__typeof__(__arg7)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 8 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
* @param __arg5 The fifth argument.
|
||||
* @param __arg6 The sixth argument.
|
||||
* @param __arg7 The seventh argument.
|
||||
* @param __arg8 The eighth argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP8(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6), \
|
||||
__typeof__(__arg7), __typeof__(__arg8)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, \
|
||||
__arg8)
|
||||
|
||||
/**
|
||||
* Invokes original IMP when the original selector takes 9 arguments.
|
||||
*
|
||||
* @param __receivingObject The object on which the IMP is invoked.
|
||||
* @param __swizzledSEL The selector used for swizzling.
|
||||
* @param __returnType The return type of the original implementation.
|
||||
* @param __originalIMP The original IMP.
|
||||
* @param __arg1 The first argument.
|
||||
* @param __arg2 The second argument.
|
||||
* @param __arg3 The third argument.
|
||||
* @param __arg4 The fourth argument.
|
||||
* @param __arg5 The fifth argument.
|
||||
* @param __arg6 The sixth argument.
|
||||
* @param __arg7 The seventh argument.
|
||||
* @param __arg8 The eighth argument.
|
||||
* @param __arg9 The ninth argument.
|
||||
*/
|
||||
#define GUL_INVOKE_ORIGINAL_IMP9(__receivingObject, __swizzledSEL, __returnType, __originalIMP, \
|
||||
__arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, __arg8, \
|
||||
__arg9) \
|
||||
((__returnType(*)(id, SEL, __typeof__(__arg1), __typeof__(__arg2), __typeof__(__arg3), \
|
||||
__typeof__(__arg4), __typeof__(__arg5), __typeof__(__arg6), \
|
||||
__typeof__(__arg7), __typeof__(__arg8), __typeof__(__arg9)))__originalIMP)( \
|
||||
__receivingObject, __swizzledSEL, __arg1, __arg2, __arg3, __arg4, __arg5, __arg6, __arg7, \
|
||||
__arg8, __arg9)
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
71
Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Public/GoogleUtilities/GULSwizzler.h
generated
Normal file
71
Pods/GoogleUtilities/GoogleUtilities/MethodSwizzler/Public/GoogleUtilities/GULSwizzler.h
generated
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
|
||||
/** This class handles the runtime manipulation necessary to instrument selectors. It stores the
|
||||
* classes and selectors that have been swizzled, and runs all operations on its own queue.
|
||||
*/
|
||||
@interface GULSwizzler : NSObject
|
||||
|
||||
/** Manipulates the Objective-C runtime to replace the original IMP with the supplied block.
|
||||
*
|
||||
* @param aClass The class to swizzle.
|
||||
* @param selector The selector of the class to swizzle.
|
||||
* @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
|
||||
* @param block The block that replaces the original IMP.
|
||||
*/
|
||||
+ (void)swizzleClass:(Class)aClass
|
||||
selector:(SEL)selector
|
||||
isClassSelector:(BOOL)isClassSelector
|
||||
withBlock:(nullable id)block;
|
||||
|
||||
/** Returns the current IMP for the given class and selector.
|
||||
*
|
||||
* @param aClass The class to use.
|
||||
* @param selector The selector to find the implementation of.
|
||||
* @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
|
||||
* @return The implementation of the selector in the runtime.
|
||||
*/
|
||||
+ (nullable IMP)currentImplementationForClass:(Class)aClass
|
||||
selector:(SEL)selector
|
||||
isClassSelector:(BOOL)isClassSelector;
|
||||
|
||||
/** Checks the runtime to see if a selector exists on a class. If a property is declared as
|
||||
* @dynamic, we have a reverse swizzling situation, where the implementation of a method exists
|
||||
* only in concrete subclasses, and NOT in the superclass. We can detect that situation using
|
||||
* this helper method. Similarly, we can detect situations where a class doesn't implement a
|
||||
* protocol method.
|
||||
*
|
||||
* @param selector The selector to check for.
|
||||
* @param aClass The class to check.
|
||||
* @param isClassSelector A BOOL specifying whether the selector is a class or instance selector.
|
||||
* @return YES if the method was found in this selector/class combination, NO otherwise.
|
||||
*/
|
||||
+ (BOOL)selector:(SEL)selector existsInClass:(Class)aClass isClassSelector:(BOOL)isClassSelector;
|
||||
|
||||
/** Returns a list of all Objective-C (and not primitive) ivars contained by the given object.
|
||||
*
|
||||
* @param object The object whose ivars will be iterated.
|
||||
* @return The list of ivar objects.
|
||||
*/
|
||||
+ (NSArray<id> *)ivarObjectsForObject:(id)object;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
207
Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.m
generated
Normal file
207
Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/GULNSData+zlib.m
generated
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2018 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/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h"
|
||||
|
||||
#import <zlib.h>
|
||||
|
||||
#define kChunkSize 1024
|
||||
#define Z_DEFAULT_COMPRESSION (-1)
|
||||
|
||||
NSString *const GULNSDataZlibErrorDomain = @"com.google.GULNSDataZlibErrorDomain";
|
||||
NSString *const GULNSDataZlibErrorKey = @"GULNSDataZlibErrorKey";
|
||||
NSString *const GULNSDataZlibRemainingBytesKey = @"GULNSDataZlibRemainingBytesKey";
|
||||
|
||||
@implementation NSData (GULGzip)
|
||||
|
||||
+ (nullable NSData *)gul_dataByInflatingGzippedData:(NSData *)data error:(NSError **)error {
|
||||
const void *bytes = [data bytes];
|
||||
NSUInteger length = [data length];
|
||||
if (!bytes || !length) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if defined(__LP64__) && __LP64__
|
||||
// Don't support > 32bit length for 64 bit, see note in header.
|
||||
if (length > UINT_MAX) {
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
z_stream strm;
|
||||
bzero(&strm, sizeof(z_stream));
|
||||
|
||||
// Setup the input.
|
||||
strm.avail_in = (unsigned int)length;
|
||||
strm.next_in = (unsigned char *)bytes;
|
||||
|
||||
int windowBits = 15; // 15 to enable any window size
|
||||
windowBits += 32; // and +32 to enable zlib or gzip header detection.
|
||||
|
||||
int retCode;
|
||||
if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
|
||||
forKey:GULNSDataZlibErrorKey];
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorInternal
|
||||
userInfo:userInfo];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Hint the size at 4x the input size.
|
||||
NSMutableData *result = [NSMutableData dataWithCapacity:(length * 4)];
|
||||
unsigned char output[kChunkSize];
|
||||
|
||||
// Loop to collect the data.
|
||||
do {
|
||||
// Update what we're passing in.
|
||||
strm.avail_out = kChunkSize;
|
||||
strm.next_out = output;
|
||||
retCode = inflate(&strm, Z_NO_FLUSH);
|
||||
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
|
||||
if (error) {
|
||||
NSMutableDictionary *userInfo =
|
||||
[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
|
||||
forKey:GULNSDataZlibErrorKey];
|
||||
if (strm.msg) {
|
||||
NSString *message = [NSString stringWithUTF8String:strm.msg];
|
||||
if (message) {
|
||||
[userInfo setObject:message forKey:NSLocalizedDescriptionKey];
|
||||
}
|
||||
}
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorInternal
|
||||
userInfo:userInfo];
|
||||
}
|
||||
inflateEnd(&strm);
|
||||
return nil;
|
||||
}
|
||||
// Collect what we got.
|
||||
unsigned gotBack = kChunkSize - strm.avail_out;
|
||||
if (gotBack > 0) {
|
||||
[result appendBytes:output length:gotBack];
|
||||
}
|
||||
|
||||
} while (retCode == Z_OK);
|
||||
|
||||
// Make sure there wasn't more data tacked onto the end of a valid compressed stream.
|
||||
if (strm.avail_in != 0) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo =
|
||||
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
|
||||
forKey:GULNSDataZlibRemainingBytesKey];
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorDataRemaining
|
||||
userInfo:userInfo];
|
||||
}
|
||||
result = nil;
|
||||
}
|
||||
// The only way out of the loop was by hitting the end of the stream.
|
||||
NSAssert(retCode == Z_STREAM_END,
|
||||
@"Thought we finished inflate w/o getting a result of stream end, code %d", retCode);
|
||||
|
||||
// Clean up.
|
||||
inflateEnd(&strm);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (nullable NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error {
|
||||
const void *bytes = [data bytes];
|
||||
NSUInteger length = [data length];
|
||||
|
||||
int level = Z_DEFAULT_COMPRESSION;
|
||||
if (!bytes || !length) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if defined(__LP64__) && __LP64__
|
||||
// Don't support > 32bit length for 64 bit, see note in header.
|
||||
if (length > UINT_MAX) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorGreaterThan32BitsToCompress
|
||||
userInfo:nil];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
z_stream strm;
|
||||
bzero(&strm, sizeof(z_stream));
|
||||
|
||||
int memLevel = 8; // Default.
|
||||
int windowBits = 15 + 16; // Enable gzip header instead of zlib header.
|
||||
|
||||
int retCode;
|
||||
if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits, memLevel,
|
||||
Z_DEFAULT_STRATEGY)) != Z_OK) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
|
||||
forKey:GULNSDataZlibErrorKey];
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorInternal
|
||||
userInfo:userInfo];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Hint the size at 1/4 the input size.
|
||||
NSMutableData *result = [NSMutableData dataWithCapacity:(length / 4)];
|
||||
unsigned char output[kChunkSize];
|
||||
|
||||
// Setup the input.
|
||||
strm.avail_in = (unsigned int)length;
|
||||
strm.next_in = (unsigned char *)bytes;
|
||||
|
||||
// Collect the data.
|
||||
do {
|
||||
// update what we're passing in
|
||||
strm.avail_out = kChunkSize;
|
||||
strm.next_out = output;
|
||||
retCode = deflate(&strm, Z_FINISH);
|
||||
if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
|
||||
forKey:GULNSDataZlibErrorKey];
|
||||
*error = [NSError errorWithDomain:GULNSDataZlibErrorDomain
|
||||
code:GULNSDataZlibErrorInternal
|
||||
userInfo:userInfo];
|
||||
}
|
||||
deflateEnd(&strm);
|
||||
return nil;
|
||||
}
|
||||
// Collect what we got.
|
||||
unsigned gotBack = kChunkSize - strm.avail_out;
|
||||
if (gotBack > 0) {
|
||||
[result appendBytes:output length:gotBack];
|
||||
}
|
||||
|
||||
} while (retCode == Z_OK);
|
||||
|
||||
// If the loop exits, it used all input and the stream ended.
|
||||
NSAssert(strm.avail_in == 0,
|
||||
@"Should have finished deflating without using all input, %u bytes left", strm.avail_in);
|
||||
NSAssert(retCode == Z_STREAM_END,
|
||||
@"thought we finished deflate w/o getting a result of stream end, code %d", retCode);
|
||||
|
||||
// Clean up.
|
||||
deflateEnd(&strm);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
53
Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h
generated
Normal file
53
Pods/GoogleUtilities/GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h
generated
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2018 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
|
||||
|
||||
/// 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
|
||||
// such data. To handle data of that size you really should be streaming it rather then doing it all
|
||||
// in memory.
|
||||
|
||||
@interface NSData (GULGzip)
|
||||
|
||||
/// Returns an data as the result of decompressing the payload of |data|.The data to decompress must
|
||||
/// be a gzipped payloads.
|
||||
+ (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.
|
||||
+ (nullable NSData *)gul_dataByGzippingData:(NSData *)data error:(NSError **)error;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorDomain;
|
||||
FOUNDATION_EXPORT NSString *const GULNSDataZlibErrorKey; // NSNumber
|
||||
FOUNDATION_EXPORT NSString *const GULNSDataZlibRemainingBytesKey; // NSNumber
|
||||
|
||||
typedef NS_ENUM(NSInteger, GULNSDataZlibError) {
|
||||
GULNSDataZlibErrorGreaterThan32BitsToCompress = 1024,
|
||||
// An internal zlib error.
|
||||
// GULNSDataZlibErrorKey will contain the error value.
|
||||
// NSLocalizedDescriptionKey may contain an error string from zlib.
|
||||
// Look in zlib.h for list of errors.
|
||||
GULNSDataZlibErrorInternal,
|
||||
// There was left over data in the buffer that was not used.
|
||||
// GULNSDataZlibRemainingBytesKey will contain number of remaining bytes.
|
||||
GULNSDataZlibErrorDataRemaining
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
101
Pods/GoogleUtilities/GoogleUtilities/Network/GULMutableDictionary.m
generated
Normal file
101
Pods/GoogleUtilities/GoogleUtilities/Network/GULMutableDictionary.m
generated
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2017 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/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
|
||||
@implementation GULMutableDictionary {
|
||||
/// The mutable dictionary.
|
||||
NSMutableDictionary *_objects;
|
||||
|
||||
/// Serial synchronization queue. All reads should use dispatch_sync, while writes use
|
||||
/// dispatch_async.
|
||||
dispatch_queue_t _queue;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_objects = [[NSMutableDictionary alloc] init];
|
||||
_queue = dispatch_queue_create("GULMutableDictionary", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
__block NSString *description;
|
||||
dispatch_sync(_queue, ^{
|
||||
description = self->_objects.description;
|
||||
});
|
||||
return description;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id)key {
|
||||
__block id object;
|
||||
dispatch_sync(_queue, ^{
|
||||
object = [self->_objects objectForKey:key];
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void)setObject:(id)object forKey:(id<NSCopying>)key {
|
||||
dispatch_async(_queue, ^{
|
||||
[self->_objects setObject:object forKey:key];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(id)key {
|
||||
dispatch_async(_queue, ^{
|
||||
[self->_objects removeObjectForKey:key];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllObjects {
|
||||
dispatch_async(_queue, ^{
|
||||
[self->_objects removeAllObjects];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSUInteger)count {
|
||||
__block NSUInteger count;
|
||||
dispatch_sync(_queue, ^{
|
||||
count = self->_objects.count;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
- (id)objectForKeyedSubscript:(id<NSCopying>)key {
|
||||
__block id object;
|
||||
dispatch_sync(_queue, ^{
|
||||
object = self->_objects[key];
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
|
||||
dispatch_async(_queue, ^{
|
||||
self->_objects[key] = obj;
|
||||
});
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionary {
|
||||
__block NSDictionary *dictionary;
|
||||
dispatch_sync(_queue, ^{
|
||||
dictionary = [self->_objects copy];
|
||||
});
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
406
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetwork.m
generated
Normal file
406
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetwork.m
generated
Normal file
@ -0,0 +1,406 @@
|
||||
// Copyright 2017 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/Network/Public/GoogleUtilities/GULNetwork.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h"
|
||||
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
#import "GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h"
|
||||
#import "GoogleUtilities/Network/GULNetworkInternal.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkConstants.h"
|
||||
#import "GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h"
|
||||
|
||||
/// Constant string for request header Content-Encoding.
|
||||
static NSString *const kGULNetworkContentCompressionKey = @"Content-Encoding";
|
||||
|
||||
/// Constant string for request header Content-Encoding value.
|
||||
static NSString *const kGULNetworkContentCompressionValue = @"gzip";
|
||||
|
||||
/// Constant string for request header Content-Length.
|
||||
static NSString *const kGULNetworkContentLengthKey = @"Content-Length";
|
||||
|
||||
/// Constant string for request header Content-Type.
|
||||
static NSString *const kGULNetworkContentTypeKey = @"Content-Type";
|
||||
|
||||
/// Constant string for request header Content-Type value.
|
||||
static NSString *const kGULNetworkContentTypeValue = @"application/x-www-form-urlencoded";
|
||||
|
||||
/// Constant string for GET request method.
|
||||
static NSString *const kGULNetworkGETRequestMethod = @"GET";
|
||||
|
||||
/// Constant string for POST request method.
|
||||
static NSString *const kGULNetworkPOSTRequestMethod = @"POST";
|
||||
|
||||
/// Default constant string as a prefix for network logger.
|
||||
static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
|
||||
|
||||
@interface GULNetwork () <GULReachabilityDelegate, GULNetworkLoggerDelegate>
|
||||
@end
|
||||
|
||||
@implementation GULNetwork {
|
||||
/// Network reachability.
|
||||
GULReachabilityChecker *_reachability;
|
||||
|
||||
/// The dictionary of requests by session IDs { NSString : id }.
|
||||
GULMutableDictionary *_requests;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithReachabilityHost:kGULNetworkReachabilityHost];
|
||||
}
|
||||
|
||||
- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Setup reachability.
|
||||
_reachability = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self
|
||||
withHost:reachabilityHost];
|
||||
if (![_reachability start]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_requests = [[GULMutableDictionary alloc] init];
|
||||
_timeoutInterval = kGULNetworkTimeOutInterval;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_reachability.reachabilityDelegate = nil;
|
||||
[_reachability stop];
|
||||
}
|
||||
|
||||
#pragma mark - External Methods
|
||||
|
||||
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||
completionHandler:(GULNetworkSystemCompletionHandler)completionHandler {
|
||||
[GULNetworkURLSession handleEventsForBackgroundURLSessionID:sessionID
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (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
|
||||
queue:queue
|
||||
usingBackgroundSession:usingBackgroundSession
|
||||
completionHandler: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;
|
||||
}
|
||||
|
||||
NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
|
||||
|
||||
NSMutableURLRequest *request =
|
||||
[[NSMutableURLRequest alloc] initWithURL:url
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:timeOutInterval];
|
||||
|
||||
if (!request) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
|
||||
queue:queue
|
||||
withHandler:handler];
|
||||
return nil;
|
||||
}
|
||||
request.allHTTPHeaderFields = headers;
|
||||
|
||||
NSError *compressError = nil;
|
||||
NSData *compressedData = [NSData gul_dataByGzippingData:payload error:&compressError];
|
||||
if (!compressedData || compressError) {
|
||||
if (compressError || payload.length > 0) {
|
||||
// If the payload is not empty but it fails to compress the payload, something has been wrong.
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkPayloadCompression
|
||||
queue:queue
|
||||
withHandler:handler];
|
||||
return nil;
|
||||
}
|
||||
compressedData = [[NSData alloc] init];
|
||||
}
|
||||
|
||||
NSString *postLength = @(compressedData.length).stringValue;
|
||||
|
||||
// Set up the request with the compressed data.
|
||||
[request setValue:postLength forHTTPHeaderField:kGULNetworkContentLengthKey];
|
||||
request.HTTPBody = compressedData;
|
||||
request.HTTPMethod = kGULNetworkPOSTRequestMethod;
|
||||
[request setValue:kGULNetworkContentTypeValue forHTTPHeaderField:kGULNetworkContentTypeKey];
|
||||
[request setValue:kGULNetworkContentCompressionValue
|
||||
forHTTPHeaderField:kGULNetworkContentCompressionKey];
|
||||
|
||||
GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
|
||||
fetcher.backgroundNetworkEnabled = usingBackgroundSession;
|
||||
|
||||
__weak GULNetwork *weakSelf = self;
|
||||
NSString *requestID = [fetcher
|
||||
sessionIDFromAsyncPOSTRequest:request
|
||||
completionHandler:^(NSHTTPURLResponse *response, NSData *data,
|
||||
NSString *sessionID, NSError *error) {
|
||||
GULNetwork *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||
dispatch_async(queueToDispatch, ^{
|
||||
if (sessionID.length) {
|
||||
[strongSelf->_requests removeObjectForKey:sessionID];
|
||||
}
|
||||
if (handler) {
|
||||
handler(response, data, error);
|
||||
}
|
||||
});
|
||||
}];
|
||||
if (!requestID) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
|
||||
queue:queue
|
||||
withHandler:handler];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeNetwork000
|
||||
message:@"Uploading data. Host"
|
||||
context:url];
|
||||
_requests[requestID] = fetcher;
|
||||
return requestID;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
|
||||
NSMutableURLRequest *request =
|
||||
[[NSMutableURLRequest alloc] initWithURL:url
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:timeOutInterval];
|
||||
|
||||
if (!request) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
|
||||
queue:queue
|
||||
withHandler:handler];
|
||||
return nil;
|
||||
}
|
||||
|
||||
request.HTTPMethod = kGULNetworkGETRequestMethod;
|
||||
request.allHTTPHeaderFields = headers;
|
||||
|
||||
GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
|
||||
fetcher.backgroundNetworkEnabled = usingBackgroundSession;
|
||||
|
||||
__weak GULNetwork *weakSelf = self;
|
||||
NSString *requestID = [fetcher
|
||||
sessionIDFromAsyncGETRequest:request
|
||||
completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSString *sessionID,
|
||||
NSError *error) {
|
||||
GULNetwork *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||
dispatch_async(queueToDispatch, ^{
|
||||
if (sessionID.length) {
|
||||
[strongSelf->_requests removeObjectForKey:sessionID];
|
||||
}
|
||||
if (handler) {
|
||||
handler(response, data, error);
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
if (!requestID) {
|
||||
[self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
|
||||
queue:queue
|
||||
withHandler:handler];
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeNetwork001
|
||||
message:@"Downloading data. Host"
|
||||
context:url];
|
||||
_requests[requestID] = fetcher;
|
||||
return requestID;
|
||||
}
|
||||
|
||||
- (BOOL)hasUploadInProgress {
|
||||
return _requests.count > 0;
|
||||
}
|
||||
|
||||
#pragma mark - Network Reachability
|
||||
|
||||
/// Tells reachability delegate to call reachabilityDidChangeToStatus: to notify the network
|
||||
/// reachability has changed.
|
||||
- (void)reachability:(GULReachabilityChecker *)reachability
|
||||
statusChanged:(GULReachabilityStatus)status {
|
||||
_networkConnected = (status == kGULReachabilityViaCellular || status == kGULReachabilityViaWifi);
|
||||
[_reachabilityDelegate reachabilityDidChange];
|
||||
}
|
||||
|
||||
#pragma mark - Network logger delegate
|
||||
|
||||
- (void)setLoggerDelegate:(id<GULNetworkLoggerDelegate>)loggerDelegate {
|
||||
// Explicitly check whether the delegate responds to the methods because conformsToProtocol does
|
||||
// not work correctly even though the delegate does respond to the methods.
|
||||
if (!loggerDelegate ||
|
||||
![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
|
||||
messageCode:message:contexts:)] ||
|
||||
![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
|
||||
messageCode:message:context:)] ||
|
||||
![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
|
||||
messageCode:message:)]) {
|
||||
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;
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
/// Handles network error and calls completion handler with the error.
|
||||
- (void)handleErrorWithCode:(NSInteger)code
|
||||
queue:(dispatch_queue_t)queue
|
||||
withHandler:(GULNetworkCompletionHandler)handler {
|
||||
NSDictionary *userInfo = @{kGULNetworkErrorContext : @"Failed to create network request"};
|
||||
NSError *error = [[NSError alloc] initWithDomain:kGULNetworkErrorDomain
|
||||
code:code
|
||||
userInfo:userInfo];
|
||||
[self GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
|
||||
messageCode:kGULNetworkMessageCodeNetwork002
|
||||
message:@"Failed to create network request. Code, error"
|
||||
contexts:@[ @(code), error ]];
|
||||
if (handler) {
|
||||
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||
dispatch_async(queueToDispatch, ^{
|
||||
handler(nil, nil, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Network logger
|
||||
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message
|
||||
contexts:(NSArray *)contexts {
|
||||
// Let the delegate log the message if there is a valid logger delegate. Otherwise, just log
|
||||
// errors/warnings/info messages to the console log.
|
||||
if (_loggerDelegate) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:logLevel
|
||||
messageCode:messageCode
|
||||
message:message
|
||||
contexts:contexts];
|
||||
return;
|
||||
}
|
||||
if (_isDebugModeEnabled || logLevel == kGULNetworkLogLevelError ||
|
||||
logLevel == kGULNetworkLogLevelWarning || logLevel == kGULNetworkLogLevelInfo) {
|
||||
NSString *formattedMessage = GULStringWithLogMessage(message, logLevel, contexts);
|
||||
NSLog(@"%@", formattedMessage);
|
||||
GULOSLogBasic((GULLoggerLevel)logLevel, kGULLogSubsystem, kGULLoggerNetwork, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message
|
||||
context:(id)context {
|
||||
if (_loggerDelegate) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:logLevel
|
||||
messageCode:messageCode
|
||||
message:message
|
||||
context:context];
|
||||
return;
|
||||
}
|
||||
NSArray *contexts = context ? @[ context ] : @[];
|
||||
[self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:contexts];
|
||||
}
|
||||
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message {
|
||||
if (_loggerDelegate) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message];
|
||||
return;
|
||||
}
|
||||
[self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:@[]];
|
||||
}
|
||||
|
||||
/// Returns a string for the given log level (e.g. kGULNetworkLogLevelError -> @"ERROR").
|
||||
static NSString *GULLogLevelDescriptionFromLogLevel(GULNetworkLogLevel logLevel) {
|
||||
static NSDictionary *levelNames = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
levelNames = @{
|
||||
@(kGULNetworkLogLevelError) : @"ERROR",
|
||||
@(kGULNetworkLogLevelWarning) : @"WARNING",
|
||||
@(kGULNetworkLogLevelInfo) : @"INFO",
|
||||
@(kGULNetworkLogLevelDebug) : @"DEBUG"
|
||||
};
|
||||
});
|
||||
return levelNames[@(logLevel)];
|
||||
}
|
||||
|
||||
/// Returns a formatted string to be used for console logging.
|
||||
static NSString *GULStringWithLogMessage(NSString *message,
|
||||
GULNetworkLogLevel logLevel,
|
||||
NSArray *contexts) {
|
||||
if (!message) {
|
||||
message = @"(Message was nil)";
|
||||
} else if (!message.length) {
|
||||
message = @"(Message was empty)";
|
||||
}
|
||||
NSMutableString *result = [[NSMutableString alloc]
|
||||
initWithFormat:@"<%@/%@> %@", kGULNetworkLogTag, GULLogLevelDescriptionFromLogLevel(logLevel),
|
||||
message];
|
||||
|
||||
if (!contexts.count) {
|
||||
return result;
|
||||
}
|
||||
|
||||
NSMutableArray *formattedContexts = [[NSMutableArray alloc] init];
|
||||
for (id item in contexts) {
|
||||
[formattedContexts addObject:(item != [NSNull null] ? item : @"(nil)")];
|
||||
}
|
||||
|
||||
[result appendString:@": "];
|
||||
[result appendString:[formattedContexts componentsJoinedByString:@", "]];
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
41
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkConstants.m
generated
Normal file
41
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkConstants.m
generated
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2017 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/Network/Public/GoogleUtilities/GULNetworkConstants.h"
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NSString *const kGULNetworkBackgroundSessionConfigIDPrefix = @"com.gul.network.background-upload";
|
||||
NSString *const kGULNetworkApplicationSupportSubdirectory = @"GUL/Network";
|
||||
NSString *const kGULNetworkTempDirectoryName = @"GULNetworkTemporaryDirectory";
|
||||
const NSTimeInterval kGULNetworkTempFolderExpireTime = 60 * 60; // 1 hour
|
||||
const NSTimeInterval kGULNetworkTimeOutInterval = 60; // 1 minute.
|
||||
NSString *const kGULNetworkReachabilityHost = @"app-measurement.com";
|
||||
NSString *const kGULNetworkErrorContext = @"Context";
|
||||
|
||||
const int kGULNetworkHTTPStatusOK = 200;
|
||||
const int kGULNetworkHTTPStatusNoContent = 204;
|
||||
const int kGULNetworkHTTPStatusCodeMultipleChoices = 300;
|
||||
const int kGULNetworkHTTPStatusCodeMovedPermanently = 301;
|
||||
const int kGULNetworkHTTPStatusCodeFound = 302;
|
||||
const int kGULNetworkHTTPStatusCodeNotModified = 304;
|
||||
const int kGULNetworkHTTPStatusCodeMovedTemporarily = 307;
|
||||
const int kGULNetworkHTTPStatusCodeNotFound = 404;
|
||||
const int kGULNetworkHTTPStatusCodeCannotAcceptTraffic = 429;
|
||||
const int kGULNetworkHTTPStatusCodeUnavailable = 503;
|
||||
|
||||
NSString *const kGULNetworkErrorDomain = @"com.gul.network.ErrorDomain";
|
||||
|
||||
GULLoggerService kGULLoggerNetwork = @"[GULNetwork]";
|
||||
24
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkInternal.h
generated
Normal file
24
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkInternal.h
generated
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2017 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 "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
extern NSString *const kGULNetworkErrorDomain;
|
||||
|
||||
/// The logger service for GULNetwork.
|
||||
extern GULLoggerService kGULLoggerNetwork;
|
||||
729
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkURLSession.m
generated
Normal file
729
Pods/GoogleUtilities/GoogleUtilities/Network/GULNetworkURLSession.m
generated
Normal file
@ -0,0 +1,729 @@
|
||||
// Copyright 2017 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 "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkURLSession.h"
|
||||
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
#import "GoogleUtilities/Network/GULNetworkInternal.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkConstants.h"
|
||||
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h"
|
||||
|
||||
@interface GULNetworkURLSession () <NSURLSessionDelegate,
|
||||
NSURLSessionDataDelegate,
|
||||
NSURLSessionDownloadDelegate,
|
||||
NSURLSessionTaskDelegate>
|
||||
@end
|
||||
|
||||
@implementation GULNetworkURLSession {
|
||||
/// The handler to be called when the request completes or error has occurs.
|
||||
GULNetworkURLSessionCompletionHandler _completionHandler;
|
||||
|
||||
/// Session ID generated randomly with a fixed prefix.
|
||||
NSString *_sessionID;
|
||||
|
||||
/// The session configuration.
|
||||
NSURLSessionConfiguration *_sessionConfig;
|
||||
|
||||
/// The current NSURLSession.
|
||||
NSURLSession *__weak _Nullable _URLSession;
|
||||
|
||||
/// The path to the directory where all temporary files are stored before uploading.
|
||||
NSURL *_networkDirectoryURL;
|
||||
|
||||
/// The downloaded data from fetching.
|
||||
NSData *_downloadedData;
|
||||
|
||||
/// The path to the temporary file which stores the uploading data.
|
||||
NSURL *_uploadingFileURL;
|
||||
|
||||
/// The current request.
|
||||
NSURLRequest *_request;
|
||||
}
|
||||
|
||||
#pragma mark - Init
|
||||
|
||||
- (instancetype)initWithNetworkLoggerDelegate:(id<GULNetworkLoggerDelegate>)networkLoggerDelegate {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Create URL to the directory where all temporary files to upload have to be stored.
|
||||
#if TARGET_OS_TV
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
#else
|
||||
NSArray *paths =
|
||||
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
#endif
|
||||
NSString *storageDirectory = paths.firstObject;
|
||||
NSArray *tempPathComponents = @[
|
||||
storageDirectory, kGULNetworkApplicationSupportSubdirectory, kGULNetworkTempDirectoryName
|
||||
];
|
||||
_networkDirectoryURL = [NSURL fileURLWithPathComponents:tempPathComponents];
|
||||
_sessionID = [NSString stringWithFormat:@"%@-%@", kGULNetworkBackgroundSessionConfigIDPrefix,
|
||||
[[NSUUID UUID] UUIDString]];
|
||||
_loggerDelegate = networkLoggerDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - External Methods
|
||||
|
||||
#pragma mark - To be called from AppDelegate
|
||||
|
||||
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||
completionHandler:
|
||||
(GULNetworkSystemCompletionHandler)systemCompletionHandler {
|
||||
// The session may not be Analytics background. Ignore those that do not have the prefix.
|
||||
if (![sessionID hasPrefix:kGULNetworkBackgroundSessionConfigIDPrefix]) {
|
||||
return;
|
||||
}
|
||||
GULNetworkURLSession *fetcher = [self fetcherWithSessionIdentifier:sessionID];
|
||||
if (fetcher != nil) {
|
||||
[fetcher addSystemCompletionHandler:systemCompletionHandler forSession:sessionID];
|
||||
} else {
|
||||
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`, and returns an ID of the connection.
|
||||
- (nullable NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
|
||||
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.
|
||||
_uploadingFileURL = [self temporaryFilePathWithSessionID:_sessionID];
|
||||
NSError *writeError;
|
||||
NSURLSessionUploadTask *postRequestTask;
|
||||
NSURLSession *session;
|
||||
BOOL didWriteFile = NO;
|
||||
|
||||
// Clean up the entire temp folder to avoid temp files that remain in case the previous session
|
||||
// crashed and did not clean up.
|
||||
[self maybeRemoveTempFilesAtURL:_networkDirectoryURL
|
||||
expiringTime:kGULNetworkTempFolderExpireTime];
|
||||
|
||||
// If there is no background network enabled, no need to write to file. This will allow default
|
||||
// network session which runs on the foreground.
|
||||
if (_backgroundNetworkEnabled && [self ensureTemporaryDirectoryExists]) {
|
||||
didWriteFile = [request.HTTPBody writeToFile:_uploadingFileURL.path
|
||||
options:NSDataWritingAtomic
|
||||
error:&writeError];
|
||||
|
||||
if (writeError) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession000
|
||||
message:@"Failed to write request data to file"
|
||||
context:writeError];
|
||||
}
|
||||
}
|
||||
|
||||
if (didWriteFile) {
|
||||
// Exclude this file from backing up to iTunes. There are conflicting reports that excluding
|
||||
// directory from backing up does not exclude files of that directory from backing up.
|
||||
[self excludeFromBackupForURL:_uploadingFileURL];
|
||||
|
||||
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||
} 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;
|
||||
|
||||
if (didWriteFile) {
|
||||
postRequestTask = [session uploadTaskWithRequest:requestWithoutHTTPBody
|
||||
fromFile:_uploadingFileURL];
|
||||
} else {
|
||||
postRequestTask = [session uploadTaskWithRequest:requestWithoutHTTPBody
|
||||
fromData:givenRequestHTTPBody];
|
||||
}
|
||||
|
||||
if (!session || !postRequestTask) {
|
||||
NSError *error = [[NSError alloc]
|
||||
initWithDomain:kGULNetworkErrorDomain
|
||||
code:GULErrorCodeNetworkRequestCreation
|
||||
userInfo:@{kGULNetworkErrorContext : @"Cannot create network session"}];
|
||||
[self callCompletionHandler:handler withResponse:nil data:nil error:error];
|
||||
return nil;
|
||||
}
|
||||
|
||||
_URLSession = session;
|
||||
|
||||
// Save the session into memory.
|
||||
[[self class] setSessionInFetcherMap:self forSessionID:_sessionID];
|
||||
|
||||
_request = [request copy];
|
||||
|
||||
// Store completion handler because background session does not accept handler block but custom
|
||||
// delegate.
|
||||
_completionHandler = [handler copy];
|
||||
[postRequestTask resume];
|
||||
|
||||
return _sessionID;
|
||||
}
|
||||
|
||||
/// Sends an async GET request using `NSURLSession`, and returns an ID of the session.
|
||||
- (nullable NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler {
|
||||
if (_backgroundNetworkEnabled) {
|
||||
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||
} else {
|
||||
_sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
}
|
||||
|
||||
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||
|
||||
// Do not cache the GET request.
|
||||
_sessionConfig.URLCache = nil;
|
||||
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:[NSOperationQueue mainQueue]];
|
||||
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
|
||||
|
||||
if (!session || !downloadTask) {
|
||||
NSError *error = [[NSError alloc]
|
||||
initWithDomain:kGULNetworkErrorDomain
|
||||
code:GULErrorCodeNetworkRequestCreation
|
||||
userInfo:@{kGULNetworkErrorContext : @"Cannot create network session"}];
|
||||
[self callCompletionHandler:handler withResponse:nil data:nil error:error];
|
||||
return nil;
|
||||
}
|
||||
|
||||
_URLSession = session;
|
||||
|
||||
// Save the session into memory.
|
||||
[[self class] setSessionInFetcherMap:self forSessionID:_sessionID];
|
||||
|
||||
_request = [request copy];
|
||||
|
||||
_completionHandler = [handler copy];
|
||||
[downloadTask resume];
|
||||
|
||||
return _sessionID;
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDataDelegate
|
||||
|
||||
/// Called by the NSURLSession when the data task has received some of the expected data.
|
||||
/// Once the session is completed, URLSession:task:didCompleteWithError will be called and the
|
||||
/// completion handler will be called with the downloaded data.
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveData:(NSData *)data {
|
||||
@synchronized(self) {
|
||||
NSMutableData *mutableData = [[NSMutableData alloc] init];
|
||||
if (_downloadedData) {
|
||||
mutableData = _downloadedData.mutableCopy;
|
||||
}
|
||||
[mutableData appendData:data];
|
||||
_downloadedData = mutableData;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionTaskDelegate
|
||||
|
||||
/// Called by the NSURLSession once the download task is completed. The file is saved in the
|
||||
/// provided URL so we need to read the data and store into _downloadedData. Once the session is
|
||||
/// completed, URLSession:task:didCompleteWithError will be called and the completion handler will
|
||||
/// be called with the downloaded data.
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
downloadTask:(NSURLSessionDownloadTask *)task
|
||||
didFinishDownloadingToURL:(NSURL *)url {
|
||||
if (!url.path) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession001
|
||||
message:@"Unable to read downloaded data from empty temp path"];
|
||||
_downloadedData = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
_downloadedData = [NSData dataWithContentsOfFile:url.path options:0 error:&error];
|
||||
|
||||
if (error) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession002
|
||||
message:@"Cannot read the content of downloaded data"
|
||||
context:error];
|
||||
_downloadedData = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession003
|
||||
message:@"Background session finished"
|
||||
context:session.configuration.identifier];
|
||||
[self callSystemCompletionHandler:session.configuration.identifier];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didCompleteWithError:(NSError *)error {
|
||||
// Avoid any chance of recursive behavior leading to it being used repeatedly.
|
||||
GULNetworkURLSessionCompletionHandler handler = _completionHandler;
|
||||
_completionHandler = nil;
|
||||
|
||||
if (task.response) {
|
||||
// The following assertion should always be true for HTTP requests, see https://goo.gl/gVLxT7.
|
||||
NSAssert([task.response isKindOfClass:[NSHTTPURLResponse class]], @"URL response must be HTTP");
|
||||
|
||||
// The server responded so ignore the error created by the system.
|
||||
error = nil;
|
||||
} else if (!error) {
|
||||
error = [[NSError alloc]
|
||||
initWithDomain:kGULNetworkErrorDomain
|
||||
code:GULErrorCodeNetworkInvalidResponse
|
||||
userInfo:@{kGULNetworkErrorContext : @"Network Error: Empty network response"}];
|
||||
}
|
||||
|
||||
[self callCompletionHandler:handler
|
||||
withResponse:(NSHTTPURLResponse *)task.response
|
||||
data:_downloadedData
|
||||
error:error];
|
||||
|
||||
// Remove the temp file to avoid trashing devices with lots of temp files.
|
||||
[self removeTempItemAtURL:_uploadingFileURL];
|
||||
|
||||
// Try to clean up stale files again.
|
||||
[self maybeRemoveTempFilesAtURL:_networkDirectoryURL
|
||||
expiringTime:kGULNetworkTempFolderExpireTime];
|
||||
|
||||
// This is called without checking the sessionID here since non-background sessions
|
||||
// won't have an ID.
|
||||
[session finishTasksAndInvalidate];
|
||||
|
||||
// Explicitly remove the session so it won't be reused. The weak map table should
|
||||
// remove the session on deallocation, but dealloc may not happen immediately after
|
||||
// calling `finishTasksAndInvalidate`.
|
||||
NSString *sessionID = session.configuration.identifier;
|
||||
[[self class] setSessionInFetcherMap:nil forSessionID:sessionID];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *credential))completionHandler {
|
||||
// The handling is modeled after GTMSessionFetcher.
|
||||
if ([challenge.protectionSpace.authenticationMethod
|
||||
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
||||
if (serverTrust == NULL) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession004
|
||||
message:@"Received empty server trust for host. Host"
|
||||
context:_request.URL];
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
return;
|
||||
}
|
||||
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
|
||||
if (!credential) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
|
||||
messageCode:kGULNetworkMessageCodeURLSession005
|
||||
message:@"Unable to verify server identity. Host"
|
||||
context:_request.URL];
|
||||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession006
|
||||
message:@"Received SSL challenge for host. Host"
|
||||
context:_request.URL];
|
||||
|
||||
void (^callback)(BOOL) = ^(BOOL allow) {
|
||||
if (allow) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
||||
} else {
|
||||
[self->_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession007
|
||||
message:@"Cancelling authentication challenge for host. Host"
|
||||
context:self->_request.URL];
|
||||
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
|
||||
}
|
||||
};
|
||||
|
||||
// Retain the trust object to avoid a SecTrustEvaluate() crash on iOS 7.
|
||||
CFRetain(serverTrust);
|
||||
|
||||
// Evaluate the certificate chain.
|
||||
//
|
||||
// The delegate queue may be the main thread. Trust evaluation could cause some
|
||||
// blocking network activity, so we must evaluate async, as documented at
|
||||
// https://developer.apple.com/library/ios/technotes/tn2232/
|
||||
dispatch_queue_t evaluateBackgroundQueue =
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
dispatch_async(evaluateBackgroundQueue, ^{
|
||||
BOOL shouldAllow;
|
||||
CFErrorRef errorRef = NULL;
|
||||
|
||||
@synchronized([GULNetworkURLSession class]) {
|
||||
shouldAllow = SecTrustEvaluateWithError(serverTrust, &errorRef);
|
||||
}
|
||||
|
||||
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.
|
||||
callback(shouldAllow);
|
||||
|
||||
CFRelease(serverTrust);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Default handling for other Auth Challenges.
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
|
||||
#pragma mark - Internal Methods
|
||||
|
||||
/// Stores system completion handler with session ID as key.
|
||||
- (void)addSystemCompletionHandler:(GULNetworkSystemCompletionHandler)handler
|
||||
forSession:(NSString *)identifier {
|
||||
if (!handler) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession009
|
||||
message:@"Cannot store nil system completion handler in network"];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!identifier.length) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession010
|
||||
message:@"Cannot store system completion handler with empty network "
|
||||
"session identifier"];
|
||||
return;
|
||||
}
|
||||
|
||||
GULMutableDictionary *systemCompletionHandlers =
|
||||
[[self class] sessionIDToSystemCompletionHandlerDictionary];
|
||||
if (systemCompletionHandlers[identifier]) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
|
||||
messageCode:kGULNetworkMessageCodeURLSession011
|
||||
message:@"Got multiple system handlers for a single session ID"
|
||||
context:identifier];
|
||||
}
|
||||
|
||||
systemCompletionHandlers[identifier] = handler;
|
||||
}
|
||||
|
||||
/// Calls the system provided completion handler with the session ID stored in the dictionary.
|
||||
/// The handler will be removed from the dictionary after being called.
|
||||
- (void)callSystemCompletionHandler:(NSString *)identifier {
|
||||
GULMutableDictionary *systemCompletionHandlers =
|
||||
[[self class] sessionIDToSystemCompletionHandlerDictionary];
|
||||
GULNetworkSystemCompletionHandler handler = [systemCompletionHandlers objectForKey:identifier];
|
||||
|
||||
if (handler) {
|
||||
[systemCompletionHandlers removeObjectForKey:identifier];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handler();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets or updates the session ID of this session.
|
||||
- (void)setSessionID:(NSString *)sessionID {
|
||||
_sessionID = [sessionID copy];
|
||||
}
|
||||
|
||||
/// Creates a background session configuration with the session ID using the supported method.
|
||||
- (NSURLSessionConfiguration *)backgroundSessionConfigWithSessionID:(NSString *)sessionID {
|
||||
return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionID];
|
||||
}
|
||||
|
||||
- (void)maybeRemoveTempFilesAtURL:(NSURL *)folderURL expiringTime:(NSTimeInterval)staleTime {
|
||||
if (!folderURL.absoluteString.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
NSArray *properties = @[ NSURLCreationDateKey ];
|
||||
NSArray *directoryContent =
|
||||
[fileManager contentsOfDirectoryAtURL:folderURL
|
||||
includingPropertiesForKeys:properties
|
||||
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
|
||||
error:&error];
|
||||
if (error && error.code != NSFileReadNoSuchFileError) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
|
||||
messageCode:kGULNetworkMessageCodeURLSession012
|
||||
message:@"Cannot get files from the temporary network folder. Error"
|
||||
context:error];
|
||||
return;
|
||||
}
|
||||
|
||||
if (!directoryContent.count) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSTimeInterval now = [NSDate date].timeIntervalSince1970;
|
||||
for (NSURL *tempFile in directoryContent) {
|
||||
NSDate *creationDate;
|
||||
BOOL getCreationDate = [tempFile getResourceValue:&creationDate
|
||||
forKey:NSURLCreationDateKey
|
||||
error:NULL];
|
||||
if (!getCreationDate) {
|
||||
continue;
|
||||
}
|
||||
NSTimeInterval creationTimeInterval = creationDate.timeIntervalSince1970;
|
||||
if (fabs(now - creationTimeInterval) > staleTime) {
|
||||
[self removeTempItemAtURL:tempFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the temporary file written to disk for sending the request. It has to be cleaned up
|
||||
/// after the session is done.
|
||||
- (void)removeTempItemAtURL:(NSURL *)fileURL {
|
||||
if (!fileURL.absoluteString.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
if (![fileManager removeItemAtURL:fileURL error:&error] && error.code != NSFileNoSuchFileError) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession013
|
||||
message:@"Failed to remove temporary uploading data file. Error"
|
||||
context:error.localizedDescription];
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the fetcher with the session ID.
|
||||
+ (instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier {
|
||||
GULNetworkURLSession *session = [self sessionFromFetcherMapForSessionID:sessionIdentifier];
|
||||
if (!session && [sessionIdentifier hasPrefix:kGULNetworkBackgroundSessionConfigIDPrefix]) {
|
||||
session = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:nil];
|
||||
[session setSessionID:sessionIdentifier];
|
||||
[self setSessionInFetcherMap:session forSessionID:sessionIdentifier];
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/// Returns a map of the fetcher by session ID. Creates a map if it is not created.
|
||||
/// When reading and writing from/to the session map, don't use this method directly.
|
||||
/// To avoid thread safety issues, use one of the helper methods at the bottom of the
|
||||
/// file: setSessionInFetcherMap:forSessionID:, sessionFromFetcherMapForSessionID:
|
||||
+ (NSMapTable<NSString *, GULNetworkURLSession *> *)sessionIDToFetcherMap {
|
||||
static NSMapTable *sessionIDToFetcherMap;
|
||||
|
||||
static dispatch_once_t sessionMapOnceToken;
|
||||
dispatch_once(&sessionMapOnceToken, ^{
|
||||
sessionIDToFetcherMap = [NSMapTable strongToWeakObjectsMapTable];
|
||||
});
|
||||
return sessionIDToFetcherMap;
|
||||
}
|
||||
|
||||
+ (NSLock *)sessionIDToFetcherMapReadWriteLock {
|
||||
static NSLock *lock;
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
lock = [[NSLock alloc] init];
|
||||
});
|
||||
return lock;
|
||||
}
|
||||
|
||||
/// Returns a map of system provided completion handler by session ID. Creates a map if it is not
|
||||
/// created.
|
||||
+ (GULMutableDictionary *)sessionIDToSystemCompletionHandlerDictionary {
|
||||
static GULMutableDictionary *systemCompletionHandlers;
|
||||
|
||||
static dispatch_once_t systemCompletionHandlerOnceToken;
|
||||
dispatch_once(&systemCompletionHandlerOnceToken, ^{
|
||||
systemCompletionHandlers = [[GULMutableDictionary alloc] init];
|
||||
});
|
||||
return systemCompletionHandlers;
|
||||
}
|
||||
|
||||
- (NSURL *)temporaryFilePathWithSessionID:(NSString *)sessionID {
|
||||
NSString *tempName = [NSString stringWithFormat:@"GULUpload_temp_%@", sessionID];
|
||||
return [_networkDirectoryURL URLByAppendingPathComponent:tempName];
|
||||
}
|
||||
|
||||
/// Makes sure that the directory to store temp files exists. If not, tries to create it and returns
|
||||
/// YES. If there is anything wrong, returns NO.
|
||||
- (BOOL)ensureTemporaryDirectoryExists {
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSError *error = nil;
|
||||
|
||||
// Create a temporary directory if it does not exist or was deleted.
|
||||
if ([_networkDirectoryURL checkResourceIsReachableAndReturnError:&error]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (error && error.code != NSFileReadNoSuchFileError) {
|
||||
[_loggerDelegate
|
||||
GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
|
||||
messageCode:kGULNetworkMessageCodeURLSession014
|
||||
message:@"Error while trying to access Network temp folder. Error"
|
||||
context:error];
|
||||
}
|
||||
|
||||
NSError *writeError = nil;
|
||||
|
||||
[fileManager createDirectoryAtURL:_networkDirectoryURL
|
||||
withIntermediateDirectories:YES
|
||||
attributes:nil
|
||||
error:&writeError];
|
||||
if (writeError) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession015
|
||||
message:@"Cannot create temporary directory. Error"
|
||||
context:writeError];
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Set the iCloud exclusion attribute on the Documents URL.
|
||||
[self excludeFromBackupForURL:_networkDirectoryURL];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)excludeFromBackupForURL:(NSURL *)url {
|
||||
if (!url.path) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the iCloud exclusion attribute on the Documents URL.
|
||||
NSError *preventBackupError = nil;
|
||||
[url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&preventBackupError];
|
||||
if (preventBackupError) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession016
|
||||
message:@"Cannot exclude temporary folder from iTunes backup"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||
newRequest:(NSURLRequest *)request
|
||||
completionHandler:(void (^)(NSURLRequest *))completionHandler {
|
||||
NSArray *nonAllowedRedirectionCodes = @[
|
||||
@(kGULNetworkHTTPStatusCodeFound), @(kGULNetworkHTTPStatusCodeMovedPermanently),
|
||||
@(kGULNetworkHTTPStatusCodeMovedTemporarily), @(kGULNetworkHTTPStatusCodeMultipleChoices)
|
||||
];
|
||||
|
||||
// Allow those not in the non allowed list to be followed.
|
||||
if (![nonAllowedRedirectionCodes containsObject:@(response.statusCode)]) {
|
||||
completionHandler(request);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not allow redirection if the response code is in the non-allowed list.
|
||||
NSURLRequest *newRequest = request;
|
||||
|
||||
if (response) {
|
||||
newRequest = nil;
|
||||
}
|
||||
|
||||
completionHandler(newRequest);
|
||||
}
|
||||
|
||||
#pragma mark - Helper Methods
|
||||
|
||||
+ (void)setSessionInFetcherMap:(GULNetworkURLSession *)session forSessionID:(NSString *)sessionID {
|
||||
[[self sessionIDToFetcherMapReadWriteLock] lock];
|
||||
GULNetworkURLSession *existingSession =
|
||||
[[[self class] sessionIDToFetcherMap] objectForKey:sessionID];
|
||||
if (existingSession) {
|
||||
if (session) {
|
||||
NSString *message = [NSString stringWithFormat:@"Discarding session: %@", existingSession];
|
||||
[existingSession->_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelInfo
|
||||
messageCode:kGULNetworkMessageCodeURLSession019
|
||||
message:message];
|
||||
}
|
||||
[existingSession->_URLSession finishTasksAndInvalidate];
|
||||
}
|
||||
if (session) {
|
||||
[[[self class] sessionIDToFetcherMap] setObject:session forKey:sessionID];
|
||||
} else {
|
||||
[[[self class] sessionIDToFetcherMap] removeObjectForKey:sessionID];
|
||||
}
|
||||
[[self sessionIDToFetcherMapReadWriteLock] unlock];
|
||||
}
|
||||
|
||||
+ (nullable GULNetworkURLSession *)sessionFromFetcherMapForSessionID:(NSString *)sessionID {
|
||||
[[self sessionIDToFetcherMapReadWriteLock] lock];
|
||||
GULNetworkURLSession *session = [[[self class] sessionIDToFetcherMap] objectForKey:sessionID];
|
||||
[[self sessionIDToFetcherMapReadWriteLock] unlock];
|
||||
return session;
|
||||
}
|
||||
|
||||
- (void)callCompletionHandler:(GULNetworkURLSessionCompletionHandler)handler
|
||||
withResponse:(NSHTTPURLResponse *)response
|
||||
data:(NSData *)data
|
||||
error:(NSError *)error {
|
||||
if (error) {
|
||||
[_loggerDelegate GULNetwork_logWithLevel:kGULNetworkLogLevelError
|
||||
messageCode:kGULNetworkMessageCodeURLSession017
|
||||
message:@"Encounter network error. Code, error"
|
||||
contexts:@[ @(error.code), error ]];
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handler(response, data, self->_sessionID, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Always use the request parameters even if the default session configuration is more restrictive.
|
||||
- (void)populateSessionConfig:(NSURLSessionConfiguration *)sessionConfig
|
||||
withRequest:(NSURLRequest *)request {
|
||||
sessionConfig.HTTPAdditionalHeaders = request.allHTTPHeaderFields;
|
||||
sessionConfig.timeoutIntervalForRequest = request.timeoutInterval;
|
||||
sessionConfig.timeoutIntervalForResource = request.timeoutInterval;
|
||||
sessionConfig.requestCachePolicy = request.cachePolicy;
|
||||
}
|
||||
|
||||
@end
|
||||
50
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h
generated
Normal file
50
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h
generated
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
|
||||
/// A mutable dictionary that provides atomic accessor and mutators.
|
||||
@interface GULMutableDictionary : NSObject
|
||||
|
||||
/// Returns an object given a key in the dictionary or nil if not found.
|
||||
- (id)objectForKey:(id)key;
|
||||
|
||||
/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
|
||||
- (void)setObject:(id)object forKey:(id<NSCopying>)key;
|
||||
|
||||
/// Removes the object given its session ID from the dictionary.
|
||||
- (void)removeObjectForKey:(id)key;
|
||||
|
||||
/// Removes all objects.
|
||||
- (void)removeAllObjects;
|
||||
|
||||
/// Returns the number of current objects in the dictionary.
|
||||
- (NSUInteger)count;
|
||||
|
||||
/// Returns an object given a key in the dictionary or nil if not found.
|
||||
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
|
||||
|
||||
/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
|
||||
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key;
|
||||
|
||||
/// Returns the immutable dictionary.
|
||||
- (NSDictionary *)dictionary;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
101
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetwork.h
generated
Normal file
101
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetwork.h
generated
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2017 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 "GULNetworkConstants.h"
|
||||
#import "GULNetworkLoggerProtocol.h"
|
||||
#import "GULNetworkURLSession.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Delegate protocol for GULNetwork events.
|
||||
@protocol GULNetworkReachabilityDelegate
|
||||
|
||||
/// Tells the delegate to handle events when the network reachability changes to connected or not
|
||||
/// connected.
|
||||
- (void)reachabilityDidChange;
|
||||
|
||||
@end
|
||||
|
||||
/// The Network component that provides network status and handles network requests and responses.
|
||||
/// This is not thread safe.
|
||||
///
|
||||
/// NOTE:
|
||||
/// User must add FIRAnalytics handleEventsForBackgroundURLSessionID:completionHandler to the
|
||||
/// AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:
|
||||
@interface GULNetwork : NSObject
|
||||
|
||||
/// Indicates if network connectivity is available.
|
||||
@property(nonatomic, readonly, getter=isNetworkConnected) BOOL networkConnected;
|
||||
|
||||
/// Indicates if there are any uploads in progress.
|
||||
@property(nonatomic, readonly, getter=hasUploadInProgress) BOOL uploadInProgress;
|
||||
|
||||
/// An optional delegate that can be used in the event when network reachability changes.
|
||||
@property(nonatomic, weak) id<GULNetworkReachabilityDelegate> reachabilityDelegate;
|
||||
|
||||
/// An optional delegate that can be used to log messages, warnings or errors that occur in the
|
||||
/// network operations.
|
||||
@property(nonatomic, weak) id<GULNetworkLoggerDelegate> loggerDelegate;
|
||||
|
||||
/// Indicates whether the logger should display debug messages.
|
||||
@property(nonatomic, assign) BOOL isDebugModeEnabled;
|
||||
|
||||
/// The time interval in seconds for the network request to timeout.
|
||||
@property(nonatomic, assign) NSTimeInterval timeoutInterval;
|
||||
|
||||
/// Initializes with the default reachability host.
|
||||
- (instancetype)init;
|
||||
|
||||
/// Initializes with a custom reachability host.
|
||||
- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost;
|
||||
|
||||
/// Handles events when background session with the given ID has finished.
|
||||
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||
completionHandler:(GULNetworkSystemCompletionHandler)completionHandler;
|
||||
|
||||
/// 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.
|
||||
- (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.
|
||||
- (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.
|
||||
- (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
|
||||
75
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkConstants.h
generated
Normal file
75
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkConstants.h
generated
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
|
||||
/// 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.
|
||||
typedef NS_ENUM(NSInteger, GULNetworkErrorCode) {
|
||||
/// Unknown error.
|
||||
GULNetworkErrorCodeUnknown = 0,
|
||||
/// Error occurs when the request URL is invalid.
|
||||
GULErrorCodeNetworkInvalidURL = 1,
|
||||
/// Error occurs when request cannot be constructed.
|
||||
GULErrorCodeNetworkRequestCreation = 2,
|
||||
/// Error occurs when payload cannot be compressed.
|
||||
GULErrorCodeNetworkPayloadCompression = 3,
|
||||
/// Error occurs when session task cannot be created.
|
||||
GULErrorCodeNetworkSessionTaskCreation = 4,
|
||||
/// Error occurs when there is no response.
|
||||
GULErrorCodeNetworkInvalidResponse = 5
|
||||
};
|
||||
|
||||
#pragma mark - Network constants
|
||||
|
||||
/// The prefix of the ID of the background session.
|
||||
extern NSString *const kGULNetworkBackgroundSessionConfigIDPrefix;
|
||||
|
||||
/// The sub directory to store the files of data that is being uploaded in the background.
|
||||
extern NSString *const kGULNetworkApplicationSupportSubdirectory;
|
||||
|
||||
/// Name of the temporary directory that stores files for background uploading.
|
||||
extern NSString *const kGULNetworkTempDirectoryName;
|
||||
|
||||
/// The period when the temporary uploading file can stay.
|
||||
extern const NSTimeInterval kGULNetworkTempFolderExpireTime;
|
||||
|
||||
/// The default network request timeout interval.
|
||||
extern const NSTimeInterval kGULNetworkTimeOutInterval;
|
||||
|
||||
/// The host to check the reachability of the network.
|
||||
extern NSString *const kGULNetworkReachabilityHost;
|
||||
|
||||
/// The key to get the error context of the UserInfo.
|
||||
extern NSString *const kGULNetworkErrorContext;
|
||||
|
||||
#pragma mark - Network Status Code
|
||||
|
||||
extern const int kGULNetworkHTTPStatusOK;
|
||||
extern const int kGULNetworkHTTPStatusNoContent;
|
||||
extern const int kGULNetworkHTTPStatusCodeMultipleChoices;
|
||||
extern const int kGULNetworkHTTPStatusCodeMovedPermanently;
|
||||
extern const int kGULNetworkHTTPStatusCodeFound;
|
||||
extern const int kGULNetworkHTTPStatusCodeNotModified;
|
||||
extern const int kGULNetworkHTTPStatusCodeMovedTemporarily;
|
||||
extern const int kGULNetworkHTTPStatusCodeNotFound;
|
||||
extern const int kGULNetworkHTTPStatusCodeCannotAcceptTraffic;
|
||||
extern const int kGULNetworkHTTPStatusCodeUnavailable;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
53
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkLoggerProtocol.h
generated
Normal file
53
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkLoggerProtocol.h
generated
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2017 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 "GULNetworkMessageCode.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// The log levels used by GULNetworkLogger.
|
||||
typedef NS_ENUM(NSInteger, GULNetworkLogLevel) {
|
||||
kGULNetworkLogLevelError = 3,
|
||||
kGULNetworkLogLevelWarning = 4,
|
||||
kGULNetworkLogLevelInfo = 6,
|
||||
kGULNetworkLogLevelDebug = 7,
|
||||
};
|
||||
|
||||
@protocol GULNetworkLoggerDelegate <NSObject>
|
||||
|
||||
@required
|
||||
/// Tells the delegate to log a message with an array of contexts and the log level.
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message
|
||||
contexts:(NSArray *)contexts;
|
||||
|
||||
/// Tells the delegate to log a message with a context and the log level.
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message
|
||||
context:(id)context;
|
||||
|
||||
/// Tells the delegate to log a message with the log level.
|
||||
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
|
||||
messageCode:(GULNetworkMessageCode)messageCode
|
||||
message:(NSString *)message;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
51
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h
generated
Normal file
51
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h
generated
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2017 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
|
||||
|
||||
// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
|
||||
typedef NS_ENUM(NSInteger, GULNetworkMessageCode) {
|
||||
// GULNetwork.m
|
||||
kGULNetworkMessageCodeNetwork000 = 900000, // I-NET900000
|
||||
kGULNetworkMessageCodeNetwork001 = 900001, // I-NET900001
|
||||
kGULNetworkMessageCodeNetwork002 = 900002, // I-NET900002
|
||||
kGULNetworkMessageCodeNetwork003 = 900003, // I-NET900003
|
||||
// GULNetworkURLSession.m
|
||||
kGULNetworkMessageCodeURLSession000 = 901000, // I-NET901000
|
||||
kGULNetworkMessageCodeURLSession001 = 901001, // I-NET901001
|
||||
kGULNetworkMessageCodeURLSession002 = 901002, // I-NET901002
|
||||
kGULNetworkMessageCodeURLSession003 = 901003, // I-NET901003
|
||||
kGULNetworkMessageCodeURLSession004 = 901004, // I-NET901004
|
||||
kGULNetworkMessageCodeURLSession005 = 901005, // I-NET901005
|
||||
kGULNetworkMessageCodeURLSession006 = 901006, // I-NET901006
|
||||
kGULNetworkMessageCodeURLSession007 = 901007, // I-NET901007
|
||||
kGULNetworkMessageCodeURLSession008 = 901008, // I-NET901008
|
||||
kGULNetworkMessageCodeURLSession009 = 901009, // I-NET901009
|
||||
kGULNetworkMessageCodeURLSession010 = 901010, // I-NET901010
|
||||
kGULNetworkMessageCodeURLSession011 = 901011, // I-NET901011
|
||||
kGULNetworkMessageCodeURLSession012 = 901012, // I-NET901012
|
||||
kGULNetworkMessageCodeURLSession013 = 901013, // I-NET901013
|
||||
kGULNetworkMessageCodeURLSession014 = 901014, // I-NET901014
|
||||
kGULNetworkMessageCodeURLSession015 = 901015, // I-NET901015
|
||||
kGULNetworkMessageCodeURLSession016 = 901016, // I-NET901016
|
||||
kGULNetworkMessageCodeURLSession017 = 901017, // I-NET901017
|
||||
kGULNetworkMessageCodeURLSession018 = 901018, // I-NET901018
|
||||
kGULNetworkMessageCodeURLSession019 = 901019, // I-NET901019
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
62
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkURLSession.h
generated
Normal file
62
Pods/GoogleUtilities/GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkURLSession.h
generated
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2017 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 "GULNetworkLoggerProtocol.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^GULNetworkCompletionHandler)(NSHTTPURLResponse *_Nullable response,
|
||||
NSData *_Nullable data,
|
||||
NSError *_Nullable error);
|
||||
typedef void (^GULNetworkURLSessionCompletionHandler)(NSHTTPURLResponse *_Nullable response,
|
||||
NSData *_Nullable data,
|
||||
NSString *sessionID,
|
||||
NSError *_Nullable error);
|
||||
typedef void (^GULNetworkSystemCompletionHandler)(void);
|
||||
|
||||
/// The protocol that uses NSURLSession for iOS >= 7.0 to handle requests and responses.
|
||||
@interface GULNetworkURLSession : NSObject
|
||||
|
||||
/// Indicates whether the background network is enabled. Default value is NO.
|
||||
@property(nonatomic, getter=isBackgroundNetworkEnabled) BOOL backgroundNetworkEnabled;
|
||||
|
||||
/// The logger delegate to log message, errors or warnings that occur during the network operations.
|
||||
@property(nonatomic, weak, nullable) id<GULNetworkLoggerDelegate> loggerDelegate;
|
||||
|
||||
/// Calls the system provided completion handler after the background session is finished.
|
||||
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||
completionHandler:(GULNetworkSystemCompletionHandler)completionHandler;
|
||||
|
||||
/// Initializes with logger delegate.
|
||||
- (instancetype)initWithNetworkLoggerDelegate:
|
||||
(nullable id<GULNetworkLoggerDelegate>)networkLoggerDelegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/// Sends an asynchronous POST request and calls the provided completion handler when the request
|
||||
/// completes or when errors occur, and returns an ID of the session/connection.
|
||||
- (nullable NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler;
|
||||
|
||||
/// Sends an asynchronous GET request and calls the provided completion handler when the request
|
||||
/// completes or when errors occur, and returns an ID of the session.
|
||||
- (nullable NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
|
||||
completionHandler:(GULNetworkURLSessionCompletionHandler)handler;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@end
|
||||
34
Pods/GoogleUtilities/GoogleUtilities/Privacy/Resources/PrivacyInfo.xcprivacy
generated
Normal file
34
Pods/GoogleUtilities/GoogleUtilities/Privacy/Resources/PrivacyInfo.xcprivacy
generated
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C56D.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
48
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h
generated
Normal file
48
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h
generated
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2017 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/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h"
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
typedef SCNetworkReachabilityRef (*GULReachabilityCreateWithNameFn)(CFAllocatorRef allocator,
|
||||
const char *host);
|
||||
|
||||
typedef Boolean (*GULReachabilitySetCallbackFn)(SCNetworkReachabilityRef target,
|
||||
SCNetworkReachabilityCallBack callback,
|
||||
SCNetworkReachabilityContext *context);
|
||||
typedef Boolean (*GULReachabilityScheduleWithRunLoopFn)(SCNetworkReachabilityRef target,
|
||||
CFRunLoopRef runLoop,
|
||||
CFStringRef runLoopMode);
|
||||
typedef Boolean (*GULReachabilityUnscheduleFromRunLoopFn)(SCNetworkReachabilityRef target,
|
||||
CFRunLoopRef runLoop,
|
||||
CFStringRef runLoopMode);
|
||||
|
||||
typedef void (*GULReachabilityReleaseFn)(CFTypeRef cf);
|
||||
|
||||
struct GULReachabilityApi {
|
||||
GULReachabilityCreateWithNameFn createWithNameFn;
|
||||
GULReachabilitySetCallbackFn setCallbackFn;
|
||||
GULReachabilityScheduleWithRunLoopFn scheduleWithRunLoopFn;
|
||||
GULReachabilityUnscheduleFromRunLoopFn unscheduleFromRunLoopFn;
|
||||
GULReachabilityReleaseFn releaseFn;
|
||||
};
|
||||
#endif
|
||||
@interface GULReachabilityChecker (Internal)
|
||||
|
||||
- (const struct GULReachabilityApi *)reachabilityApi;
|
||||
- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi;
|
||||
|
||||
@end
|
||||
263
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker.m
generated
Normal file
263
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityChecker.m
generated
Normal file
@ -0,0 +1,263 @@
|
||||
// Copyright 2017 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 "GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h"
|
||||
|
||||
#import "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"
|
||||
#import "GoogleUtilities/Reachability/GULReachabilityMessageCode.h"
|
||||
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
static GULLoggerService kGULLoggerReachability = @"[GULReachability]";
|
||||
#if !TARGET_OS_WATCH
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
|
||||
SCNetworkReachabilityFlags flags,
|
||||
void *info);
|
||||
|
||||
static const struct GULReachabilityApi kGULDefaultReachabilityApi = {
|
||||
SCNetworkReachabilityCreateWithName,
|
||||
SCNetworkReachabilitySetCallback,
|
||||
SCNetworkReachabilityScheduleWithRunLoop,
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop,
|
||||
CFRelease,
|
||||
};
|
||||
|
||||
static NSString *const kGULReachabilityUnknownStatus = @"Unknown";
|
||||
static NSString *const kGULReachabilityConnectedStatus = @"Connected";
|
||||
static NSString *const kGULReachabilityDisconnectedStatus = @"Disconnected";
|
||||
#endif
|
||||
@interface GULReachabilityChecker ()
|
||||
|
||||
@property(nonatomic, assign) const struct GULReachabilityApi *reachabilityApi;
|
||||
@property(nonatomic, assign) GULReachabilityStatus reachabilityStatus;
|
||||
@property(nonatomic, copy) NSString *host;
|
||||
#if !TARGET_OS_WATCH
|
||||
@property(nonatomic, assign) SCNetworkReachabilityRef reachability;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation GULReachabilityChecker
|
||||
|
||||
@synthesize reachabilityApi = reachabilityApi_;
|
||||
#if !TARGET_OS_WATCH
|
||||
@synthesize reachability = reachability_;
|
||||
#endif
|
||||
|
||||
- (const struct GULReachabilityApi *)reachabilityApi {
|
||||
return reachabilityApi_;
|
||||
}
|
||||
|
||||
- (void)setReachabilityApi:(const struct GULReachabilityApi *)reachabilityApi {
|
||||
#if !TARGET_OS_WATCH
|
||||
if (reachability_) {
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
@synthesize reachabilityStatus = reachabilityStatus_;
|
||||
@synthesize host = host_;
|
||||
@synthesize reachabilityDelegate = reachabilityDelegate_;
|
||||
|
||||
- (BOOL)isActive {
|
||||
#if !TARGET_OS_WATCH
|
||||
return reachability_ != nil;
|
||||
#else
|
||||
return NO;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate {
|
||||
if (reachabilityDelegate &&
|
||||
(![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(GULReachabilityDelegate)])) {
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-NET%06ld", (long)kGULReachabilityMessageCode005],
|
||||
@"Reachability delegate doesn't conform to Reachability protocol.");
|
||||
return;
|
||||
}
|
||||
reachabilityDelegate_ = reachabilityDelegate;
|
||||
}
|
||||
|
||||
- (instancetype)initWithReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate
|
||||
withHost:(NSString *)host {
|
||||
self = [super init];
|
||||
|
||||
if (!host || !host.length) {
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode001],
|
||||
@"Invalid host specified");
|
||||
return nil;
|
||||
}
|
||||
if (self) {
|
||||
#if !TARGET_OS_WATCH
|
||||
[self setReachabilityDelegate:reachabilityDelegate];
|
||||
reachabilityApi_ = &kGULDefaultReachabilityApi;
|
||||
reachabilityStatus_ = kGULReachabilityUnknown;
|
||||
host_ = [host copy];
|
||||
reachability_ = nil;
|
||||
#endif
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
reachabilityDelegate_ = nil;
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (BOOL)start {
|
||||
#if TARGET_OS_WATCH
|
||||
return NO;
|
||||
#else
|
||||
|
||||
if (!reachability_) {
|
||||
reachability_ = reachabilityApi_->createWithNameFn(kCFAllocatorDefault, [host_ UTF8String]);
|
||||
if (!reachability_) {
|
||||
return NO;
|
||||
}
|
||||
SCNetworkReachabilityContext context = {
|
||||
0, /* version */
|
||||
(__bridge void *)(self), /* info (passed as last parameter to reachability callback) */
|
||||
NULL, /* retain */
|
||||
NULL, /* release */
|
||||
NULL /* copyDescription */
|
||||
};
|
||||
if (!reachabilityApi_->setCallbackFn(reachability_, ReachabilityCallback, &context) ||
|
||||
!reachabilityApi_->scheduleWithRunLoopFn(reachability_, CFRunLoopGetMain(),
|
||||
kCFRunLoopCommonModes)) {
|
||||
reachabilityApi_->releaseFn(reachability_);
|
||||
reachability_ = nil;
|
||||
|
||||
GULOSLogError(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode002],
|
||||
@"Failed to start reachability handle");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
GULOSLogDebug(kGULLogSubsystem, kGULLoggerReachability, NO,
|
||||
[NSString stringWithFormat:@"I-REA%06ld", (long)kGULReachabilityMessageCode003],
|
||||
@"Monitoring the network status");
|
||||
return YES;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
#if !TARGET_OS_WATCH
|
||||
if (reachability_) {
|
||||
reachabilityStatus_ = kGULReachabilityUnknown;
|
||||
reachabilityApi_->unscheduleFromRunLoopFn(reachability_, CFRunLoopGetMain(),
|
||||
kCFRunLoopCommonModes);
|
||||
reachabilityApi_->releaseFn(reachability_);
|
||||
reachability_ = nil;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
- (GULReachabilityStatus)statusForFlags:(SCNetworkReachabilityFlags)flags {
|
||||
GULReachabilityStatus status = kGULReachabilityNotReachable;
|
||||
// If the Reachable flag is not set, we definitely don't have connectivity.
|
||||
if (flags & kSCNetworkReachabilityFlagsReachable) {
|
||||
// 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 || TARGET_OS_VISION
|
||||
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
|
||||
: kGULReachabilityViaWifi;
|
||||
#elif TARGET_OS_OSX
|
||||
status = kGULReachabilityViaWifi;
|
||||
#endif
|
||||
} else if ((flags & (kSCNetworkReachabilityFlagsConnectionOnDemand |
|
||||
kSCNetworkReachabilityFlagsConnectionOnTraffic)) &&
|
||||
!(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 || TARGET_OS_VISION
|
||||
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kGULReachabilityViaCellular
|
||||
: kGULReachabilityViaWifi;
|
||||
#elif TARGET_OS_OSX
|
||||
status = kGULReachabilityViaWifi;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
- (void)reachabilityFlagsChanged:(SCNetworkReachabilityFlags)flags {
|
||||
GULReachabilityStatus status = [self statusForFlags:flags];
|
||||
if (reachabilityStatus_ != status) {
|
||||
NSString *reachabilityStatusString;
|
||||
if (status == kGULReachabilityUnknown) {
|
||||
reachabilityStatusString = kGULReachabilityUnknownStatus;
|
||||
} else {
|
||||
reachabilityStatusString = (status == kGULReachabilityNotReachable)
|
||||
? kGULReachabilityDisconnectedStatus
|
||||
: kGULReachabilityConnectedStatus;
|
||||
}
|
||||
|
||||
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_];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@end
|
||||
|
||||
#if !TARGET_OS_WATCH
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
|
||||
SCNetworkReachabilityFlags flags,
|
||||
void *info) {
|
||||
GULReachabilityChecker *checker = (__bridge GULReachabilityChecker *)info;
|
||||
[checker reachabilityFlagsChanged:flags];
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function used to be at the top of the file, but it was moved here
|
||||
// as a workaround for a suspected compiler bug. When compiled in Release mode
|
||||
// and run on an iOS device with WiFi disabled, the reachability code crashed
|
||||
// when calling SCNetworkReachabilityScheduleWithRunLoop, or shortly thereafter.
|
||||
// After unsuccessfully trying to diagnose the cause of the crash, it was
|
||||
// discovered that moving this function to the end of the file magically fixed
|
||||
// the crash. If you are going to edit this file, exercise caution and make sure
|
||||
// to test thoroughly with an iOS device under various network conditions.
|
||||
const NSString *GULReachabilityStatusString(GULReachabilityStatus status) {
|
||||
switch (status) {
|
||||
case kGULReachabilityUnknown:
|
||||
return @"Reachability Unknown";
|
||||
|
||||
case kGULReachabilityNotReachable:
|
||||
return @"Not reachable";
|
||||
|
||||
case kGULReachabilityViaWifi:
|
||||
return @"Reachable via Wifi";
|
||||
|
||||
case kGULReachabilityViaCellular:
|
||||
return @"Reachable via Cellular Data";
|
||||
|
||||
default:
|
||||
return [NSString stringWithFormat:@"Invalid reachability status %d", (int)status];
|
||||
}
|
||||
}
|
||||
29
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityMessageCode.h
generated
Normal file
29
Pods/GoogleUtilities/GoogleUtilities/Reachability/GULReachabilityMessageCode.h
generated
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2017 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>
|
||||
|
||||
// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
|
||||
typedef NS_ENUM(NSInteger, GULReachabilityMessageCode) {
|
||||
// GULReachabilityChecker.m
|
||||
kGULReachabilityMessageCode000 = 902000, // I-NET902000
|
||||
kGULReachabilityMessageCode001 = 902001, // I-NET902001
|
||||
kGULReachabilityMessageCode002 = 902002, // I-NET902002
|
||||
kGULReachabilityMessageCode003 = 902003, // I-NET902003
|
||||
kGULReachabilityMessageCode004 = 902004, // I-NET902004
|
||||
kGULReachabilityMessageCode005 = 902005, // I-NET902005
|
||||
kGULReachabilityMessageCode006 = 902006, // I-NET902006
|
||||
};
|
||||
83
Pods/GoogleUtilities/GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h
generated
Normal file
83
Pods/GoogleUtilities/GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h
generated
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2017 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>
|
||||
#if !TARGET_OS_WATCH
|
||||
#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.
|
||||
kGULReachabilityNotReachable, ///< Host is not reachable.
|
||||
kGULReachabilityViaWifi, ///< Host is reachable via Wifi.
|
||||
kGULReachabilityViaCellular, ///< Host is reachable via cellular.
|
||||
} GULReachabilityStatus;
|
||||
|
||||
const NSString *GULReachabilityStatusString(GULReachabilityStatus status);
|
||||
|
||||
@class GULReachabilityChecker;
|
||||
|
||||
/// Google Analytics iOS Reachability Checker.
|
||||
@protocol GULReachabilityDelegate
|
||||
@required
|
||||
/// Called when network status has changed.
|
||||
- (void)reachability:(GULReachabilityChecker *)reachability
|
||||
statusChanged:(GULReachabilityStatus)status;
|
||||
@end
|
||||
|
||||
/// Google Analytics iOS Network Status Checker.
|
||||
@interface GULReachabilityChecker : NSObject
|
||||
|
||||
/// The last known reachability status, or GULReachabilityStatusUnknown if the
|
||||
/// checker is not active.
|
||||
@property(nonatomic, readonly) GULReachabilityStatus reachabilityStatus;
|
||||
/// The host to which reachability status is to be checked.
|
||||
@property(nonatomic, copy, readonly) NSString *host;
|
||||
/// The delegate to be notified of reachability status changes.
|
||||
@property(nonatomic, weak) id<GULReachabilityDelegate> reachabilityDelegate;
|
||||
/// `YES` if the reachability checker is active, `NO` otherwise.
|
||||
@property(nonatomic, readonly) BOOL isActive;
|
||||
|
||||
/// Initialize the reachability checker. Note that you must call start to begin checking for and
|
||||
/// receiving notifications about network status changes.
|
||||
///
|
||||
/// @param reachabilityDelegate The delegate to be notified when reachability status to host
|
||||
/// changes.
|
||||
///
|
||||
/// @param host The name of the host.
|
||||
///
|
||||
- (instancetype)initWithReachabilityDelegate:(id<GULReachabilityDelegate>)reachabilityDelegate
|
||||
withHost:(NSString *)host;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/// Start checking for reachability to the specified host. This has no effect if the status
|
||||
/// checker is already checking for connectivity.
|
||||
///
|
||||
/// @return `YES` if initiating status checking was successful or the status checking has already
|
||||
/// been initiated, `NO` otherwise.
|
||||
- (BOOL)start;
|
||||
|
||||
/// Stop checking for reachability to the specified host. This has no effect if the status
|
||||
/// checker is not checking for connectivity.
|
||||
- (void)stop;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
165
Pods/GoogleUtilities/GoogleUtilities/UserDefaults/GULUserDefaults.m
generated
Normal file
165
Pods/GoogleUtilities/GoogleUtilities/UserDefaults/GULUserDefaults.m
generated
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2018 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/UserDefaults/Public/GoogleUtilities/GULUserDefaults.h"
|
||||
|
||||
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *const kGULLogFormat = @"I-GUL%06ld";
|
||||
|
||||
static GULLoggerService kGULLogUserDefaultsService = @"[GoogleUtilities/UserDefaults]";
|
||||
|
||||
typedef NS_ENUM(NSInteger, GULUDMessageCode) {
|
||||
GULUDMessageCodeInvalidKeyGet = 1,
|
||||
GULUDMessageCodeInvalidKeySet = 2,
|
||||
GULUDMessageCodeInvalidObjectSet = 3,
|
||||
GULUDMessageCodeSynchronizeFailed = 4,
|
||||
};
|
||||
|
||||
@interface GULUserDefaults ()
|
||||
|
||||
@property(nonatomic, readonly) NSUserDefaults *userDefaults;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GULUserDefaults
|
||||
|
||||
+ (GULUserDefaults *)standardUserDefaults {
|
||||
static GULUserDefaults *standardUserDefaults;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
standardUserDefaults = [[GULUserDefaults alloc] init];
|
||||
});
|
||||
return standardUserDefaults;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [self initWithSuiteName:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithSuiteName:(nullable NSString *)suiteName {
|
||||
self = [super init];
|
||||
|
||||
NSString *name = [suiteName copy];
|
||||
|
||||
if (self) {
|
||||
_userDefaults = name.length ? [[NSUserDefaults alloc] initWithSuiteName:name]
|
||||
: [NSUserDefaults standardUserDefaults];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable id)objectForKey:(NSString *)defaultName {
|
||||
NSString *key = [defaultName copy];
|
||||
if (![key isKindOfClass:[NSString class]] || !key.length) {
|
||||
GULOSLogWarning(kGULLogSubsystem, @"<GoogleUtilities>", NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeyGet],
|
||||
@"Cannot get object for invalid user default key.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [self.userDefaults objectForKey:key];
|
||||
}
|
||||
|
||||
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName {
|
||||
NSString *key = [defaultName copy];
|
||||
if (![key isKindOfClass:[NSString class]] || !key.length) {
|
||||
GULOSLogWarning(kGULLogSubsystem, kGULLogUserDefaultsService, NO,
|
||||
[NSString stringWithFormat:kGULLogFormat, (long)GULUDMessageCodeInvalidKeySet],
|
||||
@"Cannot set object for invalid user default key.");
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
[self.userDefaults removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
BOOL isAcceptableValue =
|
||||
[value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]] ||
|
||||
[value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]] ||
|
||||
[value isKindOfClass:[NSDate class]] || [value isKindOfClass:[NSData class]];
|
||||
if (!isAcceptableValue) {
|
||||
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;
|
||||
}
|
||||
|
||||
[self.userDefaults setObject:value forKey:key];
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)key {
|
||||
[self setObject:nil forKey:key];
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
- (NSInteger)integerForKey:(NSString *)defaultName {
|
||||
NSNumber *object = [self objectForKey:defaultName];
|
||||
return object.integerValue;
|
||||
}
|
||||
|
||||
- (float)floatForKey:(NSString *)defaultName {
|
||||
NSNumber *object = [self objectForKey:defaultName];
|
||||
return object.floatValue;
|
||||
}
|
||||
|
||||
- (double)doubleForKey:(NSString *)defaultName {
|
||||
NSNumber *object = [self objectForKey:defaultName];
|
||||
return object.doubleValue;
|
||||
}
|
||||
|
||||
- (BOOL)boolForKey:(NSString *)defaultName {
|
||||
NSNumber *object = [self objectForKey:defaultName];
|
||||
return object.boolValue;
|
||||
}
|
||||
|
||||
- (nullable NSString *)stringForKey:(NSString *)defaultName {
|
||||
return [self objectForKey:defaultName];
|
||||
}
|
||||
|
||||
- (nullable NSArray *)arrayForKey:(NSString *)defaultName {
|
||||
return [self objectForKey:defaultName];
|
||||
}
|
||||
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName {
|
||||
return [self objectForKey:defaultName];
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
- (void)setInteger:(NSInteger)integer forKey:(NSString *)defaultName {
|
||||
[self setObject:@(integer) forKey:defaultName];
|
||||
}
|
||||
|
||||
- (void)setFloat:(float)value forKey:(NSString *)defaultName {
|
||||
[self setObject:@(value) forKey:defaultName];
|
||||
}
|
||||
|
||||
- (void)setDouble:(double)doubleNumber forKey:(NSString *)defaultName {
|
||||
[self setObject:@(doubleNumber) forKey:defaultName];
|
||||
}
|
||||
|
||||
- (void)setBool:(BOOL)boolValue forKey:(NSString *)defaultName {
|
||||
[self setObject:@(boolValue) forKey:defaultName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
105
Pods/GoogleUtilities/GoogleUtilities/UserDefaults/Public/GoogleUtilities/GULUserDefaults.h
generated
Normal file
105
Pods/GoogleUtilities/GoogleUtilities/UserDefaults/Public/GoogleUtilities/GULUserDefaults.h
generated
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2018 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
|
||||
|
||||
/// A thread-safe user defaults that uses C functions from CFPreferences.h instead of
|
||||
/// `NSUserDefaults`. This is to avoid sending an `NSNotification` when it's changed from a
|
||||
/// background thread to avoid crashing. // TODO: Insert radar number here.
|
||||
@interface GULUserDefaults : NSObject
|
||||
|
||||
/// A shared user defaults similar to +[NSUserDefaults standardUserDefaults] and accesses the same
|
||||
/// data of the standardUserDefaults.
|
||||
+ (GULUserDefaults *)standardUserDefaults;
|
||||
|
||||
/// Initializes preferences with a suite name that is the same with the NSUserDefaults' suite name.
|
||||
/// Both of CFPreferences and NSUserDefaults share the same plist file so their data will exactly
|
||||
/// the same.
|
||||
///
|
||||
/// @param suiteName The name of the suite of the user defaults.
|
||||
- (instancetype)initWithSuiteName:(nullable NSString *)suiteName;
|
||||
|
||||
#pragma mark - Getters
|
||||
|
||||
/// Searches the receiver's search list for a default with the key 'defaultName' and return it. If
|
||||
/// another process has changed defaults in the search list, NSUserDefaults will automatically
|
||||
/// update to the latest values. If the key in question has been marked as ubiquitous via a Defaults
|
||||
/// Configuration File, the latest value may not be immediately available, and the registered value
|
||||
/// will be returned instead.
|
||||
- (nullable id)objectForKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -objectForKey:, except that it will return nil if the value is not an NSArray.
|
||||
- (nullable NSArray *)arrayForKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -objectForKey:, except that it will return nil if the value
|
||||
/// is not an NSDictionary.
|
||||
- (nullable NSDictionary<NSString *, id> *)dictionaryForKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -objectForKey:, except that it will convert NSNumber values to their NSString
|
||||
/// representation. If a non-string non-number value is found, nil will be returned.
|
||||
- (nullable NSString *)stringForKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -objectForKey:, except that it converts the returned value to an NSInteger. If the
|
||||
/// value is an NSNumber, the result of -integerValue will be returned. If the value is an NSString,
|
||||
/// it will be converted to NSInteger if possible. If the value is a boolean, it will be converted
|
||||
/// to either 1 for YES or 0 for NO. If the value is absent or can't be converted to an integer, 0
|
||||
/// will be returned.
|
||||
- (NSInteger)integerForKey:(NSString *)defaultName;
|
||||
|
||||
/// Similar to -integerForKey:, except that it returns a float, and boolean values will not be
|
||||
/// converted.
|
||||
- (float)floatForKey:(NSString *)defaultName;
|
||||
|
||||
/// Similar to -integerForKey:, except that it returns a double, and boolean values will not be
|
||||
/// converted.
|
||||
- (double)doubleForKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value
|
||||
/// is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an
|
||||
/// NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string
|
||||
/// will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned.
|
||||
- (BOOL)boolForKey:(NSString *)defaultName;
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
/// Immediately stores a value (or removes the value if `nil` is passed as the value) for the
|
||||
/// provided key in the search list entry for the receiver's suite name in the current user and any
|
||||
/// host, then asynchronously stores the value persistently, where it is made available to other
|
||||
/// processes.
|
||||
- (void)setObject:(nullable id)value forKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -setObject:forKey: except that the value is converted from a float to an NSNumber.
|
||||
- (void)setFloat:(float)value forKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -setObject:forKey: except that the value is converted from a double to an
|
||||
/// NSNumber.
|
||||
- (void)setDouble:(double)value forKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -setObject:forKey: except that the value is converted from an NSInteger to an
|
||||
/// NSNumber.
|
||||
- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName;
|
||||
|
||||
/// Equivalent to -setObject:forKey: except that the value is converted from a BOOL to an NSNumber.
|
||||
- (void)setBool:(BOOL)value forKey:(NSString *)defaultName;
|
||||
|
||||
#pragma mark - Removing Defaults
|
||||
|
||||
/// Equivalent to -[... setObject:nil forKey:defaultName]
|
||||
- (void)removeObjectForKey:(NSString *)defaultName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
224
Pods/GoogleUtilities/LICENSE
generated
Normal file
224
Pods/GoogleUtilities/LICENSE
generated
Normal file
@ -0,0 +1,224 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
================================================================================
|
||||
|
||||
Copyright (c) 2017 Landon J. Fuller <landon@landonf.org>
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
189
Pods/GoogleUtilities/README.md
generated
Normal file
189
Pods/GoogleUtilities/README.md
generated
Normal file
@ -0,0 +1,189 @@
|
||||
[](https://cocoapods.org/pods/GoogleUtilities)
|
||||
[](https://cocoapods.org/pods/GoogleUtilities)
|
||||
[](https://cocoapods.org/pods/GoogleUtilities)
|
||||
|
||||
[![Actions Status][gh-google-utilities-badge]][gh-actions]
|
||||
|
||||
# GoogleUtilities
|
||||
|
||||
GoogleUtilities provides a set of utilities for Firebase and other Google SDKs for Apple platform
|
||||
development.
|
||||
|
||||
The utilities are not directly supported for non-Google library usage.
|
||||
|
||||
## Integration Testing
|
||||
These instructions apply to minor and patch version updates. Major versions need
|
||||
a customized adaptation.
|
||||
|
||||
After the CI is green:
|
||||
* Determine the next version for release by checking the
|
||||
[tagged releases](https://github.com/google/GoogleUtilities/tags).
|
||||
Ensure that the next release version keeps the Swift PM and CocoaPods versions in sync.
|
||||
* Verify that the releasing version is the latest entry in the [CHANGELOG.md](CHANGELOG.md),
|
||||
updating it if necessary.
|
||||
* Update the version in the podspec to match the latest entry in the [CHANGELOG.md](CHANGELOG.md)
|
||||
* Checkout the `main` branch and ensure it is up to date
|
||||
```console
|
||||
git checkout main
|
||||
git pull
|
||||
```
|
||||
* Add the CocoaPods tag (`{version}` will be the latest version in the [podspec](GoogleUtilities.podspec#L3))
|
||||
```console
|
||||
git tag CocoaPods-{version}
|
||||
git push origin CocoaPods-{version}
|
||||
```
|
||||
* Push the podspec to the designated repo
|
||||
* If this version of GoogleUtilities is intended to launch **before or with** the next Firebase release:
|
||||
<details>
|
||||
<summary>Push to <b>SpecsStaging</b></summary>
|
||||
|
||||
```console
|
||||
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:
|
||||
```console
|
||||
pod repo add staging git@github.com:firebase/SpecsStaging.git
|
||||
```
|
||||
</details>
|
||||
* Otherwise:
|
||||
<details>
|
||||
<summary>Push to <b>SpecsDev</b></summary>
|
||||
|
||||
```console
|
||||
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:
|
||||
```console
|
||||
pod repo add dev git@github.com:firebase/SpecsDev.git
|
||||
```
|
||||
</details>
|
||||
* Run Firebase CI by waiting until next nightly or adding a PR that touches `Gemfile`.
|
||||
* On google3, run copybara using the command below. Then, start a global TAP on the generated CL. Deflake as needed.
|
||||
```console
|
||||
third_party/firebase/ios/Releases/run_copy_bara.py --directory GoogleUtilities --branch main
|
||||
```
|
||||
|
||||
## Publishing
|
||||
The release process is as follows:
|
||||
1. [Tag and release for Swift PM](#swift-package-manager)
|
||||
2. [Publish to CocoaPods](#cocoapods)
|
||||
3. [Create GitHub Release](#create-github-release)
|
||||
4. [Perform post release cleanup](#post-release-cleanup)
|
||||
|
||||
### Swift Package Manager
|
||||
By creating and [pushing a tag](https://github.com/google/GoogleUtilities/tags)
|
||||
for Swift PM, the newly tagged version will be immediately released for public use.
|
||||
Given this, please verify the intended time of release for Swift PM.
|
||||
* Add a version tag for Swift PM
|
||||
```console
|
||||
git tag {version}
|
||||
git push origin {version}
|
||||
```
|
||||
*Note: Ensure that any inflight PRs that depend on the new `GoogleUtilities` version are updated to point to the
|
||||
newly tagged version rather than a checksum.*
|
||||
|
||||
### CocoaPods
|
||||
* Publish the newly versioned pod to CocoaPods
|
||||
|
||||
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.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.*
|
||||
|
||||
The pod push was successful if the above command logs: `🚀 GoogleUtilities ({version}) successfully published`.
|
||||
In addition, a new commit that publishes the new version (co-authored by [CocoaPodsAtGoogle](https://github.com/CocoaPodsAtGoogle))
|
||||
should appear in the [CocoaPods specs repo](https://github.com/CocoaPods/Specs). Last, the latest version should be displayed
|
||||
on [GoogleUtilities's CocoaPods page](https://cocoapods.org/pods/GoogleUtilities).
|
||||
|
||||
### [Create GitHub Release](https://github.com/google/GoogleUtilities/releases/new/)
|
||||
Update the [release template](https://github.com/google/GoogleUtilities/releases/new/)'s **Tag version** and **Release title**
|
||||
fields with the latest version. In addition, reference the [Release Notes](./CHANGELOG.md) in the release's description.
|
||||
|
||||
See [this release](https://github.com/google/GoogleUtilities/releases/edit/7.7.0) for an example.
|
||||
|
||||
*Don't forget to perform the [post release cleanup](#post-release-cleanup)!*
|
||||
|
||||
### Post Release Cleanup
|
||||
<details>
|
||||
<summary>Clean up <b>SpecsStaging</b></summary>
|
||||
|
||||
```console
|
||||
pwd=$(pwd)
|
||||
mkdir -p /tmp/release-cleanup && cd $_
|
||||
git clone git@github.com:firebase/SpecsStaging.git
|
||||
cd SpecsStaging/
|
||||
git rm -rf GoogleUtilities/
|
||||
git commit -m "Post publish cleanup"
|
||||
git push origin master
|
||||
rm -rf /tmp/release-cleanup
|
||||
cd $pwd
|
||||
```
|
||||
</details>
|
||||
|
||||
## Development
|
||||
|
||||
To develop in this repository, ensure that you have at least the following software:
|
||||
|
||||
* Xcode 12.0 (or later)
|
||||
* CocoaPods 1.10.0 (or later)
|
||||
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
|
||||
|
||||
For the pod that you want to develop:
|
||||
|
||||
`pod gen GoogleUtilities.podspec --local-sources=./ --auto-open --platforms=ios`
|
||||
|
||||
Note: If the CocoaPods cache is out of date, you may need to run
|
||||
`pod repo update` before the `pod gen` command.
|
||||
|
||||
Note: Set the `--platforms` option to `macos` or `tvos` to develop/test for
|
||||
those platforms. Since 10.2, Xcode does not properly handle multi-platform
|
||||
CocoaPods workspaces.
|
||||
|
||||
### Development for Catalyst
|
||||
* `pod gen GoogleUtilities.podspec --local-sources=./ --auto-open --platforms=ios`
|
||||
* Check the Mac box in the App-iOS Build Settings
|
||||
* Sign the App in the Settings Signing & Capabilities tab
|
||||
* Click Pods in the Project Manager
|
||||
* Add Signing to the iOS host app and unit test targets
|
||||
* Select the Unit-unit scheme
|
||||
* Run it to build and test
|
||||
|
||||
Alternatively disable signing in each target:
|
||||
* Go to Build Settings tab
|
||||
* Click `+`
|
||||
* Select `Add User-Defined Setting`
|
||||
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
|
||||
|
||||
### 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@18
|
||||
brew install mint
|
||||
```
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Select a scheme and press Command-u to build a component and run its unit tests.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [Contributing](CONTRIBUTING.md).
|
||||
|
||||
## License
|
||||
|
||||
The contents of this repository is licensed under the
|
||||
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
[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
|
||||
104
Pods/GoogleUtilities/third_party/IsAppEncrypted/IsAppEncrypted.m
generated
vendored
Normal file
104
Pods/GoogleUtilities/third_party/IsAppEncrypted/IsAppEncrypted.m
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2017 Landon J. Fuller <landon@landonf.org>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
// Comment from
|
||||
// <a href="http://iphonedevwiki.net/index.php/Crack_prevention">iPhone Dev
|
||||
// Wiki Crack Prevention</a>:
|
||||
// App Store binaries are signed by both their developer and Apple. This
|
||||
// encrypts the binary so that decryption keys are needed in order to make the
|
||||
// binary readable. When iOS executes the binary, the decryption keys are used
|
||||
// to decrypt the binary into a readable state where it is then loaded into
|
||||
// memory and executed. iOS can tell the encryption status of a binary via the
|
||||
// cryptid structure member of LC_ENCRYPTION_INFO MachO load command. If
|
||||
// cryptid is a non-zero value then the binary is encrypted.
|
||||
//
|
||||
// 'Cracking' works by letting the kernel decrypt the binary then siphoning the
|
||||
// decrypted data into a new binary file, resigning, and repackaging. This will
|
||||
// only work on jailbroken devices as codesignature validation has been
|
||||
// removed. Resigning takes place because while the codesignature doesn't have
|
||||
// to be valid thanks to the jailbreak, it does have to be in place unless you
|
||||
// have AppSync or similar to disable codesignature checks.
|
||||
//
|
||||
// More information at
|
||||
// <a href="http://landonf.org/2009/02/index.html">Landon Fuller's blog</a>
|
||||
|
||||
#import "third_party/IsAppEncrypted/Public/IsAppEncrypted.h"
|
||||
|
||||
#import <mach-o/dyld.h>
|
||||
#import <objc/objc.h>
|
||||
|
||||
/// The encryption info struct and constants are missing from the iPhoneSimulator SDK, but not from
|
||||
/// the iPhoneOS or Mac OS X SDKs. Since one doesn't ever ship a Simulator binary, we'll just
|
||||
/// provide the definitions here.
|
||||
#if TARGET_OS_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
|
||||
#define LC_ENCRYPTION_INFO 0x21
|
||||
struct encryption_info_command {
|
||||
uint32_t cmd;
|
||||
uint32_t cmdsize;
|
||||
uint32_t cryptoff;
|
||||
uint32_t cryptsize;
|
||||
uint32_t cryptid;
|
||||
};
|
||||
#endif
|
||||
|
||||
BOOL IsAppEncrypted(void) {
|
||||
const struct mach_header *executableHeader = NULL;
|
||||
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
|
||||
const struct mach_header *header = _dyld_get_image_header(i);
|
||||
if (header && header->filetype == MH_EXECUTE) {
|
||||
executableHeader = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!executableHeader) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL is64bit = (executableHeader->magic == MH_MAGIC_64);
|
||||
uintptr_t cursor = (uintptr_t)executableHeader +
|
||||
(is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
|
||||
const struct segment_command *segmentCommand = NULL;
|
||||
uint32_t i = 0;
|
||||
|
||||
while (i++ < executableHeader->ncmds) {
|
||||
segmentCommand = (struct segment_command *)cursor;
|
||||
|
||||
if (!segmentCommand) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((!is64bit && segmentCommand->cmd == LC_ENCRYPTION_INFO) ||
|
||||
(is64bit && segmentCommand->cmd == LC_ENCRYPTION_INFO_64)) {
|
||||
if (is64bit) {
|
||||
struct encryption_info_command_64 *cryptCmd =
|
||||
(struct encryption_info_command_64 *)segmentCommand;
|
||||
return cryptCmd && cryptCmd->cryptid != 0;
|
||||
} else {
|
||||
struct encryption_info_command *cryptCmd = (struct encryption_info_command *)segmentCommand;
|
||||
return cryptCmd && cryptCmd->cryptid != 0;
|
||||
}
|
||||
}
|
||||
cursor += segmentCommand->cmdsize;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
24
Pods/GoogleUtilities/third_party/IsAppEncrypted/Public/IsAppEncrypted.h
generated
vendored
Normal file
24
Pods/GoogleUtilities/third_party/IsAppEncrypted/Public/IsAppEncrypted.h
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2017 Landon J. Fuller <landon@landonf.org>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#import <objc/objc.h>
|
||||
|
||||
BOOL IsAppEncrypted(void);
|
||||
Reference in New Issue
Block a user