mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			432 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Matlab
		
	
		
		
			
		
	
	
			432 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Matlab
		
	
| 
											8 years ago
										 | // | ||
| 
											7 years ago
										 | //  Copyright (c) 2019 Open Whisper Systems. All rights reserved. | ||
| 
											8 years ago
										 | // | ||
|  | 
 | ||
|  | #import "OWSPreferences.h" | ||
| 
											5 years ago
										 | #import <SessionMessagingKit/SSKEnvironment.h> | ||
|  | #import <SessionMessagingKit/YapDatabaseConnection+OWS.h> | ||
|  | #import <SessionMessagingKit/YapDatabaseTransaction+OWS.h> | ||
| 
											5 years ago
										 | #import <SessionUtilitiesKit/SessionUtilitiesKit.h> | ||
| 
											8 years ago
										 | 
 | ||
|  | NS_ASSUME_NONNULL_BEGIN | ||
|  | 
 | ||
| 
											7 years ago
										 | NSString *NSStringForNotificationType(NotificationType value) | ||
|  | { | ||
| 
											7 years ago
										 |     switch (value) { | ||
| 
											7 years ago
										 |         case NotificationNamePreview: | ||
|  |             return @"NotificationNamePreview"; | ||
|  |         case NotificationNameNoPreview: | ||
|  |             return @"NotificationNameNoPreview"; | ||
|  |         case NotificationNoNameNoPreview: | ||
|  |             return @"NotificationNoNameNoPreview"; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesSignalDatabaseCollection = @"SignalPreferences"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesCallLoggingDidChangeNotification = @"OWSPreferencesCallLoggingDidChangeNotification"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesKeyScreenSecurity = @"Screen Security Key"; | ||
|  | NSString *const OWSPreferencesKeyEnableDebugLog = @"Debugging Log Enabled Key"; | ||
|  | NSString *const OWSPreferencesKeyNotificationPreviewType = @"Notification Preview Type Key"; | ||
|  | NSString *const OWSPreferencesKeyHasSentAMessage = @"User has sent a message"; | ||
|  | NSString *const OWSPreferencesKeyPlaySoundInForeground = @"NotificationSoundInForeground"; | ||
|  | NSString *const OWSPreferencesKeyLastRecordedPushToken = @"LastRecordedPushToken"; | ||
|  | NSString *const OWSPreferencesKeyLastRecordedVoipToken = @"LastRecordedVoipToken"; | ||
|  | NSString *const OWSPreferencesKeyCallKitEnabled = @"CallKitEnabled"; | ||
|  | NSString *const OWSPreferencesKeyCallKitPrivacyEnabled = @"CallKitPrivacyEnabled"; | ||
|  | NSString *const OWSPreferencesKeyCallsHideIPAddress = @"CallsHideIPAddress"; | ||
|  | NSString *const OWSPreferencesKeyHasDeclinedNoContactsView = @"hasDeclinedNoContactsView"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesKeyHasGeneratedThumbnails = @"OWSPreferencesKeyHasGeneratedThumbnails"; | ||
| 
											7 years ago
										 | NSString *const OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators | ||
|  |     = @"OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesKeyIOSUpgradeNagDate = @"iOSUpgradeNagDate"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesKey_IsReadyForAppExtensions = @"isReadyForAppExtensions_5"; | ||
| 
											8 years ago
										 | NSString *const OWSPreferencesKeySystemCallLogEnabled = @"OWSPreferencesKeySystemCallLogEnabled"; | ||
| 
											8 years ago
										 | 
 | ||
|  | @implementation OWSPreferences | ||
|  | 
 | ||
|  | - (instancetype)init | ||
|  | { | ||
|  |     self = [super init]; | ||
|  |     if (!self) { | ||
|  |         return self; | ||
|  |     } | ||
|  | 
 | ||
|  |     return self; | ||
|  | } | ||
|  | 
 | ||
|  | #pragma mark - Helpers | ||
|  | 
 | ||
| 
											8 years ago
										 | - (void)clear | ||
|  | { | ||
| 
											8 years ago
										 |     [NSUserDefaults removeAll]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (nullable id)tryGetValueForKey:(NSString *)key | ||
|  | { | ||
| 
											8 years ago
										 |     __block id result; | ||
| 
											5 years ago
										 |     [LKStorage readWithBlock:^(YapDatabaseReadTransaction *transaction) { | ||
| 
											8 years ago
										 |         result = [self tryGetValueForKey:key transaction:transaction]; | ||
|  |     }]; | ||
|  |     return result; | ||
|  | } | ||
|  | 
 | ||
|  | - (nullable id)tryGetValueForKey:(NSString *)key transaction:(YapDatabaseReadTransaction *)transaction | ||
|  | { | ||
|  |     return [transaction objectForKey:key inCollection:OWSPreferencesSignalDatabaseCollection]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (void)setValueForKey:(NSString *)key toValue:(nullable id)value | ||
| 
											8 years ago
										 | { | ||
| 
											5 years ago
										 |     [LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { | ||
| 
											7 years ago
										 |         [self setValueForKey:key toValue:value transaction:transaction]; | ||
| 
											5 years ago
										 |     }]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (void)setValueForKey:(NSString *)key | ||
|  |                toValue:(nullable id)value | ||
|  |            transaction:(YapDatabaseReadWriteTransaction *)transaction | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     [transaction setObject:value forKey:key inCollection:OWSPreferencesSignalDatabaseCollection]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | #pragma mark - Specific Preferences | ||
|  | 
 | ||
| 
											8 years ago
										 | + (BOOL)isReadyForAppExtensions | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     NSNumber *preference = [NSUserDefaults.appUserDefaults objectForKey:OWSPreferencesKey_IsReadyForAppExtensions]; | ||
| 
											8 years ago
										 | 
 | ||
|  |     if (preference) { | ||
|  |         return [preference boolValue]; | ||
|  |     } else { | ||
|  |         return NO; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | + (void)setIsReadyForAppExtensions | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     [NSUserDefaults.appUserDefaults setObject:@(YES) forKey:OWSPreferencesKey_IsReadyForAppExtensions]; | ||
| 
											8 years ago
										 |     [NSUserDefaults.appUserDefaults synchronize]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (BOOL)screenSecurityIsEnabled | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyScreenSecurity]; | ||
|  |     return preference ? [preference boolValue] : YES; | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (void)setScreenSecurity:(BOOL)flag | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyScreenSecurity toValue:@(flag)]; | ||
|  | } | ||
|  | 
 | ||
| 
											7 years ago
										 | - (BOOL)hasSentAMessage | ||
| 
											8 years ago
										 | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyHasSentAMessage]; | ||
|  |     if (preference) { | ||
|  |         return [preference boolValue]; | ||
|  |     } else { | ||
|  |         return NO; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | + (BOOL)isLoggingEnabled | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     NSNumber *preference = [NSUserDefaults.appUserDefaults objectForKey:OWSPreferencesKeyEnableDebugLog]; | ||
| 
											8 years ago
										 | 
 | ||
|  |     if (preference) { | ||
|  |         return [preference boolValue]; | ||
|  |     } else { | ||
|  |         return YES; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | + (void)setIsLoggingEnabled:(BOOL)flag | ||
| 
											8 years ago
										 | { | ||
|  |     // Logging preferences are stored in UserDefaults instead of the database, so that we can (optionally) start | ||
|  |     // logging before the database is initialized. This is important because sometimes there are problems *with* the | ||
|  |     // database initialization, and without logging it would be hard to track down. | ||
| 
											8 years ago
										 |     [NSUserDefaults.appUserDefaults setObject:@(flag) forKey:OWSPreferencesKeyEnableDebugLog]; | ||
|  |     [NSUserDefaults.appUserDefaults synchronize]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (void)setHasSentAMessage:(BOOL)enabled | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyHasSentAMessage toValue:@(enabled)]; | ||
|  | } | ||
|  | 
 | ||
|  | - (BOOL)hasDeclinedNoContactsView | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyHasDeclinedNoContactsView]; | ||
|  |     // Default to NO. | ||
|  |     return preference ? [preference boolValue] : NO; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setHasDeclinedNoContactsView:(BOOL)value | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyHasDeclinedNoContactsView toValue:@(value)]; | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (BOOL)hasGeneratedThumbnails | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyHasGeneratedThumbnails]; | ||
|  |     // Default to NO. | ||
|  |     return preference ? [preference boolValue] : NO; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setHasGeneratedThumbnails:(BOOL)value | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyHasGeneratedThumbnails toValue:@(value)]; | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (void)setIOSUpgradeNagDate:(NSDate *)value | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     [self setValueForKey:OWSPreferencesKeyIOSUpgradeNagDate toValue:value]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (nullable NSDate *)iOSUpgradeNagDate | ||
| 
											8 years ago
										 | { | ||
| 
											8 years ago
										 |     return [self tryGetValueForKey:OWSPreferencesKeyIOSUpgradeNagDate]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											7 years ago
										 | - (BOOL)shouldShowUnidentifiedDeliveryIndicators | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators]; | ||
|  |     return preference ? [preference boolValue] : NO; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setShouldShowUnidentifiedDeliveryIndicators:(BOOL)value | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators toValue:@(value)]; | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | #pragma mark - Calling | ||
|  | 
 | ||
|  | #pragma mark CallKit | ||
|  | 
 | ||
| 
											8 years ago
										 | - (BOOL)isSystemCallLogEnabled | ||
|  | { | ||
|  |     if (@available(iOS 11, *)) { | ||
|  |         // do nothing | ||
|  |     } else { | ||
|  |         return NO; | ||
|  |     } | ||
|  | 
 | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeySystemCallLogEnabled]; | ||
| 
											8 years ago
										 |     return preference ? preference.boolValue : YES; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (void)setIsSystemCallLogEnabled:(BOOL)flag | ||
|  | { | ||
|  |     if (@available(iOS 11, *)) { | ||
|  |         // do nothing | ||
|  |     } else { | ||
|  |         return; | ||
|  |     } | ||
|  | 
 | ||
|  |     [self setValueForKey:OWSPreferencesKeySystemCallLogEnabled toValue:@(flag)]; | ||
|  | } | ||
|  | 
 | ||
|  | // In iOS 10.2.1, Apple fixed a bug wherein call history was backed up to iCloud. | ||
|  | // | ||
|  | // See: https://support.apple.com/en-us/HT207482 | ||
|  | // | ||
|  | // In iOS 11, Apple introduced a property CXProviderConfiguration.includesCallsInRecents | ||
|  | // that allows us to prevent Signal calls made with CallKit from showing up in the device's | ||
|  | // call history. | ||
|  | // | ||
|  | // Therefore in versions of iOS after 11, we have no need of call privacy. | ||
|  | #pragma mark Legacy CallKit | ||
|  | 
 | ||
| 
											8 years ago
										 | // Be a little conservative with system call logging with legacy users, even though it's | ||
|  | // not synced to iCloud, users could be concerned to suddenly see caller names in their | ||
|  | // recent calls list. | ||
|  | - (void)applyCallLoggingSettingsForLegacyUsersWithTransaction:(YapDatabaseReadWriteTransaction *)transaction | ||
|  | { | ||
|  |     NSNumber *_Nullable callKitPreference = | ||
|  |         [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled transaction:transaction]; | ||
|  |     BOOL wasUsingCallKit = callKitPreference ? [callKitPreference boolValue] : YES; | ||
|  | 
 | ||
|  |     NSNumber *_Nullable callKitPrivacyPreference = | ||
|  |         [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled transaction:transaction]; | ||
|  |     BOOL wasUsingCallKitPrivacy = callKitPrivacyPreference ? callKitPrivacyPreference.boolValue : YES; | ||
|  | 
 | ||
|  |     BOOL shouldLogCallsInRecents = ^{ | ||
|  |         if (wasUsingCallKit && !wasUsingCallKitPrivacy) { | ||
|  |             // User was using CallKit and explicitly opted in to showing names/numbers, | ||
|  |             // so it's OK to continue to show names/numbers in the system recents list. | ||
|  |             return YES; | ||
|  |         } else { | ||
|  |             // User was not previously showing names/numbers in the system | ||
|  |             // recents list, so don't opt them in. | ||
|  |             return NO; | ||
|  |         } | ||
|  |     }(); | ||
|  | 
 | ||
|  |     [self setValueForKey:OWSPreferencesKeySystemCallLogEnabled | ||
|  |                  toValue:@(shouldLogCallsInRecents) | ||
|  |              transaction:transaction]; | ||
| 
											8 years ago
										 |      | ||
|  |     // We need to reload the callService.callUIAdapter here, but SignalMessaging doesn't know about CallService, so we use | ||
|  |     // notifications to decouple the code. This is admittedly awkward, but it only happens once, and the alternative would | ||
|  |     // be importing all the call related classes into SignalMessaging. | ||
|  |     [[NSNotificationCenter defaultCenter] postNotificationNameAsync:OWSPreferencesCallLoggingDidChangeNotification object:nil]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (BOOL)isCallKitEnabled | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
| 
											8 years ago
										 |         return YES; | ||
| 
											8 years ago
										 |     } | ||
|  | 
 | ||
| 
											8 years ago
										 |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled]; | ||
|  |     return preference ? [preference boolValue] : YES; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setIsCallKitEnabled:(BOOL)flag | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
|  |         return; | ||
|  |     } | ||
|  | 
 | ||
| 
											8 years ago
										 |     [self setValueForKey:OWSPreferencesKeyCallKitEnabled toValue:@(flag)]; | ||
| 
											8 years ago
										 |     // Rev callUIAdaptee to get new setting | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (BOOL)isCallKitEnabledSet | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
| 
											8 years ago
										 |         return NO; | ||
|  |     } | ||
| 
											8 years ago
										 | 
 | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitEnabled]; | ||
|  |     return preference != nil; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (BOOL)isCallKitPrivacyEnabled | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
| 
											8 years ago
										 |         return NO; | ||
|  |     } | ||
| 
											8 years ago
										 | 
 | ||
| 
											8 years ago
										 |     NSNumber *_Nullable preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled]; | ||
|  |     if (preference) { | ||
|  |         return [preference boolValue]; | ||
|  |     } else { | ||
|  |         // Private by default. | ||
|  |         return YES; | ||
|  |     } | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
|  | - (void)setIsCallKitPrivacyEnabled:(BOOL)flag | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
|  |         return; | ||
|  |     } | ||
|  | 
 | ||
| 
											8 years ago
										 |     [self setValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled toValue:@(flag)]; | ||
|  | } | ||
|  | 
 | ||
|  | - (BOOL)isCallKitPrivacySet | ||
|  | { | ||
| 
											8 years ago
										 |     if (@available(iOS 11, *)) { | ||
|  |         return NO; | ||
|  |     } | ||
|  | 
 | ||
| 
											8 years ago
										 |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallKitPrivacyEnabled]; | ||
|  |     return preference != nil; | ||
|  | } | ||
|  | 
 | ||
|  | #pragma mark direct call connectivity (non-TURN) | ||
|  | 
 | ||
|  | // Allow callers to connect directly, when desirable, vs. enforcing TURN only proxy connectivity | ||
|  | 
 | ||
|  | - (BOOL)doCallsHideIPAddress | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyCallsHideIPAddress]; | ||
|  |     return preference ? [preference boolValue] : NO; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setDoCallsHideIPAddress:(BOOL)flag | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyCallsHideIPAddress toValue:@(flag)]; | ||
|  | } | ||
|  | 
 | ||
|  | #pragma mark Notification Preferences | ||
|  | 
 | ||
|  | - (BOOL)soundInForeground | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyPlaySoundInForeground]; | ||
|  |     if (preference) { | ||
|  |         return [preference boolValue]; | ||
|  |     } else { | ||
|  |         return YES; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setSoundInForeground:(BOOL)enabled | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyPlaySoundInForeground toValue:@(enabled)]; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setNotificationPreviewType:(NotificationType)type | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyNotificationPreviewType toValue:@(type)]; | ||
|  | } | ||
|  | 
 | ||
|  | - (NotificationType)notificationPreviewType | ||
|  | { | ||
|  |     NSNumber *preference = [self tryGetValueForKey:OWSPreferencesKeyNotificationPreviewType]; | ||
|  | 
 | ||
|  |     if (preference) { | ||
|  |         return [preference unsignedIntegerValue]; | ||
|  |     } else { | ||
|  |         return NotificationNamePreview; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | - (NSString *)nameForNotificationPreviewType:(NotificationType)notificationType | ||
|  | { | ||
|  |     switch (notificationType) { | ||
|  |         case NotificationNamePreview: | ||
|  |             return NSLocalizedString(@"NOTIFICATIONS_SENDER_AND_MESSAGE", nil); | ||
|  |         case NotificationNameNoPreview: | ||
|  |             return NSLocalizedString(@"NOTIFICATIONS_SENDER_ONLY", nil); | ||
|  |         case NotificationNoNameNoPreview: | ||
|  |             return NSLocalizedString(@"NOTIFICATIONS_NONE", nil); | ||
|  |         default: | ||
|  |             return @""; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | #pragma mark - Push Tokens | ||
|  | 
 | ||
|  | - (void)setPushToken:(NSString *)value | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyLastRecordedPushToken toValue:value]; | ||
|  | } | ||
|  | 
 | ||
|  | - (nullable NSString *)getPushToken | ||
|  | { | ||
|  |     return [self tryGetValueForKey:OWSPreferencesKeyLastRecordedPushToken]; | ||
|  | } | ||
|  | 
 | ||
|  | - (void)setVoipToken:(NSString *)value | ||
|  | { | ||
|  |     [self setValueForKey:OWSPreferencesKeyLastRecordedVoipToken toValue:value]; | ||
|  | } | ||
|  | 
 | ||
|  | - (nullable NSString *)getVoipToken | ||
|  | { | ||
|  |     return [self tryGetValueForKey:OWSPreferencesKeyLastRecordedVoipToken]; | ||
|  | } | ||
|  | 
 | ||
| 
											8 years ago
										 | - (void)unsetRecordedAPNSTokens | ||
|  | { | ||
| 
											8 years ago
										 |     [self setValueForKey:OWSPreferencesKeyLastRecordedPushToken toValue:nil]; | ||
|  |     [self setValueForKey:OWSPreferencesKeyLastRecordedVoipToken toValue:nil]; | ||
| 
											8 years ago
										 | } | ||
|  | 
 | ||
| 
											8 years ago
										 | @end | ||
|  | 
 | ||
|  | NS_ASSUME_NONNULL_END |