123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2015-2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- #import "AppGroup.h"
- #import "DatabaseManager.h"
- #import "ValidationLogger.h"
- #define KEY_APP_GROUP_TYPE_APP @"AppGroupTypeApp"
- #define KEY_APP_GROUP_TYPE_SHARE_EXTENSION @"AppGroupTypeShareExtension"
- #define KEY_DID_MIGRATION_CHECK @"DefaultsKeyDidMigrationCheck"
- #define THREEMA_SHARE_EXTENSION_SUFFIX @"ShareExtension"
- #define THREEMA_APP_GROUP_NOTIFICATION_SUFFIX @".SyncAppGroup"
- #ifdef DEBUG
- static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
- #else
- static const DDLogLevel ddLogLevel = DDLogLevelWarning;
- #endif
- /*
- Tooling to determine if extension or app is running.
- E.g. when the share extension is triggered from within the app there is no other way to know that.
- */
- static NSString *appId;
- static NSString *groupId;
- static CFStringRef appSyncNotificationKey;
- @implementation AppGroup
- + (void)setAppId:(NSString *)newAppId {
- NSAssert(appId == nil || [appId isEqualToString:newAppId], @"cannot change appId at runtime!");
-
- appId = newAppId;
- appSyncNotificationKey = CFBridgingRetain([NSString stringWithFormat:@"%@%@", appId, THREEMA_APP_GROUP_NOTIFICATION_SUFFIX]);
- [self registerAppGroupSyncObserver];
- }
- + (void)setGroupId:(NSString *)newGroupId {
- NSAssert(groupId == nil || [groupId isEqualToString:newGroupId], @"cannot change groupId at runtime!");
-
- groupId = newGroupId;
- }
- + (NSString *)groupId {
- NSAssert(groupId != nil, @"groupId not set");
-
- return groupId;
- }
- + (void)setActive:(BOOL)active forType:(AppGroupType)type {
- NSAssert(appId != nil, @"appId not set, you need to set an id for the app or extension");
- NSUserDefaults *defaults = [self userDefaults];
- [defaults setBool:active forKey:[self keyForType:type]];
- [defaults synchronize];
- }
- + (AppGroupType)getActiveType {
- NSUserDefaults *defaults = [self userDefaults];
-
- BOOL isShareExtensionActive = [defaults boolForKey:KEY_APP_GROUP_TYPE_SHARE_EXTENSION];
- if (isShareExtensionActive) {
- return AppGroupTypeShareExtension;
- }
- else {
- return AppGroupTypeApp;
- }
- }
- + (AppGroupType)getCurrentType {
- NSBundle *mainBundle = [NSBundle mainBundle];
-
- if ([mainBundle.bundleIdentifier hasSuffix:THREEMA_SHARE_EXTENSION_SUFFIX]) {
- return AppGroupTypeShareExtension;
- } else {
- return AppGroupTypeApp;
- }
- }
- + (BOOL)amIActive {
- return [AppGroup getCurrentType] == [AppGroup getActiveType];
- }
- + (NSUserDefaults *)userDefaults {
- static NSUserDefaults *userDefaults;
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- userDefaults = [[NSUserDefaults alloc] initWithSuiteName: [self groupId]];
- if ([userDefaults boolForKey:KEY_DID_MIGRATION_CHECK] == NO) {
- [self migrateDefaults: userDefaults];
- }
- });
-
- return userDefaults;
- }
- + (void)migrateDefaults:(NSUserDefaults *)userDefaults {
- NSUserDefaults *oldDefaults = [NSUserDefaults standardUserDefaults];
-
- for (NSString *key in oldDefaults.dictionaryRepresentation.keyEnumerator) {
- id object = [oldDefaults objectForKey:key];
-
- [userDefaults setObject:object forKey:key];
- }
-
- [userDefaults setBool:YES forKey:KEY_DID_MIGRATION_CHECK];
- [userDefaults synchronize];
- }
- + (void)resetUserDefaults {
- NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
- [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
- }
- + (NSString *)keyForType:(AppGroupType)type {
- switch (type) {
- case AppGroupTypeApp:
- return KEY_APP_GROUP_TYPE_APP;
-
- case AppGroupTypeShareExtension:
- return KEY_APP_GROUP_TYPE_SHARE_EXTENSION;
-
- default:
- DDLogError(@"unknown AppGroupType %d", type);
- return KEY_APP_GROUP_TYPE_SHARE_EXTENSION;
- }
- }
- #pragma mark - inter app communication as seen in (https://developer.apple.com/videos/wwdc/2015/?id=224)
- static void observerCallback() {
- [[DatabaseManager dbManager] refreshDirtyObjects];
- };
- + (void)registerAppGroupSyncObserver {
- if ([AppGroup getCurrentType] != AppGroupTypeApp) {
- // otherwise notification gets consumed by extension
- return;
- }
-
- CFNotificationCenterRef nc = CFNotificationCenterGetDarwinNotifyCenter();
- CFNotificationCenterAddObserver(nc,
- NULL,
- (CFNotificationCallback)observerCallback,
- appSyncNotificationKey,
- NULL,
- CFNotificationSuspensionBehaviorCoalesce);
- }
- + (void)notifyAppGroupSyncNeeded {
- CFNotificationCenterRef nc = CFNotificationCenterGetDarwinNotifyCenter();
- CFNotificationCenterPostNotification(nc,
- appSyncNotificationKey,
- nil,
- nil,
- TRUE);
- }
- @end
|