### Configuration Manager Example Source: https://context7.com/google/googleutilities/llms.txt A sample `ConfigManager` class demonstrating how to encapsulate GULUserDefaults operations for managing application settings like API endpoints and event counts. ```objectivec // Example: Configuration manager using GULUserDefaults @interface ConfigManager : NSObject @property (nonatomic, strong) GULUserDefaults *defaults; @end @implementation ConfigManager - (instancetype)init { self = [super init]; if (self) { _defaults = [GULUserDefaults standardUserDefaults]; } return self; } - (void)setAPIEndpoint:(NSString *)endpoint { [self.defaults setObject:endpoint forKey:@"api_endpoint"]; } - (NSString *)apiEndpoint { return [self.defaults stringForKey:@"api_endpoint"] ?: @"https://api.default.com"; } - (void)incrementEventCount { NSInteger current = [self.defaults integerForKey:@"event_count"]; [self.defaults setInteger:current + 1 forKey:@"event_count"]; } @end ``` -------------------------------- ### Implement Network Reachability Monitoring Source: https://context7.com/google/googleutilities/llms.txt Implement GULReachabilityDelegate to receive network status changes. Initialize the checker with a delegate and a host to monitor. Start monitoring to receive updates. ```Objective-C #import @interface MyConnectivityMonitor : NSObject @property (nonatomic, strong) GULReachabilityChecker *reachabilityChecker; @end @implementation MyConnectivityMonitor - (instancetype)init { self = [super init]; if (self) { // Initialize with delegate and host to monitor _reachabilityChecker = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:@"www.google.com"]; } return self; } - (void)startMonitoring { BOOL started = [self.reachabilityChecker start]; if (started) { NSLog(@"Reachability monitoring started for host: %@", self.reachabilityChecker.host); } else { NSLog(@"Failed to start reachability monitoring"); } } - (void)stopMonitoring { [self.reachabilityChecker stop]; NSLog(@"Reachability monitoring stopped"); } - (GULReachabilityStatus)currentStatus { return self.reachabilityChecker.reachabilityStatus; } - (BOOL)isOnline { GULReachabilityStatus status = self.reachabilityChecker.reachabilityStatus; return (status == kGULReachabilityViaWifi || status == kGULReachabilityViaCellular); } #pragma mark - GULReachabilityDelegate - (void)reachability:(GULReachabilityChecker *)reachability statusChanged:(GULReachabilityStatus)status { // Get human-readable status string NSString *statusString = (NSString *)GULReachabilityStatusString(status); NSLog(@"Network status changed: %@", statusString); switch (status) { case kGULReachabilityUnknown: NSLog(@"Reachability unknown"); break; case kGULReachabilityNotReachable: NSLog(@"Network not reachable - queue offline operations"); [self pauseNetworkOperations]; break; case kGULReachabilityViaWifi: NSLog(@"Connected via WiFi - optimal for large transfers"); [self resumeNetworkOperations]; [self uploadQueuedData]; break; case kGULReachabilityViaCellular: NSLog(@"Connected via Cellular - limit data usage"); [self resumeNetworkOperations]; break; } } - (void)pauseNetworkOperations { // Implement pause logic } - (void)resumeNetworkOperations { // Implement resume logic } - (void)uploadQueuedData { // Upload data when on WiFi } @end // Usage MyConnectivityMonitor *monitor = [[MyConnectivityMonitor alloc] init]; [monitor startMonitoring]; if (monitor.reachabilityChecker.isActive) { NSLog(@"Monitoring is active"); } ``` -------------------------------- ### Get Device and Platform Information Source: https://context7.com/google/googleutilities/llms.txt Retrieve details about the device model, operating system version, Apple platform (e.g., iOS, macOS), and deployment method (e.g., CocoaPods, Swift Package Manager). ```Objective-C // Device and platform information NSString *deviceModel = [GULAppEnvironmentUtil deviceModel]; // e.g., "iPhone14,2" NSString *simModel = [GULAppEnvironmentUtil deviceSimulatorModel]; // e.g., "x86_64" on simulator NSString *osVersion = [GULAppEnvironmentUtil systemVersion]; // e.g., "16.0" NSString *platform = [GULAppEnvironmentUtil applePlatform]; // "ios", "macos", "tvos", etc. NSString *devicePlatform = [GULAppEnvironmentUtil appleDevicePlatform]; // includes "ipados" NSString *deploymentType = [GULAppEnvironmentUtil deploymentType]; // "cocoapods", "swiftpm", etc. NSLog(@"Device: %@ running %@ %@", deviceModel, platform, osVersion); NSLog(@"Deployment: %@", deploymentType); ``` -------------------------------- ### Initialize and Use GULUserDefaults Source: https://context7.com/google/googleutilities/llms.txt Demonstrates initializing GULUserDefaults with standard or custom suites and storing/retrieving various data types. Ensure GULUserDefaults is imported. ```objectivec #import // Use standard user defaults (same data as NSUserDefaults.standardUserDefaults) GULUserDefaults *defaults = [GULUserDefaults standardUserDefaults]; // Or use a custom suite (app groups for sharing between app and extensions) GULUserDefaults *sharedDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"group.com.myapp.shared"]; // Store various types [defaults setObject:@"user@example.com" forKey:@"userEmail"]; [defaults setObject:@[@"item1", @"item2", @"item3"] forKey:@"recentItems"]; [defaults setObject:@{@"theme": @"dark", @"fontSize": @14} forKey:@"preferences"]; [defaults setBool:YES forKey:@"notificationsEnabled"]; [defaults setInteger:42 forKey:@"launchCount"]; [defaults setFloat:0.75f forKey:@"volume"]; [defaults setDouble:3.14159265359 forKey:@"precisionValue"]; // Retrieve values with type-safe methods NSString *email = [defaults stringForKey:@"userEmail"]; NSArray *recentItems = [defaults arrayForKey:@"recentItems"]; NSDictionary *prefs = [defaults dictionaryForKey:@"preferences"]; BOOL notifications = [defaults boolForKey:@"notificationsEnabled"]; NSInteger launchCount = [defaults integerForKey:@"launchCount"]; float volume = [defaults floatForKey:@"volume"]; double precision = [defaults doubleForKey:@"precisionValue"]; // Generic object retrieval id unknownValue = [defaults objectForKey:@"someKey"]; // Remove a value [defaults removeObjectForKey:@"temporaryData"]; ``` -------------------------------- ### Implement GULNetwork in Objective-C Source: https://context7.com/google/googleutilities/llms.txt Demonstrates initializing GULNetwork, handling network requests, and implementing reachability and logging delegates. ```objectivec #import #import // Implement the reachability delegate @interface MyNetworkHandler : NSObject @property (nonatomic, strong) GULNetwork *network; @end @implementation MyNetworkHandler - (instancetype)init { self = [super init]; if (self) { // Initialize with default reachability host _network = [[GULNetwork alloc] init]; // Or use custom reachability host // _network = [[GULNetwork alloc] initWithReachabilityHost:@"api.myservice.com"]; _network.reachabilityDelegate = self; _network.loggerDelegate = self; _network.timeoutInterval = 30.0; // Default is kGULNetworkTimeOutInterval _network.isDebugModeEnabled = YES; } return self; } // POST request with background session support - (void)uploadData:(NSData *)data toURL:(NSURL *)url { NSDictionary *headers = @{ @"Content-Type": @"application/json", @"Authorization": @"Bearer your-token" }; NSString *sessionID = [self.network postURL:url headers:headers payload:data queue:dispatch_get_main_queue() usingBackgroundSession:YES // Use background session for large uploads completionHandler:^(NSHTTPURLResponse *response, NSData *responseData, NSString *sessionID, NSError *error) { if (error) { NSLog(@"Upload failed: %@", error.localizedDescription); return; } if (response.statusCode == kGULNetworkHTTPStatusOK) { NSLog(@"Upload successful, response: %@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]); } else { NSLog(@"Server returned status: %ld", (long)response.statusCode); } }]; NSLog(@"Started upload session: %@", sessionID); } // GET request - (void)fetchDataFromURL:(NSURL *)url { NSDictionary *headers = @{@"Accept": @"application/json"}; [self.network getURL:url headers:headers queue:dispatch_get_main_queue() usingBackgroundSession:NO // Use default session for quick fetches completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSString *sessionID, NSError *error) { if (error) { NSLog(@"Fetch failed: %@", error); return; } NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSLog(@"Received: %@", json); }]; } // Check network status - (BOOL)canMakeRequest { return self.network.isNetworkConnected && !self.network.hasUploadInProgress; } #pragma mark - GULNetworkReachabilityDelegate - (void)reachabilityDidChange { if (self.network.isNetworkConnected) { NSLog(@"Network connected - resuming operations"); } else { NSLog(@"Network disconnected - pausing operations"); } } #pragma mark - GULNetworkLoggerDelegate - (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel messageCode:(NSString *)messageCode message:(NSString *)message { NSLog(@"[Network %@] %@", messageCode, message); } @end // Handle background session events in AppDelegate // AppDelegate.m - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { [GULNetwork handleEventsForBackgroundURLSessionID:identifier completionHandler:completionHandler]; } ``` -------------------------------- ### Initialize and Use GULKeychainStorage Source: https://context7.com/google/googleutilities/llms.txt Demonstrates initializing GULKeychainStorage with a service name, storing credentials, retrieving them, and removing them. Ensure objects conform to NSSecureCoding. ```Objective-C #import // Initialize with a service name (maps to kSecAttrService) GULKeychainStorage *keychain = [[GULKeychainStorage alloc] initWithService:@"com.myapp.credentials"]; // Store a secure object (must conform to NSSecureCoding) NSString *accessGroup = @"com.mycompany.shared"; // nil for default // Store credentials NSDictionary *credentials = @{ @"token": @"eyJhbGciOiJIUzI1NiIs...", @"refreshToken": @"dGhpcyBpcyBhIHJlZnJlc2g...", @"expiresAt": [NSDate dateWithTimeIntervalSinceNow:3600] }; [keychain setObject:credentials forKey:@"user_credentials" accessGroup:accessGroup completionHandler:^(id savedObject, NSError *error) { if (error) { NSLog(@"Failed to save credentials: %@", error.localizedDescription); return; } NSLog(@"Credentials saved securely"); }]; // Retrieve credentials [keychain getObjectForKey:@"user_credentials" objectClass:[NSDictionary class] accessGroup:accessGroup completionHandler:^(id obj, NSError *error) { if (error) { NSLog(@"Failed to retrieve credentials: %@", error.localizedDescription); return; } if (obj) { NSDictionary *creds = (NSDictionary *)obj; NSString *token = creds[@"token"]; NSDate *expiresAt = creds[@"expiresAt"]; if ([expiresAt compare:[NSDate date]] == NSOrderedDescending) { NSLog(@"Token is valid: %@...", [token substringToIndex:20]); } else { NSLog(@"Token expired, need to refresh"); } } else { NSLog(@"No credentials stored"); } }]; // Remove credentials (e.g., on logout) [keychain removeObjectForKey:@"user_credentials" accessGroup:accessGroup completionHandler:^(NSError *error) { if (error) { NSLog(@"Failed to remove credentials: %@", error.localizedDescription); } else { NSLog(@"Credentials removed"); } }]; ``` ```Objective-C // Example: Secure token manager @interface SecureTokenManager : NSObject @property (nonatomic, strong) GULKeychainStorage *keychain; @end @implementation SecureTokenManager - (instancetype)init { self = [super init]; if (self) { _keychain = [[GULKeychainStorage alloc] initWithService:@"com.myapp.tokens"]; } return self; } - (void)saveToken:(NSString *)token forKey:(NSString *)key completion:(void(^)(BOOL success))completion { [self.keychain setObject:token forKey:key accessGroup:nil completionHandler:^(id obj, NSError *error) { completion(error == nil); }]; } - (void)getToken:(NSString *)key completion:(void(^)(NSString *token))completion { [self.keychain getObjectForKey:key objectClass:[NSString class] accessGroup:nil completionHandler:^(id obj, NSError *error) { completion((NSString *)obj); }]; } @end ``` -------------------------------- ### Push to SpecsStaging Repository Source: https://github.com/google/googleutilities/blob/main/README.md Publishes the GoogleUtilities podspec to the staging repository for pre-release testing. Add the staging repo if it does not exist. ```bash pod repo push --skip-tests --use-json staging GoogleUtilities.podspec ``` ```bash pod repo add staging git@github.com:firebase/SpecsStaging.git ``` -------------------------------- ### Swizzle Methods and Inspect Ivars with GULSwizzler Source: https://context7.com/google/googleutilities/llms.txt Demonstrates swizzling instance and class methods using blocks, and inspecting object instance variables for debugging. ```objectivec #import #import #import // Example: Swizzle an instance method @interface SwizzlerExample : NSObject @end @implementation SwizzlerExample + (void)swizzleViewDidLoadOnViewController { Class targetClass = [UIViewController class]; SEL selector = @selector(viewDidLoad); // Check if method exists if (![GULSwizzler selector:selector existsInClass:targetClass isClassSelector:NO]) { NSLog(@"Method does not exist, cannot swizzle"); return; } // Get the original implementation IMP originalIMP = [GULSwizzler currentImplementationForClass:targetClass selector:selector isClassSelector:NO]; // Create swizzle block that calls original and adds behavior id swizzleBlock = ^(UIViewController *self) { NSLog(@"viewDidLoad called on: %@", NSStringFromClass([self class])); // Call original implementation if (originalIMP) { ((void (*)(id, SEL))originalIMP)(self, selector); } // Add custom behavior after original NSLog(@"viewDidLoad completed for: %@", NSStringFromClass([self class])); }; // Perform the swizzle [GULSwizzler swizzleClass:targetClass selector:selector isClassSelector:NO withBlock:swizzleBlock]; NSLog(@"Successfully swizzled viewDidLoad"); } // Example: Swizzle a class method + (void)swizzleSharedInstanceOnSingleton { Class targetClass = [MySingleton class]; SEL selector = @selector(sharedInstance); IMP originalIMP = [GULSwizzler currentImplementationForClass:targetClass selector:selector isClassSelector:YES]; id swizzleBlock = ^id(id self) { NSLog(@"Singleton accessed"); // Call original and return its result if (originalIMP) { return ((id (*)(id, SEL))originalIMP)(self, selector); } return nil; }; [GULSwizzler swizzleClass:targetClass selector:selector isClassSelector:YES withBlock:swizzleBlock]; } // Inspect object's ivars (useful for debugging) + (void)inspectObject:(id)object { NSArray *ivars = [GULSwizzler ivarObjectsForObject:object]; NSLog(@"Object %@ has %lu object ivars:", NSStringFromClass([object class]), (unsigned long)ivars.count); for (id ivar in ivars) { NSLog(@" - %@: %@", NSStringFromClass([ivar class]), ivar); } } @end // Usage [SwizzlerExample swizzleViewDidLoadOnViewController]; // Inspect ivars of an object UIViewController *vc = [[UIViewController alloc] init]; [SwizzlerExample inspectObject:vc]; ``` -------------------------------- ### Push to SpecsDev Repository Source: https://github.com/google/googleutilities/blob/main/README.md Publishes the GoogleUtilities podspec to the development repository. Add the dev repo if it does not exist. ```bash pod repo push --skip-tests --use-json dev GoogleUtilities.podspec ``` ```bash pod repo add dev git@github.com:firebase/SpecsDev.git ``` -------------------------------- ### Configure Feature Flags Based on Environment Source: https://context7.com/google/googleutilities/llms.txt Dynamically set configuration options like verbose logging, analytics enablement, and debug menu visibility based on the detected runtime environment and device information. ```Objective-C // Example: Conditional feature flags based on environment NSDictionary *config = @{ @"verbose_logging": @(![GULAppEnvironmentUtil isFromAppStore]), @"analytics_enabled": @([GULAppEnvironmentUtil isFromAppStore]), @"debug_menu": @([GULAppEnvironmentUtil isSimulator] || [GULAppEnvironmentUtil isAppStoreReceiptSandbox]), @"device_model": deviceModel ?: @"unknown", @"os_version": osVersion, @"platform": devicePlatform }; ``` -------------------------------- ### Run Copybara for Google3 Source: https://github.com/google/googleutilities/blob/main/README.md Executes the copybara script on Google3 to manage third-party code synchronization for releases. ```bash third_party/firebase/ios/Releases/run_copy_bara.py --directory GoogleUtilities --branch main ``` -------------------------------- ### Add and Push Swift PM Version Tag Source: https://github.com/google/googleutilities/blob/main/README.md Tags and pushes a new version for Swift Package Manager releases. Ensure this is done at the intended release time. ```bash git tag {version} git push origin {version} ``` -------------------------------- ### Check Feature Availability Source: https://context7.com/google/googleutilities/llms.txt Verify if specific features, such as background URL session uploads, are supported on the current platform. This is crucial for choosing appropriate network session types. ```Objective-C // Check feature availability if ([GULAppEnvironmentUtil supportsBackgroundURLSessionUploads]) { NSLog(@"Background uploads supported"); // Use background sessions } else { NSLog(@"Background uploads not supported (extension or App Clip)"); // Use foreground sessions only } ``` -------------------------------- ### Initialize and Configure GULLogger Source: https://context7.com/google/googleutilities/llms.txt Initialize the logger, register SDK version, and set the desired logging level. Note that the logging level cannot be set above Notice in App Store builds. ```Objective-C #import #import // Define your subsystem and service identifiers static NSString *const kMySubsystem = @"com.myapp.sdk"; static NSString *const kMyLoggerService = @"[MySDK]"; // Initialize the logger (call once at app startup) GULLoggerInitialize(); // Register your SDK version for log output GULLoggerRegisterVersion(@"1.0.0"); // Set the logging level (defaults to GULLoggerLevelNotice) // Note: Cannot set above Notice level in App Store builds GULSetLoggerLevel(GULLoggerLevelDebug); // Check current log level GULLoggerLevel currentLevel = GULGetLoggerLevel(); NSLog(@"Current log level: %ld", (long)currentLevel); // Check if a specific level would be logged if (GULIsLoggableLevel(GULLoggerLevelDebug)) { NSLog(@"Debug logging is enabled"); } ``` -------------------------------- ### Define an App Delegate Interceptor Class Source: https://github.com/google/googleutilities/blob/main/GoogleUtilities/AppDelegateSwizzler/README.md Create a class that implements UIApplicationDelegate to intercept specific app delegate methods. Ensure it provides a singleton instance for registration. ```objc #import #import NS_ASSUME_NONNULL_BEGIN /// An instance of this class is meant to be registered as an AppDelegate interceptor, and /// implements the logic that my SDK needs to perform when certain app delegate methods are invoked. @interface MYAppDelegateInterceptor : NSObject /// Returns the MYAppDelegateInterceptor singleton. /// Always register just this singleton as the app delegate interceptor. This instance is /// retained. The App Delegate Swizzler only retains weak references and so this is needed. + (instancetype)sharedInstance; @end NS_ASSUME_NONNULL_END ``` ```objc #import "MYAppDelegateInterceptor.h" @implementation MYAppDelegateInterceptor + (instancetype)sharedInstance { static dispatch_once_t once; static MYAppDelegateInterceptor *sharedInstance; dispatch_once(&once, ^{ sharedInstance = [[MYAppDelegateInterceptor alloc] init]; }); return sharedInstance; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL options:(NSDictionary *)options { [MYInterestingClass doSomething]; // Results of this are ORed and NO doesn't affect other delegate interceptors' result. return NO; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { [MYInterestingClass doSomething]; // Results of this are ORed and NO doesn't affect other delegate interceptors' result. return NO; } #pragma mark - Network overridden handler methods - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { // Note: Interceptors are not responsible for (and should not) call the completion handler. [MYInterestingClass doSomething]; } #pragma mark - User Activities overridden handler methods - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { [MYInterestingClass doSomething]; // Results of this are ORed and NO doesn't affect other delegate interceptors' result. return NO; } @end ``` -------------------------------- ### Detect Runtime Environment Source: https://context7.com/google/googleutilities/llms.txt Determine if the app is running on a simulator, within an App Extension, or as an App Clip. This helps in managing hardware-dependent features or platform-specific capabilities. ```Objective-C // Check runtime environment if ([GULAppEnvironmentUtil isSimulator]) { NSLog(@"Running on Simulator - skip hardware-dependent features"); // Skip push notification registration, camera features, etc. } if ([GULAppEnvironmentUtil isAppExtension]) { NSLog(@"Running in App Extension - limited capabilities"); // Avoid UIApplication access, background tasks } if ([GULAppEnvironmentUtil isAppClip]) { NSLog(@"Running as App Clip - streamlined experience"); // Show limited functionality } ``` -------------------------------- ### Checkout and Pull Main Branch Source: https://github.com/google/googleutilities/blob/main/README.md Ensures the local main branch is up-to-date before proceeding with release tasks. ```bash git checkout main git pull ``` -------------------------------- ### Add and Push CocoaPods Tag Source: https://github.com/google/googleutilities/blob/main/README.md Creates and pushes a Git tag for CocoaPods releases, ensuring version synchronization. ```bash git tag CocoaPods-{version} git push origin CocoaPods-{version} ``` -------------------------------- ### Push Podspec to CocoaPods Trunk Source: https://github.com/google/googleutilities/blob/main/README.md Publishes the specified version of the GoogleUtilities podspec to the CocoaPods trunk. Use the staging repository path for accuracy. ```bash pod trunk push ~/.cocoapods/repos/staging/GoogleUtilities/{version}/GoogleUtilities.podspec.json ``` -------------------------------- ### Generate CocoaPods workspace Source: https://github.com/google/googleutilities/blob/main/README.md Generates a local workspace for development using CocoaPods generate. ```bash pod gen GoogleUtilities.podspec --local-sources=./ --auto-open --platforms=ios ``` -------------------------------- ### Thread-Safe Background Access Source: https://context7.com/google/googleutilities/llms.txt Illustrates thread-safe reading and writing of user defaults from background threads using dispatch_async. This prevents notification crashes. ```objectivec dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ // Safe to read/write from background threads - no notification crash NSInteger count = [defaults integerForKey:@"backgroundCounter"]; [defaults setInteger:count + 1 forKey:@"backgroundCounter"]; }); ``` -------------------------------- ### Include APNS Methods in Swizzling Source: https://github.com/google/googleutilities/blob/main/GoogleUtilities/AppDelegateSwizzler/README.md To swizzle APNS-related App Delegate methods, use `proxyOriginalDelegateIncludingAPNSMethods`. This is necessary if your app uses push notifications and you need to intercept these specific methods. ```objc - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo; - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; ``` ```objc // [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods] ``` -------------------------------- ### Send Compressed JSON Data in Network Request Source: https://context7.com/google/googleutilities/llms.txt Serializes a dictionary to JSON, compresses it using gzip, and sends it via a POST request. Includes fallback to sending uncompressed data if compression fails and sets appropriate Content-Type and Content-Encoding headers. ```Objective-C // Example: Network request with compression @interface CompressedNetworkClient : NSObject @end @implementation CompressedNetworkClient - (void)sendCompressedJSON:(NSDictionary *)json toURL:(NSURL *)url { NSError *jsonError = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:json options:0 error:&jsonError]; if (jsonError) { NSLog(@"JSON serialization failed: %@", jsonError); return; } NSError *compressionError = nil; NSData *compressedData = [NSData gul_dataByGzippingData:jsonData error:&compressionError]; if (compressionError) { NSLog(@"Compression failed, sending uncompressed"); compressedData = jsonData; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; request.HTTPBody = compressedData; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; if (!compressionError) { [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"]; } NSLog(@"Sending %lu bytes (%@ compression)", (unsigned long)compressedData.length, compressionError ? @"no" : @"with"); // Send request... } @end ``` -------------------------------- ### Clean up SpecsStaging repository Source: https://github.com/google/googleutilities/blob/main/README.md Removes the GoogleUtilities directory from the SpecsStaging repository after a release. ```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 ``` -------------------------------- ### Compress and Decompress NSData with Gzip Source: https://context7.com/google/googleutilities/llms.txt Compresses NSData using gzip and then decompresses it. Handles potential compression and decompression errors, including specific error codes for data size issues and remaining bytes. ```Objective-C #import // Compress data for network transmission NSData *originalData = [@"This is a test string that will be compressed. " @"Repeated content compresses well. " @"Repeated content compresses well. " @"Repeated content compresses well." dataUsingEncoding:NSUTF8StringEncoding]; NSError *compressionError = nil; NSData *compressedData = [NSData gul_dataByGzippingData:originalData error:&compressionError]; if (compressionError) { NSLog(@"Compression failed: %@", compressionError.localizedDescription); // Check specific error types if (compressionError.code == GULNSDataZlibErrorGreaterThan32BitsToCompress) { NSLog(@"Data too large for compression (>32 bits on 64-bit systems)"); } else if (compressionError.code == GULNSDataZlibErrorInternal) { NSNumber *zlibError = compressionError.userInfo[GULNSDataZlibErrorKey]; NSLog(@"Internal zlib error: %@", zlibError); } } else { NSLog(@"Original size: %lu bytes", (unsigned long)originalData.length); NSLog(@"Compressed size: %lu bytes", (unsigned long)compressedData.length); NSLog(@"Compression ratio: %.1f%%", (1.0 - (float)compressedData.length / originalData.length) * 100); } // Decompress received data NSError *decompressionError = nil; NSData *decompressedData = [NSData gul_dataByInflatingGzippedData:compressedData error:&decompressionError]; if (decompressionError) { NSLog(@"Decompression failed: %@", decompressionError.localizedDescription); if (decompressionError.code == GULNSDataZlibErrorDataRemaining) { NSNumber *remaining = decompressionError.userInfo[GULNSDataZlibRemainingBytesKey]; NSLog(@"Warning: %@ bytes remaining after decompression", remaining); } } else { NSString *decompressedString = [[NSString alloc] initWithData:decompressedData encoding:NSUTF8StringEncoding]; NSLog(@"Decompressed: %@", decompressedString); } ``` -------------------------------- ### Update CocoaPods repository Source: https://github.com/google/googleutilities/blob/main/README.md Updates the local CocoaPods cache if it is out of date. ```bash pod repo update ``` -------------------------------- ### Force Debug Logging with GULLogger Source: https://context7.com/google/googleutilities/llms.txt Enable debug logging regardless of the configured level, which is particularly useful during development phases. ```Objective-C GULLoggerForceDebug(); ``` -------------------------------- ### Disable code signing for targets Source: https://github.com/google/googleutilities/blob/main/README.md Adds a user-defined setting to disable code signing requirements in build settings. ```text CODE_SIGNING_REQUIRED=NO ``` -------------------------------- ### Detect App Distribution Channel Source: https://context7.com/google/googleutilities/llms.txt Check if the app is distributed via the App Store, TestFlight, or a development build. This is useful for enabling/disabling features like analytics or verbose logging. ```Objective-C #import // Check distribution channel if ([GULAppEnvironmentUtil isFromAppStore]) { NSLog(@"Running from App Store - production environment"); // Disable verbose logging, enable analytics } else if ([GULAppEnvironmentUtil isAppStoreReceiptSandbox]) { NSLog(@"Running from TestFlight - beta environment"); // Enable beta features, moderate logging } else { NSLog(@"Development/sideloaded build"); // Enable all debug features } ``` -------------------------------- ### Register App Delegate Interceptor Source: https://github.com/google/googleutilities/blob/main/GoogleUtilities/AppDelegateSwizzler/README.md Proxy the original app delegate and register your custom interceptor using GULAppDelegateSwizzler. This ensures your interceptor receives app delegate method calls. ```objc // MYInterestingClass.m #import "GoogleUtilities/AppDelegateSwizzler/Public/GoogleUtilities/GULAppDelegateSwizzler.h" ... - (void)someInterestingMethod { ... // Calling this ensures that the app delegate is proxied (has no effect if some other SDK has // already done it). [GULAppDelegateSwizzler proxyOriginalDelegate]; MYAppDelegateInterceptor *interceptor = [MYAppDelegateInterceptor sharedInstance]; [GULAppDelegateSwizzler registerAppDelegateInterceptor:interceptor]; } ``` -------------------------------- ### Log Custom Messages with GULLoggerWrapper Source: https://context7.com/google/googleutilities/llms.txt Utilize GULLoggerWrapper for Objective-C contexts when dealing with va_list to log custom messages at a specified level. ```Objective-C // Using GULLoggerWrapper for Objective-C contexts with va_list va_list args; [GULLoggerWrapper logWithLevel:GULLoggerLevelInfo subsystem:kMySubsystem category:kMyLoggerService messageCode:@"I-SDK000006" message:@"Custom message" arguments:args]; ``` -------------------------------- ### Log Messages at Various Levels with GULLogger Source: https://context7.com/google/googleutilities/llms.txt Log messages using predefined macros for different severity levels. Message codes should follow the format I-XXX000000, where I indicates iOS, XXX is the service identifier, and the remaining digits form a unique message ID. ```Objective-C // Error logging - always shown GULOSLogError(kMySubsystem, kMyLoggerService, NO, @"I-SDK000001", @"Failed to initialize: %@", @"missing configuration"); // Warning logging GULOSLogWarning(kMySubsystem, kMyLoggerService, NO, @"I-SDK000002", @"Configuration value missing, using default: %@", @"timeout=30"); // Notice logging - default level GULOSLogNotice(kMySubsystem, kMyLoggerService, NO, @"I-SDK000003", @"SDK initialized successfully"); // Info logging GULOSLogInfo(kMySubsystem, kMyLoggerService, NO, @"I-SDK000004", @"Processing %lu items", (unsigned long)itemCount); // Debug logging - most verbose GULOSLogDebug(kMySubsystem, kMyLoggerService, NO, @"I-SDK000005", @"Request payload: %@", requestData); ``` -------------------------------- ### Intercept App Delegate Methods with GULAppDelegateSwizzler Source: https://context7.com/google/googleutilities/llms.txt Use GULAppDelegateSwizzler to intercept UIApplicationDelegate methods without modifying the AppDelegate. Ensure the original delegate is proxied before registering an interceptor. This is useful for handling deep links and remote notifications. ```objectivec // MyAppDelegateInterceptor.h #import @interface MyAppDelegateInterceptor : NSObject @property (nonatomic, strong) GULAppDelegateInterceptorID interceptorID; @end // MyAppDelegateInterceptor.m @implementation MyAppDelegateInterceptor - (void)startIntercepting { // Proxy the original delegate first (required before registering) [GULAppDelegateSwizzler proxyOriginalDelegate]; // For APNS methods (push notifications), use this instead: // [GULAppDelegateSwizzler proxyOriginalDelegateIncludingAPNSMethods]; // Register this object as an interceptor self.interceptorID = [GULAppDelegateSwizzler registerAppDelegateInterceptor:self]; if (self.interceptorID) { NSLog(@"Successfully registered interceptor: %@", self.interceptorID); } } - (void)stopIntercepting { if (self.interceptorID) { [GULAppDelegateSwizzler unregisterAppDelegateInterceptorWithID:self.interceptorID]; self.interceptorID = nil; } } // Intercepted delegate methods - called when original delegate receives them - (BOOL)application:(GULApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { NSLog(@"Intercepted URL: %@", url); // Handle deep links, custom URL schemes, etc. return YES; } - (void)application:(GULApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { NSLog(@"Intercepted remote notification: %@", userInfo); // Process push notification completionHandler(UIBackgroundFetchResultNewData); } @end // Usage in your SDK initialization MyAppDelegateInterceptor *interceptor = [[MyAppDelegateInterceptor alloc] init]; [interceptor startIntercepting]; // Check if proxy is enabled (can be disabled via Info.plist) if ([GULAppDelegateSwizzler isAppDelegateProxyEnabled]) { NSLog(@"App delegate swizzling is enabled"); } // Get shared application reference GULApplication *app = [GULAppDelegateSwizzler sharedApplication]; ``` -------------------------------- ### Intercept Scene Delegate Methods with GULSceneDelegateSwizzler Source: https://context7.com/google/googleutilities/llms.txt Use GULSceneDelegateSwizzler for intercepting UISceneDelegate methods on iOS 13+. Proxy scene delegates before registering an interceptor. This allows SDKs to handle scene-specific lifecycle events. ```objectivec #import @interface MySceneInterceptor : NSObject @property (nonatomic, strong) GULSceneDelegateInterceptorID interceptorID; @end @implementation MySceneInterceptor - (void)startIntercepting API_AVAILABLE(ios(13.0)) { // Proxy scene delegates first [GULSceneDelegateSwizzler proxyOriginalSceneDelegate]; // Register as scene delegate interceptor self.interceptorID = [GULSceneDelegateSwizzler registerSceneDelegateInterceptor:self]; } - (void)stopIntercepting API_AVAILABLE(ios(13.0)) { if (self.interceptorID) { [GULSceneDelegateSwizzler unregisterSceneDelegateInterceptorWithID:self.interceptorID]; } } // Intercepted scene delegate methods - (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts API_AVAILABLE(ios(13.0)) { for (UIOpenURLContext *context in URLContexts) { NSLog(@"Scene intercepted URL: %@", context.URL); } } - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) { NSLog(@"Scene connecting to session"); } @end // Check if scene delegate proxy is enabled if ([GULSceneDelegateSwizzler isSceneDelegateProxyEnabled]) { MySceneInterceptor *sceneInterceptor = [[MySceneInterceptor alloc] init]; if (@available(iOS 13.0, tvOS 13.0, *)) { [sceneInterceptor startIntercepting]; } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.