AppGroup.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "AppGroup.h"
  21. #import "DatabaseManager.h"
  22. #import "ValidationLogger.h"
  23. #define KEY_APP_GROUP_TYPE_APP @"AppGroupTypeApp"
  24. #define KEY_APP_GROUP_TYPE_SHARE_EXTENSION @"AppGroupTypeShareExtension"
  25. #define KEY_DID_MIGRATION_CHECK @"DefaultsKeyDidMigrationCheck"
  26. #define THREEMA_SHARE_EXTENSION_SUFFIX @"ShareExtension"
  27. #define THREEMA_APP_GROUP_NOTIFICATION_SUFFIX @".SyncAppGroup"
  28. #ifdef DEBUG
  29. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  30. #else
  31. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  32. #endif
  33. /*
  34. Tooling to determine if extension or app is running.
  35. E.g. when the share extension is triggered from within the app there is no other way to know that.
  36. */
  37. static NSString *appId;
  38. static NSString *groupId;
  39. static CFStringRef appSyncNotificationKey;
  40. @implementation AppGroup
  41. + (void)setAppId:(NSString *)newAppId {
  42. NSAssert(appId == nil || [appId isEqualToString:newAppId], @"cannot change appId at runtime!");
  43. appId = newAppId;
  44. appSyncNotificationKey = CFBridgingRetain([NSString stringWithFormat:@"%@%@", appId, THREEMA_APP_GROUP_NOTIFICATION_SUFFIX]);
  45. [self registerAppGroupSyncObserver];
  46. }
  47. + (void)setGroupId:(NSString *)newGroupId {
  48. NSAssert(groupId == nil || [groupId isEqualToString:newGroupId], @"cannot change groupId at runtime!");
  49. groupId = newGroupId;
  50. }
  51. + (NSString *)groupId {
  52. NSAssert(groupId != nil, @"groupId not set");
  53. return groupId;
  54. }
  55. + (void)setActive:(BOOL)active forType:(AppGroupType)type {
  56. NSAssert(appId != nil, @"appId not set, you need to set an id for the app or extension");
  57. NSUserDefaults *defaults = [self userDefaults];
  58. [defaults setBool:active forKey:[self keyForType:type]];
  59. [defaults synchronize];
  60. }
  61. + (AppGroupType)getActiveType {
  62. NSUserDefaults *defaults = [self userDefaults];
  63. BOOL isShareExtensionActive = [defaults boolForKey:KEY_APP_GROUP_TYPE_SHARE_EXTENSION];
  64. if (isShareExtensionActive) {
  65. return AppGroupTypeShareExtension;
  66. }
  67. else {
  68. return AppGroupTypeApp;
  69. }
  70. }
  71. + (AppGroupType)getCurrentType {
  72. NSBundle *mainBundle = [NSBundle mainBundle];
  73. if ([mainBundle.bundleIdentifier hasSuffix:THREEMA_SHARE_EXTENSION_SUFFIX]) {
  74. return AppGroupTypeShareExtension;
  75. } else {
  76. return AppGroupTypeApp;
  77. }
  78. }
  79. + (BOOL)amIActive {
  80. return [AppGroup getCurrentType] == [AppGroup getActiveType];
  81. }
  82. + (NSUserDefaults *)userDefaults {
  83. static NSUserDefaults *userDefaults;
  84. static dispatch_once_t onceToken;
  85. dispatch_once(&onceToken, ^{
  86. userDefaults = [[NSUserDefaults alloc] initWithSuiteName: [self groupId]];
  87. if ([userDefaults boolForKey:KEY_DID_MIGRATION_CHECK] == NO) {
  88. [self migrateDefaults: userDefaults];
  89. }
  90. });
  91. return userDefaults;
  92. }
  93. + (void)migrateDefaults:(NSUserDefaults *)userDefaults {
  94. NSUserDefaults *oldDefaults = [NSUserDefaults standardUserDefaults];
  95. for (NSString *key in oldDefaults.dictionaryRepresentation.keyEnumerator) {
  96. id object = [oldDefaults objectForKey:key];
  97. [userDefaults setObject:object forKey:key];
  98. }
  99. [userDefaults setBool:YES forKey:KEY_DID_MIGRATION_CHECK];
  100. [userDefaults synchronize];
  101. }
  102. + (void)resetUserDefaults {
  103. NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
  104. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
  105. }
  106. + (NSString *)keyForType:(AppGroupType)type {
  107. switch (type) {
  108. case AppGroupTypeApp:
  109. return KEY_APP_GROUP_TYPE_APP;
  110. case AppGroupTypeShareExtension:
  111. return KEY_APP_GROUP_TYPE_SHARE_EXTENSION;
  112. default:
  113. DDLogError(@"unknown AppGroupType %d", type);
  114. return KEY_APP_GROUP_TYPE_SHARE_EXTENSION;
  115. }
  116. }
  117. #pragma mark - inter app communication as seen in (https://developer.apple.com/videos/wwdc/2015/?id=224)
  118. static void observerCallback() {
  119. [[DatabaseManager dbManager] refreshDirtyObjects];
  120. };
  121. + (void)registerAppGroupSyncObserver {
  122. if ([AppGroup getCurrentType] != AppGroupTypeApp) {
  123. // otherwise notification gets consumed by extension
  124. return;
  125. }
  126. CFNotificationCenterRef nc = CFNotificationCenterGetDarwinNotifyCenter();
  127. CFNotificationCenterAddObserver(nc,
  128. NULL,
  129. (CFNotificationCallback)observerCallback,
  130. appSyncNotificationKey,
  131. NULL,
  132. CFNotificationSuspensionBehaviorCoalesce);
  133. }
  134. + (void)notifyAppGroupSyncNeeded {
  135. CFNotificationCenterRef nc = CFNotificationCenterGetDarwinNotifyCenter();
  136. CFNotificationCenterPostNotification(nc,
  137. appSyncNotificationKey,
  138. nil,
  139. nil,
  140. TRUE);
  141. }
  142. @end