Archived
Push from command line
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2017 Google
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "FIRMessagingPendingTopicsList.h"
|
||||
|
||||
#import "FIRMessaging_Private.h"
|
||||
#import "FIRMessagingLogger.h"
|
||||
#import "FIRMessagingPubSub.h"
|
||||
|
||||
#import "FIRMessagingDefines.h"
|
||||
|
||||
NSString *const kPendingTopicBatchActionKey = @"action";
|
||||
NSString *const kPendingTopicBatchTopicsKey = @"topics";
|
||||
|
||||
NSString *const kPendingBatchesEncodingKey = @"batches";
|
||||
NSString *const kPendingTopicsTimestampEncodingKey = @"ts";
|
||||
|
||||
#pragma mark - FIRMessagingTopicBatch
|
||||
|
||||
@interface FIRMessagingTopicBatch ()
|
||||
|
||||
@property(nonatomic, strong, nonnull) NSMutableDictionary
|
||||
<NSString *, NSMutableArray <FIRMessagingTopicOperationCompletion> *> *topicHandlers;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRMessagingTopicBatch
|
||||
|
||||
- (instancetype)initWithAction:(FIRMessagingTopicAction)action {
|
||||
if (self = [super init]) {
|
||||
_action = action;
|
||||
_topics = [NSMutableSet set];
|
||||
_topicHandlers = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSCoding
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeInteger:self.action forKey:kPendingTopicBatchActionKey];
|
||||
[aCoder encodeObject:self.topics forKey:kPendingTopicBatchTopicsKey];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
|
||||
// Ensure that our integer -> enum casting is safe
|
||||
NSInteger actionRawValue = [aDecoder decodeIntegerForKey:kPendingTopicBatchActionKey];
|
||||
FIRMessagingTopicAction action = FIRMessagingTopicActionSubscribe;
|
||||
if (actionRawValue == FIRMessagingTopicActionUnsubscribe) {
|
||||
action = FIRMessagingTopicActionUnsubscribe;
|
||||
}
|
||||
|
||||
if (self = [self initWithAction:action]) {
|
||||
NSSet *topics = [aDecoder decodeObjectForKey:kPendingTopicBatchTopicsKey];
|
||||
if ([topics isKindOfClass:[NSSet class]]) {
|
||||
_topics = [topics mutableCopy];
|
||||
}
|
||||
_topicHandlers = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - FIRMessagingPendingTopicsList
|
||||
|
||||
@interface FIRMessagingPendingTopicsList ()
|
||||
|
||||
@property(nonatomic, readwrite, strong) NSDate *archiveDate;
|
||||
@property(nonatomic, strong) NSMutableArray <FIRMessagingTopicBatch *> *topicBatches;
|
||||
|
||||
@property(nonatomic, strong) FIRMessagingTopicBatch *currentBatch;
|
||||
@property(nonatomic, strong) NSMutableSet <NSString *> *topicsInFlight;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRMessagingPendingTopicsList
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_topicBatches = [NSMutableArray array];
|
||||
_topicsInFlight = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (void)pruneTopicBatches:(NSMutableArray <FIRMessagingTopicBatch *> *)topicBatches {
|
||||
// For now, just remove empty batches. In the future we can use this to make the subscriptions
|
||||
// more efficient, by actually pruning topic actions that cancel each other out, for example.
|
||||
for (NSInteger i = topicBatches.count-1; i >= 0; i--) {
|
||||
FIRMessagingTopicBatch *batch = topicBatches[i];
|
||||
if (batch.topics.count == 0) {
|
||||
[topicBatches removeObjectAtIndex:i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark NSCoding
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder {
|
||||
[aCoder encodeObject:[NSDate date] forKey:kPendingTopicsTimestampEncodingKey];
|
||||
[aCoder encodeObject:self.topicBatches forKey:kPendingBatchesEncodingKey];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
|
||||
|
||||
if (self = [self init]) {
|
||||
_archiveDate = [aDecoder decodeObjectForKey:kPendingTopicsTimestampEncodingKey];
|
||||
NSArray *archivedBatches = [aDecoder decodeObjectForKey:kPendingBatchesEncodingKey];
|
||||
if (archivedBatches) {
|
||||
_topicBatches = [archivedBatches mutableCopy];
|
||||
[FIRMessagingPendingTopicsList pruneTopicBatches:_topicBatches];
|
||||
}
|
||||
_topicsInFlight = [NSMutableSet set];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Getters
|
||||
|
||||
- (NSUInteger)numberOfBatches {
|
||||
return self.topicBatches.count;
|
||||
}
|
||||
|
||||
#pragma mark Adding/Removing topics
|
||||
|
||||
- (void)addOperationForTopic:(NSString *)topic
|
||||
withAction:(FIRMessagingTopicAction)action
|
||||
completion:(nullable FIRMessagingTopicOperationCompletion)completion {
|
||||
|
||||
FIRMessagingTopicBatch *lastBatch = nil;
|
||||
@synchronized (self) {
|
||||
lastBatch = self.topicBatches.lastObject;
|
||||
if (!lastBatch || lastBatch.action != action) {
|
||||
// There either was no last batch, or our last batch's action was not the same, so we have to
|
||||
// create a new batch
|
||||
lastBatch = [[FIRMessagingTopicBatch alloc] initWithAction:action];
|
||||
[self.topicBatches addObject:lastBatch];
|
||||
}
|
||||
BOOL topicExistedBefore = ([lastBatch.topics member:topic] != nil);
|
||||
if (!topicExistedBefore) {
|
||||
[lastBatch.topics addObject:topic];
|
||||
[self.delegate pendingTopicsListDidUpdate:self];
|
||||
}
|
||||
// Add the completion handler to the batch
|
||||
if (completion) {
|
||||
NSMutableArray *handlers = lastBatch.topicHandlers[topic];
|
||||
if (!handlers) {
|
||||
handlers = [[NSMutableArray alloc] init];
|
||||
}
|
||||
[handlers addObject:completion];
|
||||
lastBatch.topicHandlers[topic] = handlers;
|
||||
}
|
||||
if (!self.currentBatch) {
|
||||
self.currentBatch = lastBatch;
|
||||
}
|
||||
// This may have been the first topic added, or was added to an ongoing batch
|
||||
if (self.currentBatch == lastBatch && !topicExistedBefore) {
|
||||
// Add this topic to our ongoing operations
|
||||
FIRMessaging_WEAKIFY(self);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
FIRMessaging_STRONGIFY(self);
|
||||
[self resumeOperationsIfNeeded];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resumeOperationsIfNeeded {
|
||||
@synchronized (self) {
|
||||
// If current batch is not set, set it now
|
||||
if (!self.currentBatch) {
|
||||
self.currentBatch = self.topicBatches.firstObject;
|
||||
}
|
||||
if (self.currentBatch.topics.count == 0) {
|
||||
return;
|
||||
}
|
||||
if (!self.delegate) {
|
||||
FIRMessagingLoggerError(kFIRMessagingMessageCodePendingTopicsList000,
|
||||
@"Attempted to update pending topics without a delegate");
|
||||
return;
|
||||
}
|
||||
if (![self.delegate pendingTopicsListCanRequestTopicUpdates:self]) {
|
||||
return;
|
||||
}
|
||||
for (NSString *topic in self.currentBatch.topics) {
|
||||
if ([self.topicsInFlight member:topic]) {
|
||||
// This topic is already active, so skip
|
||||
continue;
|
||||
}
|
||||
[self beginUpdateForCurrentBatchTopic:topic];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)subscriptionErrorIsRecoverable:(NSError *)error {
|
||||
return [error.domain isEqualToString:NSURLErrorDomain];
|
||||
}
|
||||
|
||||
- (void)beginUpdateForCurrentBatchTopic:(NSString *)topic {
|
||||
|
||||
@synchronized (self) {
|
||||
[self.topicsInFlight addObject:topic];
|
||||
}
|
||||
FIRMessaging_WEAKIFY(self);
|
||||
[self.delegate
|
||||
pendingTopicsList:self
|
||||
requestedUpdateForTopic:topic
|
||||
action:self.currentBatch.action
|
||||
completion:^(NSError *error) {
|
||||
dispatch_async(
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
FIRMessaging_STRONGIFY(self);
|
||||
@synchronized(self) {
|
||||
[self.topicsInFlight removeObject:topic];
|
||||
|
||||
BOOL recoverableError = [self subscriptionErrorIsRecoverable:error];
|
||||
if (!error || !recoverableError) {
|
||||
// Notify our handlers and remove the topic from our batch
|
||||
NSMutableArray *handlers = self.currentBatch.topicHandlers[topic];
|
||||
if (handlers.count) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
for (FIRMessagingTopicOperationCompletion handler in handlers) {
|
||||
handler(error);
|
||||
}
|
||||
[handlers removeAllObjects];
|
||||
});
|
||||
}
|
||||
[self.currentBatch.topics removeObject:topic];
|
||||
[self.currentBatch.topicHandlers removeObjectForKey:topic];
|
||||
if (self.currentBatch.topics.count == 0) {
|
||||
// All topic updates successfully finished in this batch, move on
|
||||
// to the next batch
|
||||
[self.topicBatches removeObject:self.currentBatch];
|
||||
self.currentBatch = nil;
|
||||
}
|
||||
[self.delegate pendingTopicsListDidUpdate:self];
|
||||
FIRMessaging_WEAKIFY(self);
|
||||
dispatch_async(
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
|
||||
^{
|
||||
FIRMessaging_STRONGIFY(self);
|
||||
[self resumeOperationsIfNeeded];
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user