Merge branch 'charlesmchen/dontResurrectZombies'

This commit is contained in:
Matthew Chen 2017-11-15 13:27:40 -05:00
commit adca3f5d18
38 changed files with 317 additions and 288 deletions

View file

@ -30,9 +30,9 @@ class SessionResetJob: NSObject {
self.storageManager.deleteAllSessions(forContact: self.recipientId)
DispatchQueue.main.async {
let endSessionMessage = EndSessionMessage(timestamp:NSDate.ows_millisecondTimeStamp(), in: self.thread)
let endSessionMessage = EndSessionMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: self.thread)
self.messageSender.send(endSessionMessage, success: {
self.messageSender.enqueue(endSessionMessage, success: {
Logger.info("\(self.TAG) successfully sent EndSession<essage.")
let message = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(),
in: self.thread,

View file

@ -12,7 +12,7 @@ public extension MessageSender {
*/
public func sendPromise(message: TSOutgoingMessage) -> Promise<Void> {
let promise: Promise<Void> = Promise { fulfill, reject in
self.send(message, success: fulfill, failure: reject)
self.enqueue(message, success: fulfill, failure: reject)
}
// Ensure sends complete before they're GC'd.

View file

@ -43,8 +43,8 @@ import PromiseKit
identityManager: self.identityManager,
profileManager: self.profileManager)
let dataSource = DataSourceValue.dataSource(withSyncMessage:syncContactsMessage.buildPlainTextAttachmentData())
self.messageSender.sendTemporaryAttachmentData(dataSource,
let dataSource = DataSourceValue.dataSource(withSyncMessage: syncContactsMessage.buildPlainTextAttachmentData())
self.messageSender.enqueueTemporaryAttachment(dataSource,
contentType: OWSMimeTypeApplicationOctetStream,
in: syncContactsMessage,
success: {

View file

@ -1465,7 +1465,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
return;
}
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent profile key message to thread: %@", self.logTag, thread);
[OWSProfileManager.sharedManager addThreadToProfileWhitelist:thread];

View file

@ -1654,7 +1654,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
[UIAlertAction actionWithTitle:NSLocalizedString(@"SEND_AGAIN_BUTTON", @"")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully resent failed message.", self.logTag);
}
@ -3391,7 +3391,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
if (newGroupModel.groupImage) {
NSData *data = UIImagePNGRepresentation(newGroupModel.groupImage);
DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
[self.messageSender enqueueAttachment:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
@ -3405,7 +3405,7 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
DDLogError(@"%@ Failed to send group avatar update with error: %@", self.logTag, error);
}];
} else {
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogDebug(@"%@ Successfully sent group update", self.logTag);
if (successCompletion) {

View file

@ -219,7 +219,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSyncGroupsRequestMessage *syncGroupsRequestMessage =
[[OWSSyncGroupsRequestMessage alloc] initWithThread:thread
groupId:[Randomness generateRandomBytes:16]];
[[Environment getCurrent].messageSender sendMessage:syncGroupsRequestMessage
[[Environment getCurrent].messageSender enqueueMessage:syncGroupsRequestMessage
success:^{
DDLogWarn(@"%@ Successfully sent Request Group Info message.", self.logTag);
}
@ -1055,7 +1055,6 @@ NS_ASSUME_NONNULL_BEGIN
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread
groupMetaMessage:TSGroupMessageNew];
// This will save the message.
[message updateWithCustomMessage:NSLocalizedString(@"GROUP_CREATED", nil)];
OWSMessageSender *messageSender = [Environment getCurrent].messageSender;
@ -1067,11 +1066,11 @@ NS_ASSUME_NONNULL_BEGIN
});
});
};
[messageSender sendMessage:message
success:completion
failure:^(NSError *error) {
completion();
}];
[messageSender enqueueMessage:message
success:completion
failure:^(NSError *error) {
completion();
}];
}
+ (void)injectFakeIncomingMessages:(int)counter thread:(TSThread *)thread

View file

@ -437,7 +437,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(message);
OWSMessageSender *messageSender = [Environment getCurrent].messageSender;
[messageSender sendMessage:message
[messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent message.", self.logTag);
}

View file

@ -526,7 +526,11 @@ class ExperienceUpgradesPageViewController: OWSViewController, UIPageViewControl
}
public func addViewController(experienceUpgrade: ExperienceUpgrade) {
guard let identifier = ExperienceUpgradeId(rawValue: experienceUpgrade.uniqueId) else {
guard let uniqueId = experienceUpgrade.uniqueId else {
Logger.error("\(self.TAG) experienceUpgrade is missing uniqueId.")
return
}
guard let identifier = ExperienceUpgradeId(rawValue: uniqueId) else {
owsFail("\(TAG) unknown experience upgrade. skipping")
return
}

View file

@ -679,7 +679,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread
groupMetaMessage:TSGroupMessageQuit];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
[self dismissViewControllerAnimated:YES
completion:^{

View file

@ -551,7 +551,11 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate {
AssertIsOnMainThread()
self.databaseConnection.read { transaction in
guard let newMessage = TSInteraction.fetch(uniqueId: self.message.uniqueId, transaction: transaction) as? TSMessage else {
guard let uniqueId = self.message.uniqueId else {
Logger.error("\(self.TAG) Message is missing uniqueId.")
return
}
guard let newMessage = TSInteraction.fetch(uniqueId: uniqueId, transaction: transaction) as? TSMessage else {
Logger.error("\(self.TAG) Couldn't reload message.")
return
}
@ -564,7 +568,11 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate {
let notifications = self.databaseConnection.beginLongLivedReadTransaction()
guard self.databaseConnection.hasChange(forKey: message.uniqueId,
guard let uniqueId = self.message.uniqueId else {
Logger.error("\(self.TAG) Message is missing uniqueId.")
return
}
guard self.databaseConnection.hasChange(forKey: uniqueId,
inCollection: TSInteraction.collection(),
in: notifications) else {
Logger.debug("\(TAG) No relevant changes.")

View file

@ -493,7 +493,6 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68;
inThread:thread
groupMetaMessage:TSGroupMessageNew];
// This will save the message.
[message updateWithCustomMessage:NSLocalizedString(@"GROUP_CREATED", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@ -501,14 +500,14 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68;
NSData *data = UIImagePNGRepresentation(model.groupImage);
DataSource *_Nullable dataSource =
[DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
success:successHandler
failure:failureHandler];
[self.messageSender enqueueAttachment:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
success:successHandler
failure:failureHandler];
} else {
[self.messageSender sendMessage:message success:successHandler failure:failureHandler];
[self.messageSender enqueueMessage:message success:successHandler failure:failureHandler];
}
});
}];

View file

@ -885,7 +885,7 @@ NS_ASSUME_NONNULL_BEGIN
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:gThread
groupMetaMessage:TSGroupMessageQuit];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully left group.", self.logTag);
}

View file

@ -59,7 +59,7 @@ class ExperienceUpgradeFinder: NSObject {
// MARK: - Instance Methods
public func allUnseen(transaction: YapDatabaseReadTransaction) -> [ExperienceUpgrade] {
return allExperienceUpgrades.filter { ExperienceUpgrade.fetch(uniqueId: $0.uniqueId, transaction: transaction) == nil }
return allExperienceUpgrades.filter { ExperienceUpgrade.fetch(uniqueId: $0.uniqueId!, transaction: transaction) == nil }
}
public func markAllAsSeen(transaction: YapDatabaseReadWriteTransaction) {

View file

@ -16,7 +16,7 @@ class ExperienceUpgrade: TSYapDatabaseObject {
super.init(uniqueId: uniqueId)
}
override required init(uniqueId: String) {
override required init(uniqueId: String?) {
// This is the unfortunate seam between strict swift and fast-and-loose objc
// we can't leave these properties nil, since we really "don't know" that the superclass
// will assign them.

View file

@ -115,7 +115,7 @@ NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey =
DataSource *dataSource =
[DataSourceValue dataSourceWithSyncMessage:[syncContactsMessage buildPlainTextAttachmentData]];
[self.messageSender sendTemporaryAttachmentData:dataSource
[self.messageSender enqueueTemporaryAttachment:dataSource
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncContactsMessage
success:^{

View file

@ -86,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:[NSMutableArray new]
expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)];
[messageSender sendMessage:message success:successHandler failure:failureHandler];
[messageSender enqueueMessage:message success:successHandler failure:failureHandler];
return message;
}
@ -117,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN
inThread:thread
isVoiceMessage:[attachment isVoiceMessage]
expiresInSeconds:(configuration.isEnabled ? configuration.durationSeconds : 0)];
[messageSender sendAttachmentData:attachment.dataSource
[messageSender enqueueAttachment:attachment.dataSource
contentType:attachment.mimeType
sourceFilename:attachment.filenameOrDefault
inMessage:message

View file

@ -27,7 +27,7 @@ const uint32_t OWSDisappearingMessagesConfigurationDefaultExpirationDuration = k
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
}
- (instancetype)initWithCoder:(NSCoder *)coder
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];

View file

@ -156,27 +156,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (atomic, readonly) BOOL isMuted;
@property (atomic, readonly, nullable) NSDate *mutedUntilDate;
// This model may be updated from many threads. We don't want to save
// our local copy (this instance) since it may be out of date. Instead, we
// use these "updateWith..." methods to:
//
// a) Update a property of this instance.
// b) Load an up-to-date instance of this model from from the data store.
// c) Update and save that fresh instance.
// d) If this instance hasn't yet been saved, save this local instance.
//
// After "updateWith...":
//
// a) An updated copy of this instance will always have been saved in the
// data store.
// b) The local property on this instance will always have been updated.
// c) Other properties on this instance may be out of date.
//
// All mutable properties of this class have been made read-only to
// prevent accidentally modifying them directly.
//
// This isn't a perfect arrangement, but in practice this will prevent
// data loss and will resolve all known issues.
#pragma mark - Update With... Methods
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate;
@end

View file

@ -34,7 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
return @"TSThread";
}
- (instancetype)initWithUniqueId:(NSString *)uniqueId {
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId
{
self = [super initWithUniqueId:uniqueId];
if (self) {
@ -382,33 +383,15 @@ NS_ASSUME_NONNULL_BEGIN
[mutedUntilDate timeIntervalSinceDate:now] > 0);
}
// This method does the work for the "updateWith..." methods. Please see
// the header for a discussion of those methods.
- (void)applyChangeToSelfAndLatestThread:(YapDatabaseReadWriteTransaction *)transaction
changeBlock:(void (^)(TSThread *))changeBlock
{
OWSAssert(transaction);
changeBlock(self);
NSString *collection = [[self class] collection];
TSThread *latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestInstance) {
changeBlock(latestInstance);
[latestInstance saveWithTransaction:transaction];
} else {
// This message has not yet been saved.
[self saveWithTransaction:transaction];
}
}
#pragma mark - Update With... Methods
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestThread:transaction
changeBlock:^(TSThread *thread) {
[thread setMutedUntilDate:mutedUntilDate];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSThread *thread) {
[thread setMutedUntilDate:mutedUntilDate];
}];
}];
}

View file

@ -109,6 +109,7 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
[outgoingMessage saveWithTransaction:transaction];
[outgoingMessage updateWithWasSentFromLinkedDeviceWithTransaction:transaction];
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager];
@ -138,6 +139,7 @@ NS_ASSUME_NONNULL_BEGIN
expiresInSeconds:transcript.expirationDuration
expireStartedAt:transcript.expirationStartedAt];
// Since textMessage is a new message, updateWithWasSentAndDelivered will save it.
[textMessage saveWithTransaction:transaction];
[textMessage updateWithWasSentFromLinkedDeviceWithTransaction:transaction];
}
}

View file

@ -15,9 +15,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface TSMessage : TSInteraction
@property (nonatomic, readonly) NSMutableArray<NSString *> *attachmentIds;
@property (nullable, nonatomic) NSString *body;
@property (nonatomic) uint32_t expiresInSeconds;
@property (nonatomic) uint64_t expireStartedAt;
@property (nonatomic, readonly, nullable) NSString *body;
@property (nonatomic, readonly) uint32_t expiresInSeconds;
@property (nonatomic, readonly) uint64_t expireStartedAt;
@property (nonatomic, readonly) uint64_t expiresAt;
@property (nonatomic, readonly) BOOL isExpiringMessage;
@ -56,6 +56,10 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)shouldStartExpireTimer;
- (BOOL)shouldStartExpireTimer:(YapDatabaseReadTransaction *)transaction;
#pragma mark - Update With... Methods
- (void)updateWithExpireStartedAt:(uint64_t)expireStartedAt transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

View file

@ -16,6 +16,10 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
@interface TSMessage ()
@property (nonatomic, nullable) NSString *body;
@property (nonatomic) uint32_t expiresInSeconds;
@property (nonatomic) uint64_t expireStartedAt;
/**
* The version of the model class's schema last used to serialize this model. Use this to manage data migrations during
* object de/serialization.
@ -277,6 +281,18 @@ static const NSUInteger OWSMessageSchemaVersion = 3;
return YES;
}
#pragma mark - Update With... Methods
- (void)updateWithExpireStartedAt:(uint64_t)expireStartedAt transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(expireStartedAt > 0);
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSMessage *message) {
[message setExpireStartedAt:expireStartedAt];
}];
}
@end
NS_ASSUME_NONNULL_END

View file

@ -148,32 +148,14 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId
filename:(nullable NSString *)filename;
// TSOutgoingMessage are updated from many threads. We don't want to save
// our local copy (this instance) since it may be out of date. Instead, we
// use these "updateWith..." methods to:
//
// a) Update a property of this instance.
// b) Load an up-to-date instance of this model from from the data store.
// c) Update and save that fresh instance.
// d) If this message hasn't yet been saved, save this local instance.
//
// After "updateWith...":
//
// a) An updated copy of this message will always have been saved in the
// data store.
// b) The local property on this instance will always have been updated.
// c) Other properties on this instance may be out of date.
//
// All mutable properties of this class have been made read-only to
// prevent accidentally modifying them directly.
//
// This isn't a perfect arrangement, but in practice this will prevent
// data loss and will resolve all known issues.
#pragma mark - Update With... Methods
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState;
- (void)updateWithMessageState:(TSOutgoingMessageState)messageState
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithSendingError:(NSError *)error;
- (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript;
- (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript
transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithCustomMessage:(NSString *)customMessage transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithCustomMessage:(NSString *)customMessage;
// deliveryTimestamp is an optional parameter, since legacy
@ -196,7 +178,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
- (NSUInteger)sentRecipientsCount;
- (BOOL)wasSentToRecipient:(NSString *)contactId;
- (void)updateWithSentRecipient:(NSString *)contactId transaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)updateWithSentRecipient:(NSString *)contactId;
@end

View file

@ -238,38 +238,18 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
return OWSInteractionType_OutgoingMessage;
}
#pragma mark - Update Methods
// This method does the work for the "updateWith..." methods. Please see
// the header for a discussion of those methods.
- (void)applyChangeToSelfAndLatestOutgoingMessage:(YapDatabaseReadWriteTransaction *)transaction
changeBlock:(void (^)(TSOutgoingMessage *))changeBlock
{
OWSAssert(transaction);
changeBlock(self);
NSString *collection = [[self class] collection];
TSOutgoingMessage *latestMessage = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestMessage) {
changeBlock(latestMessage);
[latestMessage saveWithTransaction:transaction];
} else {
// This message has not yet been saved.
[self saveWithTransaction:transaction];
}
}
#pragma mark - Update With... Methods
- (void)updateWithSendingError:(NSError *)error
{
OWSAssert(error);
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateUnsent];
[message setMostRecentFailureText:error.localizedDescription];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateUnsent];
[message setMostRecentFailureText:error.localizedDescription];
}];
}];
}
@ -285,20 +265,19 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
{
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:messageState];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:messageState];
}];
}
- (void)updateWithHasSyncedTranscript:(BOOL)hasSyncedTranscript
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setHasSyncedTranscript:hasSyncedTranscript];
}];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setHasSyncedTranscript:hasSyncedTranscript];
}];
}
- (void)updateWithCustomMessage:(NSString *)customMessage transaction:(YapDatabaseReadWriteTransaction *)transaction
@ -306,10 +285,10 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
OWSAssert(customMessage);
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setCustomMessage:customMessage];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setCustomMessage:customMessage];
}];
}
- (void)updateWithCustomMessage:(NSString *)customMessage
@ -326,32 +305,31 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
OWSAssert(recipientId.length > 0);
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
if (deliveryTimestamp) {
NSMutableDictionary<NSString *, NSNumber *> *recipientDeliveryMap
= (message.recipientDeliveryMap
? [message.recipientDeliveryMap mutableCopy]
: [NSMutableDictionary new]);
recipientDeliveryMap[recipientId] = deliveryTimestamp;
message.recipientDeliveryMap = [recipientDeliveryMap copy];
}
if (deliveryTimestamp) {
NSMutableDictionary<NSString *, NSNumber *> *recipientDeliveryMap
= (message.recipientDeliveryMap ? [message.recipientDeliveryMap mutableCopy]
: [NSMutableDictionary new]);
recipientDeliveryMap[recipientId] = deliveryTimestamp;
message.recipientDeliveryMap = [recipientDeliveryMap copy];
}
[message setWasDelivered:YES];
}];
[message setWasDelivered:YES];
}];
}
- (void)updateWithWasSentFromLinkedDeviceWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateSentToService];
[message setWasDelivered:YES];
[message setIsFromLinkedDevice:YES];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setMessageState:TSOutgoingMessageStateSentToService];
[message setWasDelivered:YES];
[message setIsFromLinkedDevice:YES];
}];
}
- (void)updateWithSingleGroupRecipient:(NSString *)singleGroupRecipient
@ -360,10 +338,10 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
OWSAssert(transaction);
OWSAssert(singleGroupRecipient.length > 0);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setSingleGroupRecipient:singleGroupRecipient];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message setSingleGroupRecipient:singleGroupRecipient];
}];
}
#pragma mark - Sent Recipients
@ -415,17 +393,10 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
- (void)updateWithSentRecipient:(NSString *)contactId transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message addSentRecipient:contactId];
}];
}
- (void)updateWithSentRecipient:(NSString *)contactId
{
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self updateWithSentRecipient:contactId transaction:transaction];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
[message addSentRecipient:contactId];
}];
}
- (void)updateWithReadRecipientId:(NSString *)recipientId
@ -435,14 +406,14 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
OWSAssert(recipientId.length > 0);
OWSAssert(transaction);
[self applyChangeToSelfAndLatestOutgoingMessage:transaction
changeBlock:^(TSOutgoingMessage *message) {
NSMutableDictionary<NSString *, NSNumber *> *recipientReadMap
= (message.recipientReadMap ? [message.recipientReadMap mutableCopy]
: [NSMutableDictionary new]);
recipientReadMap[recipientId] = @(readTimestamp);
message.recipientReadMap = [recipientReadMap copy];
}];
[self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) {
NSMutableDictionary<NSString *, NSNumber *> *recipientReadMap
= (message.recipientReadMap ? [message.recipientReadMap mutableCopy]
: [NSMutableDictionary new]);
recipientReadMap[recipientId] = @(readTimestamp);
message.recipientReadMap = [recipientReadMap copy];
}];
}
- (nullable NSNumber *)firstRecipientReadTimestamp

View file

@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithEnvelopeData:(NSData *)envelopeData
plaintextData:(NSData *_Nullable)plaintextData NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_UNAVAILABLE;
- (OWSSignalServiceProtosEnvelope *)envelopeProto;
@end

View file

@ -258,7 +258,7 @@ NSString *const kOWSBlockingManager_SyncedBlockedPhoneNumbersKey = @"kOWSBlockin
OWSBlockedPhoneNumbersMessage *message =
[[OWSBlockedPhoneNumbersMessage alloc] initWithPhoneNumbers:blockedPhoneNumbers];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent blocked phone numbers sync message", self.logTag);

View file

@ -184,8 +184,7 @@ NS_ASSUME_NONNULL_BEGIN
// Don't clobber if multiple actions simultaneously triggered expiration.
if (message.expireStartedAt == 0 || message.expireStartedAt > expirationStartedAt) {
message.expireStartedAt = expirationStartedAt;
[message saveWithTransaction:transaction];
[message updateWithExpireStartedAt:expirationStartedAt transaction:transaction];
}
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration.

View file

@ -517,10 +517,10 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
// subsequently
OWSOutgoingNullMessage *nullMessage = [[OWSOutgoingNullMessage alloc] initWithContactThread:contactThread
verificationStateSyncMessage:message];
[self.messageSender sendMessage:nullMessage
[self.messageSender enqueueMessage:nullMessage
success:^{
DDLogInfo(@"%@ Successfully sent verification state NullMessage", self.logTag);
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent verification state sync message", self.logTag);

View file

@ -366,7 +366,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSyncGroupsRequestMessage *syncGroupsRequestMessage =
[[OWSSyncGroupsRequestMessage alloc] initWithThread:thread groupId:groupId];
[self.messageSender sendMessage:syncGroupsRequestMessage
[self.messageSender enqueueMessage:syncGroupsRequestMessage
success:^{
DDLogWarn(@"%@ Successfully sent Request Group Info message.", self.logTag);
}
@ -609,7 +609,7 @@ NS_ASSUME_NONNULL_BEGIN
profileManager:self.profileManager];
DataSource *dataSource =
[DataSourceValue dataSourceWithSyncMessage:[syncContactsMessage buildPlainTextAttachmentData]];
[self.messageSender sendTemporaryAttachmentData:dataSource
[self.messageSender enqueueTemporaryAttachment:dataSource
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncContactsMessage
success:^{
@ -622,7 +622,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSSyncGroupsMessage *syncGroupsMessage = [[OWSSyncGroupsMessage alloc] init];
DataSource *dataSource = [DataSourceValue
dataSourceWithSyncMessage:[syncGroupsMessage buildPlainTextAttachmentDataWithTransaction:transaction]];
[self.messageSender sendTemporaryAttachmentData:dataSource
[self.messageSender enqueueTemporaryAttachment:dataSource
contentType:OWSMimeTypeApplicationOctetStream
inMessage:syncGroupsMessage
success:^{
@ -639,7 +639,7 @@ NS_ASSUME_NONNULL_BEGIN
[[OWSReadReceiptManager sharedManager] areReadReceiptsEnabledWithTransaction:transaction];
OWSSyncConfigurationMessage *syncConfigurationMessage =
[[OWSSyncConfigurationMessage alloc] initWithReadReceiptsEnabled:areReadReceiptsEnabled];
[self.messageSender sendMessage:syncConfigurationMessage
[self.messageSender enqueueMessage:syncConfigurationMessage
success:^{
DDLogInfo(@"%@ Successfully sent Configuration response syncMessage.", self.logTag);
}
@ -774,7 +774,7 @@ NS_ASSUME_NONNULL_BEGIN
if (gThread.groupModel.groupImage) {
NSData *data = UIImagePNGRepresentation(gThread.groupModel.groupImage);
DataSource *_Nullable dataSource = [DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
[self.messageSender enqueueAttachment:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
@ -785,7 +785,7 @@ NS_ASSUME_NONNULL_BEGIN
DDLogError(@"%@ Failed to send group avatar update with error: %@", self.logTag, error);
}];
} else {
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogDebug(@"%@ Successfully sent group update", self.logTag);
}

View file

@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithEnvelope:(OWSSignalServiceProtosEnvelope *)envelope NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_UNAVAILABLE;
- (OWSSignalServiceProtosEnvelope *)envelopeProto;
@end

View file

@ -63,33 +63,33 @@ NS_SWIFT_NAME(MessageSender)
/**
* Send and resend text messages or resend messages with existing attachments.
* If you haven't yet created the attachment, see the `sendAttachmentData:` variants.
* If you haven't yet created the attachment, see the ` enqueueAttachment:` variants.
*/
// TODO: make transaction nonnull and remove `sendMessage:success:failure`
- (void)sendMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)enqueueMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Takes care of allocating and uploading the attachment, then sends the message.
* Only necessary to call once. If sending fails, retry with `sendMessage:`.
*/
- (void)sendAttachmentData:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)enqueueAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Same as `sendAttachmentData:`, but deletes the local copy of the attachment after sending.
* Same as ` enqueueAttachment:`, but deletes the local copy of the attachment after sending.
* Used for sending sync request data, not for user visible attachments.
*/
- (void)sendTemporaryAttachmentData:(DataSource *)dataSource
contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
- (void)enqueueTemporaryAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)outgoingMessage
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Set local configuration to match that of the of `outgoingMessage`'s sender

View file

@ -155,7 +155,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
@property (nonatomic, readonly) TSOutgoingMessage *message;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) void (^successHandler)();
@property (nonatomic, readonly) void (^successHandler)(void);
@property (nonatomic, readonly) void (^failureHandler)(NSError *_Nonnull error);
@property (nonatomic) OWSSendMessageOperationState operationState;
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@ -280,6 +280,15 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
- (void)tryWithRemainingRetries:(NSUInteger)remainingRetries
{
// If the message has been deleted, abort send.
if (![TSOutgoingMessage fetchObjectWithUniqueID:self.message.uniqueId]) {
DDLogInfo(@"%@ aborting message send; message deleted.", self.logTag);
NSError *error = OWSErrorWithCodeDescription(
OWSErrorCodeMessageDeletedBeforeSent, @"Message was deleted before it could be sent.");
self.failureHandler(error);
return;
}
// Use this flag to ensure a given operation only succeeds or fails once.
__block BOOL onceFlag = NO;
RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) {
@ -410,9 +419,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
}
- (void)sendMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
- (void)enqueueMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
{
OWSAssert(message);
@ -429,7 +438,11 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
//
// So we're using YDB behavior to ensure this invariant, which is a bit
// unorthodox.
[message updateWithMessageState:TSOutgoingMessageStateAttemptingOut];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
// All outgoing messages should be saved at the time they are enqueued.
[message saveWithTransaction:transaction];
[message updateWithMessageState:TSOutgoingMessageStateAttemptingOut transaction:transaction];
}];
OWSSendMessageOperation *sendMessageOperation =
[[OWSSendMessageOperation alloc] initWithMessage:message
@ -457,12 +470,13 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
{
[self ensureAnyAttachmentsUploaded:message
success:^() {
[self deliverMessage:message
success:successHandler
failure:^(NSError *error) {
DDLogDebug(@"%@ Message send attempt failed: %@", self.logTag, message.debugDescription);
failureHandler(error);
}];
[self sendMessageToService:message
success:successHandler
failure:^(NSError *error) {
DDLogDebug(
@"%@ Message send attempt failed: %@", self.logTag, message.debugDescription);
failureHandler(error);
}];
}
failure:^(NSError *error) {
DDLogDebug(@"%@ Attachment upload attempt failed: %@", self.logTag, message.debugDescription);
@ -495,15 +509,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
failure:failureHandler];
}
- (void)sendTemporaryAttachmentData:(DataSource *)dataSource
contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
- (void)enqueueTemporaryAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
{
OWSAssert(dataSource);
void (^successWithDeleteHandler)() = ^() {
void (^successWithDeleteHandler)(void) = ^() {
successHandler();
DDLogDebug(@"Removing temporary attachment message.");
@ -517,20 +531,20 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[message remove];
};
[self sendAttachmentData:dataSource
contentType:contentType
sourceFilename:nil
inMessage:message
success:successWithDeleteHandler
failure:failureWithDeleteHandler];
[self enqueueAttachment:dataSource
contentType:contentType
sourceFilename:nil
inMessage:message
success:successWithDeleteHandler
failure:failureWithDeleteHandler];
}
- (void)sendAttachmentData:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
inMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
- (void)enqueueAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
inMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler
{
OWSAssert(dataSource);
@ -554,9 +568,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (sourceFilename) {
message.attachmentFilenameMap[attachmentStream.uniqueId] = sourceFilename;
}
[message save];
[self sendMessage:message success:successHandler failure:failureHandler];
[self enqueueMessage:message success:successHandler failure:failureHandler];
});
}
@ -586,9 +599,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return [recipients copy];
}
- (void)deliverMessage:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
- (void)sendMessageToService:(TSOutgoingMessage *)message
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
dispatch_async([OWSDispatch sendingQueue], ^{
TSThread *thread = message.thread;
@ -674,12 +687,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
return;
}
[self sendMessage:message
recipient:recipient
thread:thread
attempts:OWSMessageSenderRetryAttempts
success:successHandler
failure:failureHandler];
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:OWSMessageSenderRetryAttempts
success:successHandler
failure:failureHandler];
} else {
// Neither a group nor contact thread? This should never happen.
OWSFail(@"%@ Unknown message type: %@", self.logTag, NSStringFromClass([message class]));
@ -698,13 +711,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
{
TOCFutureSource *futureSource = [[TOCFutureSource alloc] init];
[self sendMessage:message
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:OWSMessageSenderRetryAttempts
success:^{
DDLogInfo(@"%@ Marking group message as sent to recipient: %@", self.logTag, recipient.uniqueId);
[message updateWithSentRecipient:recipient.uniqueId];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithSentRecipient:recipient.uniqueId transaction:transaction];
}];
[futureSource trySetResult:@1];
}
failure:^(NSError *error) {
@ -826,12 +841,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}];
}
- (void)sendMessage:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
thread:(TSThread *)thread
attempts:(int)remainingAttempts
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
- (void)sendMessageToService:(TSOutgoingMessage *)message
recipient:(SignalRecipient *)recipient
thread:(TSThread *)thread
attempts:(int)remainingAttempts
success:(void (^)(void))successHandler
failure:(RetryableFailureHandler)failureHandler
{
DDLogInfo(@"%@ attempting to send message: %@, timestamp: %llu, recipient: %@",
self.logTag,
@ -1022,7 +1037,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
long statuscode = response.statusCode;
NSData *responseData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey];
void (^retrySend)() = ^void() {
void (^retrySend)(void) = ^void() {
if (remainingAttempts <= 0) {
// Since we've already repeatedly failed to send to the messaging API,
// it's unlikely that repeating the whole process will succeed.
@ -1032,12 +1047,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
dispatch_async([OWSDispatch sendingQueue], ^{
DDLogDebug(@"%@ Retrying: %@", self.logTag, message.debugDescription);
[self sendMessage:message
recipient:recipient
thread:thread
attempts:remainingAttempts
success:successHandler
failure:failureHandler];
[self sendMessageToService:message
recipient:recipient
thread:thread
attempts:remainingAttempts
success:successHandler
failure:failureHandler];
});
};
@ -1146,7 +1161,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (message.shouldSyncTranscript) {
// TODO: I suspect we shouldn't optimistically set hasSyncedTranscript.
// We could set this in a success handler for [sendSyncTranscriptForMessage:].
[message updateWithHasSyncedTranscript:YES];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[message updateWithHasSyncedTranscript:YES transaction:transaction];
}];
[self sendSyncTranscriptForMessage:message];
}
@ -1226,7 +1243,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
OWSOutgoingSentMessageTranscript *sentMessageTranscript =
[[OWSOutgoingSentMessageTranscript alloc] initWithOutgoingMessage:message];
[self sendMessage:sentMessageTranscript
[self sendMessageToService:sentMessageTranscript
recipient:[SignalRecipient selfRecipient]
thread:message.thread
attempts:OWSMessageSenderRetryAttempts

View file

@ -49,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN
[[OWSDisappearingMessagesConfigurationMessage alloc] initWithConfiguration:self.configuration
thread:self.thread];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogDebug(
@"%@ Successfully notified %@ of new disappearing messages configuration", self.logTag, self.thread);

View file

@ -229,7 +229,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
OWSReadReceiptsForLinkedDevicesMessage *message =
[[OWSReadReceiptsForLinkedDevicesMessage alloc] initWithReadReceipts:readReceiptsForLinkedDevices];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent %zd read receipt to linked devices.",
self.logTag,
@ -253,7 +253,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
[[OWSReadReceiptsForSenderMessage alloc] initWithThread:thread
messageTimestamps:timestamps.allObjects];
[self.messageSender sendMessage:message
[self.messageSender enqueueMessage:message
success:^{
DDLogInfo(@"%@ Successfully sent %zd read receipts to sender.", self.logTag, timestamps.count);
}
@ -590,7 +590,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
OWSSyncConfigurationMessage *syncConfigurationMessage =
[[OWSSyncConfigurationMessage alloc] initWithReadReceiptsEnabled:value];
[self.messageSender sendMessage:syncConfigurationMessage
[self.messageSender enqueueMessage:syncConfigurationMessage
success:^{
DDLogInfo(@"%@ Successfully sent Configuration syncMessage.", self.logTag);
}

View file

@ -31,7 +31,7 @@ OWSSignalServiceProtosVerifiedState OWSVerificationStateToProtoState(OWSVerifica
#pragma mark - Initializers
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

View file

@ -4,6 +4,8 @@
#import <Mantle/MTLModel+NSCoding.h>
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class YapDatabaseConnection;
@class YapDatabaseReadTransaction;
@ -18,8 +20,8 @@
*
* @return Initialized object
*/
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
/**
* Returns the collection to which the object belongs.
@ -77,8 +79,10 @@
*
* @return Instance of the object or nil if non-existent
*/
+ (instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID transaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(fetch(uniqueId:transaction:));
+ (instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID NS_SWIFT_NAME(fetch(uniqueId:));
+ (nullable instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID
transaction:(YapDatabaseReadTransaction *)transaction
NS_SWIFT_NAME(fetch(uniqueId:transaction:));
+ (nullable instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID NS_SWIFT_NAME(fetch(uniqueId:));
/**
* Saves the object with the shared readWrite connection.
@ -113,9 +117,40 @@
/**
* The unique identifier of the stored object
*/
@property (nonatomic) NSString *uniqueId;
@property (nonatomic, nullable) NSString *uniqueId;
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (void)remove;
#pragma mark Update With...
// This method is used by "updateWith..." methods.
//
// This model may be updated from many threads. We don't want to save
// our local copy (this instance) since it may be out of date. We also
// want to avoid re-saving a model that has been deleted. Therefore, we
// use "updateWith..." methods to:
//
// a) Update a property of this instance.
// b) If a copy of this model exists in the database, load an up-to-date copy,
// and update and save that copy.
// b) If a copy of this model _DOES NOT_ exist in the database, do _NOT_ save
// this local instance.
//
// After "updateWith...":
//
// a) Any copy of this model in the database will have been updated.
// b) The local property on this instance will always have been updated.
// c) Other properties on this instance may be out of date.
//
// All mutable properties of this class have been made read-only to
// prevent accidentally modifying them directly.
//
// This isn't a perfect arrangement, but in practice this will prevent
// data loss and will resolve all known issues.
- (void)applyChangeToSelfAndLatestCopy:(YapDatabaseReadWriteTransaction *)transaction
changeBlock:(void (^)(id))changeBlock;
@end
NS_ASSUME_NONNULL_END

View file

@ -6,6 +6,8 @@
#import "TSStorageManager.h"
#import <YapDatabase/YapDatabaseTransaction.h>
NS_ASSUME_NONNULL_BEGIN
@implementation TSYapDatabaseObject
- (instancetype)init
@ -13,7 +15,7 @@
return [self initWithUniqueId:[[NSUUID UUID] UUIDString]];
}
- (instancetype)initWithUniqueId:(NSString *)aUniqueId
- (instancetype)initWithUniqueId:(NSString *_Nullable)aUniqueId
{
self = [super init];
if (!self) {
@ -25,6 +27,11 @@
return self;
}
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
return [super initWithCoder:coder];
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
[transaction setObject:self forKey:self.uniqueId inCollection:[[self class] collection]];
@ -176,18 +183,40 @@
}];
}
+ (instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID transaction:(YapDatabaseReadTransaction *)transaction
+ (nullable instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID
transaction:(YapDatabaseReadTransaction *)transaction
{
return [transaction objectForKey:uniqueID inCollection:[self collection]];
}
+ (instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID
+ (nullable instancetype)fetchObjectWithUniqueID:(NSString *)uniqueID
{
__block id object;
__block id _Nullable object = nil;
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *transaction) {
object = [transaction objectForKey:uniqueID inCollection:[self collection]];
}];
return object;
}
#pragma mark Update With...
// This method does the work for the "updateWith..." methods. Please see
// the header for a discussion of those methods.
- (void)applyChangeToSelfAndLatestCopy:(YapDatabaseReadWriteTransaction *)transaction
changeBlock:(void (^)(id))changeBlock
{
OWSAssert(transaction);
changeBlock(self);
NSString *collection = [[self class] collection];
id latestInstance = [transaction objectForKey:self.uniqueId inCollection:collection];
if (latestInstance) {
changeBlock(latestInstance);
[latestInstance saveWithTransaction:transaction];
}
}
@end
NS_ASSUME_NONNULL_END

View file

@ -26,6 +26,7 @@ typedef NS_ENUM(NSInteger, OWSErrorCode) {
OWSErrorCodeMessageSendNoValidRecipients = 777407,
OWSErrorCodeContactsUpdaterRateLimit = 777408,
OWSErrorCodeCouldNotWriteAttachmentData = 777409,
OWSErrorCodeMessageDeletedBeforeSent = 777410,
};
extern NSError *OWSErrorWithCodeDescription(OWSErrorCode code, NSString *description);