EntityManager.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2014-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 "EntityManager.h"
  21. #import "EntityCreator.h"
  22. #import "EntityFetcher.h"
  23. #import "ErrorHandler.h"
  24. #import "DatabaseManager.h"
  25. #import "BackgroundTaskManagerProxy.h"
  26. #import "Utils.h"
  27. #ifdef DEBUG
  28. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  29. #else
  30. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  31. #endif
  32. @implementation EntityManager {
  33. DatabaseContext *dbContext;
  34. }
  35. - (instancetype)init
  36. {
  37. return [self initForBackgroundProcess:NO];
  38. }
  39. - (instancetype)initForBackgroundProcess:(BOOL)forBackgroundProcess
  40. {
  41. self = [super init];
  42. if (self) {
  43. dbContext = [[DatabaseManager dbManager] getDatabaseContext:forBackgroundProcess];
  44. _entityCreator = [[EntityCreator alloc] initWith:dbContext.current];
  45. _entityFetcher = [[EntityFetcher alloc] initWith:dbContext.current];
  46. _entityDestroyer = [[EntityDestroyer alloc] initWithManagedObjectContext:dbContext.current];
  47. }
  48. return self;
  49. }
  50. - (void)refreshObject:(NSManagedObject *)object
  51. mergeChanges:(BOOL)flag {
  52. if (object != nil) {
  53. [self performBlockAndWait:^{
  54. [dbContext.current refreshObject:object mergeChanges:flag];
  55. }];
  56. }
  57. }
  58. - (instancetype)initWithDatabaseContext:(DatabaseContext *)context
  59. {
  60. self = [super init];
  61. if (self) {
  62. dbContext = context;
  63. _entityCreator = [[EntityCreator alloc] initWith:dbContext.current];
  64. _entityFetcher = [[EntityFetcher alloc] initWith:dbContext.current];
  65. _entityDestroyer = [[EntityDestroyer alloc] initWithManagedObjectContext:dbContext.current];
  66. }
  67. return self;
  68. }
  69. - (void)performBlockAndWait:(void (^)(void))block {
  70. [dbContext.current performBlockAndWait:^{
  71. if (block) {
  72. block();
  73. }
  74. }];
  75. }
  76. - (void)performBlock:(void (^)(void))block {
  77. [dbContext.current performBlock:^{
  78. if (block) {
  79. block();
  80. }
  81. }];
  82. }
  83. - (void)performAsyncBlockAndSafe:(void (^)(void))block {
  84. // saves always on main queue
  85. NSString *identifier = [BackgroundTaskManagerProxy counterWithIdentifier:kAppCoreDataSaveBackgroundTask];
  86. [BackgroundTaskManagerProxy newBackgroundTaskWithKey:identifier timeout:kAppCoreDataSaveBackgroundTaskTime completionHandler:nil];
  87. dispatch_async(dispatch_get_main_queue(), ^{
  88. [dbContext.current performBlock:^{
  89. [self internalPerformWithIdentifier:identifier blockAndSave:block];
  90. }];
  91. });
  92. }
  93. - (void)performSyncBlockAndSafe:(void (^)(void))block {
  94. // saves always on main queue
  95. NSString *identifier = [BackgroundTaskManagerProxy counterWithIdentifier:kAppCoreDataSaveBackgroundTask];
  96. [BackgroundTaskManagerProxy newBackgroundTaskWithKey:identifier timeout:kAppCoreDataSaveBackgroundTaskTime completionHandler:nil];
  97. if ([self isMainQueue]) {
  98. [dbContext.current performBlockAndWait:^{
  99. [self internalPerformWithIdentifier:identifier blockAndSave:block];
  100. }];
  101. } else {
  102. dispatch_sync(dispatch_get_main_queue(), ^{
  103. [dbContext.current performBlockAndWait:^{
  104. [self internalPerformWithIdentifier:identifier blockAndSave:block];
  105. }];
  106. });
  107. }
  108. }
  109. - (BOOL)isMainQueue {
  110. return [[NSOperationQueue currentQueue] underlyingQueue] == dispatch_get_main_queue();
  111. }
  112. - (void)internalPerformWithIdentifier:(NSString *)identifier blockAndSave:(void (^)(void))block {
  113. if (block) {
  114. block();
  115. }
  116. if ([dbContext.current hasChanges]) {
  117. [self internalSave:^{
  118. [BackgroundTaskManagerProxy cancelBackgroundTaskWithKey:identifier];
  119. }];
  120. } else {
  121. [BackgroundTaskManagerProxy cancelBackgroundTaskWithKey:identifier];
  122. }
  123. }
  124. - (void)internalSave:(void (^)(void))onCompletion {
  125. if (ddLogLevel == DDLogLevelVerbose) {
  126. DDLogVerbose(@"inserted objects: %@", [dbContext.current insertedObjects]);
  127. DDLogVerbose(@"updated objects: %@", [dbContext.current updatedObjects]);
  128. DDLogVerbose(@"deleted objects: %@", [dbContext.current deletedObjects]);
  129. }
  130. NSError *error = nil;
  131. if (![dbContext.current save:&error]) {
  132. DDLogError(@"Error saving context %@, %@", error, [error userInfo]);
  133. [ErrorHandler abortWithError: error];
  134. if (onCompletion != nil) {
  135. onCompletion();
  136. }
  137. }
  138. else if ([dbContext.current parentContext] != nil) {
  139. // asynchronously save parent context (changes were pushed by save in child context)
  140. [dbContext.main performBlock:^{
  141. NSError *error = nil;
  142. if (![dbContext.main save:&error]) {
  143. DDLogError(@"Error saving context %@, %@", error, [error userInfo]);
  144. [ErrorHandler abortWithError: error];
  145. if (onCompletion != nil) {
  146. onCompletion();
  147. }
  148. }
  149. if (onCompletion != nil) {
  150. onCompletion();
  151. }
  152. }];
  153. }
  154. else {
  155. if (onCompletion != nil) {
  156. onCompletion();
  157. }
  158. }
  159. }
  160. - (void)rollback {
  161. [dbContext.current rollback];
  162. }
  163. - (Conversation *)conversationForContact:(Contact *)contact createIfNotExisting:(BOOL) create {
  164. Conversation *conversation = [[self entityFetcher] conversationForContact:contact];
  165. if (create && conversation == nil) {
  166. conversation = [self entityCreator].conversation;
  167. conversation.contact = contact;
  168. if (![Utils hideThreemaTypeIconForContact:contact]) {
  169. // add work info as first message
  170. SystemMessage *systemMessage = [self.entityCreator systemMessageForConversation:conversation];
  171. systemMessage.type = [NSNumber numberWithInteger:kSystemMessageContactOtherAppInfo];
  172. systemMessage.remoteSentDate = [NSDate date];
  173. }
  174. }
  175. return conversation;
  176. }
  177. @end