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/FIRMessagingTopicOperation.h"
18
 
19
#import "FirebaseMessaging/Sources/FIRMessagingDefines.h"
20
#import "FirebaseMessaging/Sources/FIRMessagingLogger.h"
21
#import "FirebaseMessaging/Sources/FIRMessagingUtilities.h"
22
#import "FirebaseMessaging/Sources/NSError+FIRMessaging.h"
23
#import "FirebaseMessaging/Sources/Token/FIRMessagingTokenManager.h"
24
 
25
static NSString *const kFIRMessagingSubscribeServerHost =
26
    @"https://iid.googleapis.com/iid/register";
27
 
28
NSString *FIRMessagingSubscriptionsServer(void) {
29
  static NSString *serverHost = nil;
30
  static dispatch_once_t onceToken;
31
  dispatch_once(&onceToken, ^{
32
    NSDictionary *environment = [[NSProcessInfo processInfo] environment];
33
    NSString *customServerHost = environment[@"FCM_SERVER_ENDPOINT"];
34
    if (customServerHost.length) {
35
      serverHost = customServerHost;
36
    } else {
37
      serverHost = kFIRMessagingSubscribeServerHost;
38
    }
39
  });
40
  return serverHost;
41
}
42
 
43
@interface FIRMessagingTopicOperation () {
44
  BOOL _isFinished;
45
  BOOL _isExecuting;
46
}
47
 
48
@property(nonatomic, readwrite, copy) NSString *topic;
49
@property(nonatomic, readwrite, assign) FIRMessagingTopicAction action;
50
@property(nonatomic, readwrite, strong) FIRMessagingTokenManager *tokenManager;
51
@property(nonatomic, readwrite, copy) NSDictionary *options;
52
@property(nonatomic, readwrite, copy) FIRMessagingTopicOperationCompletion completion;
53
 
54
@property(atomic, strong) NSURLSessionDataTask *dataTask;
55
 
56
@end
57
 
58
@implementation FIRMessagingTopicOperation
59
 
60
+ (NSURLSession *)sharedSession {
61
  static NSURLSession *subscriptionOperationSharedSession;
62
  static dispatch_once_t onceToken;
63
  dispatch_once(&onceToken, ^{
64
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
65
    config.timeoutIntervalForResource = 60.0f;  // 1 minute
66
    subscriptionOperationSharedSession = [NSURLSession sessionWithConfiguration:config];
67
    subscriptionOperationSharedSession.sessionDescription = @"com.google.fcm.topics.session";
68
  });
69
  return subscriptionOperationSharedSession;
70
}
71
 
72
- (instancetype)initWithTopic:(NSString *)topic
73
                       action:(FIRMessagingTopicAction)action
74
                 tokenManager:(FIRMessagingTokenManager *)tokenManager
75
                      options:(NSDictionary *)options
76
                   completion:(FIRMessagingTopicOperationCompletion)completion {
77
  if (self = [super init]) {
78
    _topic = topic;
79
    _action = action;
80
    _tokenManager = tokenManager;
81
    _options = options;
82
    _completion = completion;
83
 
84
    _isExecuting = NO;
85
    _isFinished = NO;
86
  }
87
  return self;
88
}
89
 
90
- (void)dealloc {
91
  _topic = nil;
92
  _completion = nil;
93
}
94
 
95
- (BOOL)isAsynchronous {
96
  return YES;
97
}
98
 
99
- (BOOL)isExecuting {
100
  return _isExecuting;
101
}
102
 
103
- (void)setExecuting:(BOOL)executing {
104
  [self willChangeValueForKey:@"isExecuting"];
105
  _isExecuting = executing;
106
  [self didChangeValueForKey:@"isExecuting"];
107
}
108
 
109
- (BOOL)isFinished {
110
  return _isFinished;
111
}
112
 
113
- (void)setFinished:(BOOL)finished {
114
  [self willChangeValueForKey:@"isFinished"];
115
  _isFinished = finished;
116
  [self didChangeValueForKey:@"isFinished"];
117
}
118
 
119
- (void)start {
120
  if (self.isCancelled) {
121
    NSError *error = [NSError
122
        messagingErrorWithCode:kFIRMessagingErrorCodePubSubOperationIsCancelled
123
                 failureReason:
124
                     @"Failed to start the pubsub service as the topic operation is cancelled."];
125
    [self finishWithError:error];
126
    return;
127
  }
128
 
129
  [self setExecuting:YES];
130
 
131
  [self performSubscriptionChange];
132
}
133
 
134
- (void)finishWithError:(NSError *)error {
135
  // Add a check to prevent this finish from being called more than once.
136
  if (self.isFinished) {
137
    return;
138
  }
139
  self.dataTask = nil;
140
  if (self.completion) {
141
    self.completion(error);
142
  }
143
 
144
  [self setExecuting:NO];
145
  [self setFinished:YES];
146
}
147
 
148
- (void)cancel {
149
  [super cancel];
150
  [self.dataTask cancel];
151
  NSError *error = [NSError messagingErrorWithCode:kFIRMessagingErrorCodePubSubOperationIsCancelled
152
                                     failureReason:@"The topic operation is cancelled."];
153
  [self finishWithError:error];
154
}
155
 
156
- (void)performSubscriptionChange {
157
  NSURL *url = [NSURL URLWithString:FIRMessagingSubscriptionsServer()];
158
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
159
  NSString *appIdentifier = FIRMessagingAppIdentifier();
160
  NSString *authString = [NSString
161
      stringWithFormat:@"AidLogin %@:%@", _tokenManager.deviceAuthID, _tokenManager.secretToken];
162
  [request setValue:authString forHTTPHeaderField:@"Authorization"];
163
  [request setValue:appIdentifier forHTTPHeaderField:@"app"];
164
  [request setValue:_tokenManager.versionInfo forHTTPHeaderField:@"info"];
165
  // Topic can contain special characters (like `%`) so encode the value.
166
  NSCharacterSet *characterSet = [NSCharacterSet URLQueryAllowedCharacterSet];
167
  NSString *encodedTopic =
168
      [self.topic stringByAddingPercentEncodingWithAllowedCharacters:characterSet];
169
  if (encodedTopic == nil) {
170
    // The transformation was somehow not possible, so use the original topic.
171
    FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicOptionTopicEncodingFailed,
172
                           @"Unable to encode the topic '%@' during topic subscription change. "
173
                           @"Please ensure that the topic name contains only valid characters.",
174
                           self.topic);
175
    encodedTopic = self.topic;
176
  }
177
 
178
  NSMutableString *content = [NSMutableString
179
      stringWithFormat:@"sender=%@&app=%@&device=%@&"
180
                       @"app_ver=%@&X-gcm.topic=%@&X-scope=%@",
181
                       _tokenManager.defaultFCMToken, appIdentifier, _tokenManager.deviceAuthID,
182
                       FIRMessagingCurrentAppVersion(), encodedTopic, encodedTopic];
183
 
184
  if (self.action == FIRMessagingTopicActionUnsubscribe) {
185
    [content appendString:@"&delete=true"];
186
  }
187
 
188
  FIRMessagingLoggerInfo(kFIRMessagingMessageCodeTopicOption000, @"Topic subscription request: %@",
189
                         content);
190
 
191
  request.HTTPBody = [content dataUsingEncoding:NSUTF8StringEncoding];
192
  [request setHTTPMethod:@"POST"];
193
 
194
  FIRMessaging_WEAKIFY(self) void (^requestHandler)(NSData *, NSURLResponse *, NSError *) =
195
      ^(NSData *data, NSURLResponse *URLResponse, NSError *error) {
196
        FIRMessaging_STRONGIFY(self) if (error) {
197
          // Our operation could have been cancelled, which would result in our data task's error
198
          // being NSURLErrorCancelled
199
          if (error.code == NSURLErrorCancelled) {
200
            // We would only have been cancelled in the -cancel method, which will call finish for
201
            // us so just return and do nothing.
202
            return;
203
          }
204
          FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption001,
205
                                  @"Device registration HTTP fetch error. Error Code: %ld",
206
                                  (long)error.code);
207
          [self finishWithError:error];
208
          return;
209
        }
210
        NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
211
        if (response.length == 0) {
212
          NSString *failureReason = @"Invalid registration response - zero length.";
213
          FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOperationEmptyResponse, @"%@",
214
                                  failureReason);
215
          [self finishWithError:[NSError messagingErrorWithCode:kFIRMessagingErrorCodeUnknown
216
                                                  failureReason:failureReason]];
217
          return;
218
        }
219
        NSArray *parts = [response componentsSeparatedByString:@"="];
220
        if (![parts[0] isEqualToString:@"token"] || parts.count <= 1) {
221
          NSString *failureReason = [NSString
222
              stringWithFormat:@"Invalid registration response :'%@'. It is missing 'token' field.",
223
                               response];
224
          FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption002, @"%@", failureReason);
225
          [self finishWithError:[NSError messagingErrorWithCode:kFIRMessagingErrorCodeUnknown
226
                                                  failureReason:failureReason]];
227
          return;
228
        }
229
        [self finishWithError:nil];
230
      };
231
 
232
  NSURLSession *urlSession = [FIRMessagingTopicOperation sharedSession];
233
 
234
  self.dataTask = [urlSession dataTaskWithRequest:request completionHandler:requestHandler];
235
  NSString *description;
236
  if (_action == FIRMessagingTopicActionSubscribe) {
237
    description = [NSString stringWithFormat:@"com.google.fcm.topics.subscribe: %@", _topic];
238
  } else {
239
    description = [NSString stringWithFormat:@"com.google.fcm.topics.unsubscribe: %@", _topic];
240
  }
241
  self.dataTask.taskDescription = description;
242
  [self.dataTask resume];
243
}
244
 
245
@end