Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*
2
 * Copyright 2017 Google
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
17
#import "FirebaseMessaging/Sources/FIRMessagingPendingTopicsList.h"
18
 
19
#import "FirebaseMessaging/Sources/FIRMessagingDefines.h"
20
#import "FirebaseMessaging/Sources/FIRMessagingLogger.h"
21
#import "FirebaseMessaging/Sources/FIRMessagingPubSub.h"
22
#import "FirebaseMessaging/Sources/FIRMessaging_Private.h"
23
 
24
NSString *const kPendingTopicBatchActionKey = @"action";
25
NSString *const kPendingTopicBatchTopicsKey = @"topics";
26
 
27
NSString *const kPendingBatchesEncodingKey = @"batches";
28
NSString *const kPendingTopicsTimestampEncodingKey = @"ts";
29
 
30
#pragma mark - FIRMessagingTopicBatch
31
 
32
@interface FIRMessagingTopicBatch ()
33
 
34
@property(nonatomic, strong, nonnull)
35
    NSMutableDictionary<NSString *, NSMutableArray<FIRMessagingTopicOperationCompletion> *>
36
        *topicHandlers;
37
 
38
@end
39
 
40
@implementation FIRMessagingTopicBatch
41
 
42
- (instancetype)initWithAction:(FIRMessagingTopicAction)action {
43
  if (self = [super init]) {
44
    _action = action;
45
    _topics = [NSMutableSet set];
46
    _topicHandlers = [NSMutableDictionary dictionary];
47
  }
48
  return self;
49
}
50
 
51
#pragma mark NSSecureCoding
52
 
53
+ (BOOL)supportsSecureCoding {
54
  return YES;
55
}
56
 
57
- (void)encodeWithCoder:(NSCoder *)aCoder {
58
  [aCoder encodeInteger:self.action forKey:kPendingTopicBatchActionKey];
59
  [aCoder encodeObject:self.topics forKey:kPendingTopicBatchTopicsKey];
60
}
61
 
62
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
63
  // Ensure that our integer -> enum casting is safe
64
  NSInteger actionRawValue = [aDecoder decodeIntegerForKey:kPendingTopicBatchActionKey];
65
  FIRMessagingTopicAction action = FIRMessagingTopicActionSubscribe;
66
  if (actionRawValue == FIRMessagingTopicActionUnsubscribe) {
67
    action = FIRMessagingTopicActionUnsubscribe;
68
  }
69
 
70
  if (self = [self initWithAction:action]) {
71
    _topics = [aDecoder
72
        decodeObjectOfClasses:[NSSet setWithObjects:NSMutableSet.class, NSString.class, nil]
73
                       forKey:kPendingTopicBatchTopicsKey];
74
    _topicHandlers = [NSMutableDictionary dictionary];
75
  }
76
  return self;
77
}
78
 
79
@end
80
 
81
#pragma mark - FIRMessagingPendingTopicsList
82
 
83
@interface FIRMessagingPendingTopicsList ()
84
 
85
@property(nonatomic, readwrite, strong) NSDate *archiveDate;
86
@property(nonatomic, strong) NSMutableArray<FIRMessagingTopicBatch *> *topicBatches;
87
 
88
@property(nonatomic, strong) FIRMessagingTopicBatch *currentBatch;
89
@property(nonatomic, strong) NSMutableSet<NSString *> *topicsInFlight;
90
 
91
@end
92
 
93
@implementation FIRMessagingPendingTopicsList
94
 
95
- (instancetype)init {
96
  if (self = [super init]) {
97
    _topicBatches = [NSMutableArray array];
98
    _topicsInFlight = [NSMutableSet set];
99
  }
100
  return self;
101
}
102
 
103
+ (void)pruneTopicBatches:(NSMutableArray<FIRMessagingTopicBatch *> *)topicBatches {
104
  // For now, just remove empty batches. In the future we can use this to make the subscriptions
105
  // more efficient, by actually pruning topic actions that cancel each other out, for example.
106
  for (NSInteger i = topicBatches.count - 1; i >= 0; i--) {
107
    FIRMessagingTopicBatch *batch = topicBatches[i];
108
    if (batch.topics.count == 0) {
109
      [topicBatches removeObjectAtIndex:i];
110
    }
111
  }
112
}
113
 
114
#pragma mark NSSecureCoding
115
 
116
+ (BOOL)supportsSecureCoding {
117
  return YES;
118
}
119
 
120
- (void)encodeWithCoder:(NSCoder *)aCoder {
121
  [aCoder encodeObject:[NSDate date] forKey:kPendingTopicsTimestampEncodingKey];
122
  [aCoder encodeObject:self.topicBatches forKey:kPendingBatchesEncodingKey];
123
}
124
 
125
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
126
  if (self = [self init]) {
127
    _archiveDate =
128
        [aDecoder decodeObjectOfClass:NSDate.class forKey:kPendingTopicsTimestampEncodingKey];
129
    _topicBatches =
130
        [aDecoder decodeObjectOfClasses:[NSSet setWithObjects:NSMutableArray.class,
131
                                                              FIRMessagingTopicBatch.class, nil]
132
                                 forKey:kPendingBatchesEncodingKey];
133
    if (_topicBatches) {
134
      [FIRMessagingPendingTopicsList pruneTopicBatches:_topicBatches];
135
    }
136
    _topicsInFlight = [NSMutableSet set];
137
  }
138
  return self;
139
}
140
 
141
#pragma mark Getters
142
 
143
- (NSUInteger)numberOfBatches {
144
  return self.topicBatches.count;
145
}
146
 
147
#pragma mark Adding/Removing topics
148
 
149
- (void)addOperationForTopic:(NSString *)topic
150
                  withAction:(FIRMessagingTopicAction)action
151
                  completion:(nullable FIRMessagingTopicOperationCompletion)completion {
152
  FIRMessagingTopicBatch *lastBatch = nil;
153
  @synchronized(self) {
154
    lastBatch = self.topicBatches.lastObject;
155
    if (!lastBatch || lastBatch.action != action) {
156
      // There either was no last batch, or our last batch's action was not the same, so we have to
157
      // create a new batch
158
      lastBatch = [[FIRMessagingTopicBatch alloc] initWithAction:action];
159
      [self.topicBatches addObject:lastBatch];
160
    }
161
    BOOL topicExistedBefore = ([lastBatch.topics member:topic] != nil);
162
    if (!topicExistedBefore) {
163
      [lastBatch.topics addObject:topic];
164
      [self.delegate pendingTopicsListDidUpdate:self];
165
    }
166
    // Add the completion handler to the batch
167
    if (completion) {
168
      NSMutableArray *handlers = lastBatch.topicHandlers[topic];
169
      if (!handlers) {
170
        handlers = [[NSMutableArray alloc] init];
171
      }
172
      [handlers addObject:completion];
173
      lastBatch.topicHandlers[topic] = handlers;
174
    }
175
    if (!self.currentBatch) {
176
      self.currentBatch = lastBatch;
177
    }
178
    // This may have been the first topic added, or was added to an ongoing batch
179
    if (self.currentBatch == lastBatch && !topicExistedBefore) {
180
      // Add this topic to our ongoing operations
181
      FIRMessaging_WEAKIFY(self);
182
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
183
        FIRMessaging_STRONGIFY(self);
184
        [self resumeOperationsIfNeeded];
185
      });
186
    }
187
  }
188
}
189
 
190
- (void)resumeOperationsIfNeeded {
191
  @synchronized(self) {
192
    // If current batch is not set, set it now
193
    if (!self.currentBatch) {
194
      self.currentBatch = self.topicBatches.firstObject;
195
    }
196
    if (self.currentBatch.topics.count == 0) {
197
      return;
198
    }
199
    if (!self.delegate) {
200
      FIRMessagingLoggerError(kFIRMessagingMessageCodePendingTopicsList000,
201
                              @"Attempted to update pending topics without a delegate");
202
      return;
203
    }
204
    if (![self.delegate pendingTopicsListCanRequestTopicUpdates:self]) {
205
      return;
206
    }
207
    for (NSString *topic in self.currentBatch.topics) {
208
      if ([self.topicsInFlight member:topic]) {
209
        // This topic is already active, so skip
210
        continue;
211
      }
212
      [self beginUpdateForCurrentBatchTopic:topic];
213
    }
214
  }
215
}
216
 
217
- (BOOL)subscriptionErrorIsRecoverable:(NSError *)error {
218
  return [error.domain isEqualToString:NSURLErrorDomain];
219
}
220
 
221
- (void)beginUpdateForCurrentBatchTopic:(NSString *)topic {
222
  @synchronized(self) {
223
    [self.topicsInFlight addObject:topic];
224
  }
225
  FIRMessaging_WEAKIFY(self);
226
  [self.delegate
227
            pendingTopicsList:self
228
      requestedUpdateForTopic:topic
229
                       action:self.currentBatch.action
230
                   completion:^(NSError *error) {
231
                     dispatch_async(
232
                         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
233
                           FIRMessaging_STRONGIFY(self);
234
                           @synchronized(self) {
235
                             [self.topicsInFlight removeObject:topic];
236
 
237
                             BOOL recoverableError = [self subscriptionErrorIsRecoverable:error];
238
                             if (!error || !recoverableError) {
239
                               // Notify our handlers and remove the topic from our batch
240
                               NSMutableArray *handlers = self.currentBatch.topicHandlers[topic];
241
                               if (handlers.count) {
242
                                 dispatch_async(dispatch_get_main_queue(), ^{
243
                                   for (FIRMessagingTopicOperationCompletion handler in handlers) {
244
                                     handler(error);
245
                                   }
246
                                   [handlers removeAllObjects];
247
                                 });
248
                               }
249
                               [self.currentBatch.topics removeObject:topic];
250
                               [self.currentBatch.topicHandlers removeObjectForKey:topic];
251
                               if (self.currentBatch.topics.count == 0) {
252
                                 // All topic updates successfully finished in this batch, move on
253
                                 // to the next batch
254
                                 [self.topicBatches removeObject:self.currentBatch];
255
                                 self.currentBatch = nil;
256
                               }
257
                               [self.delegate pendingTopicsListDidUpdate:self];
258
                               FIRMessaging_WEAKIFY(self);
259
                               dispatch_async(
260
                                   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
261
                                   ^{
262
                                     FIRMessaging_STRONGIFY(self);
263
                                     [self resumeOperationsIfNeeded];
264
                                   });
265
                             }
266
                           }
267
                         });
268
                   }];
269
}
270
 
271
@end