Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// Copyright 2017 Google
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
 
15
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetwork.h"
16
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkMessageCode.h"
17
 
18
#import "GoogleUtilities/Logger/Public/GoogleUtilities/GULLogger.h"
19
#import "GoogleUtilities/NSData+zlib/Public/GoogleUtilities/GULNSData+zlib.h"
20
#import "GoogleUtilities/Network/GULNetworkInternal.h"
21
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULMutableDictionary.h"
22
#import "GoogleUtilities/Network/Public/GoogleUtilities/GULNetworkConstants.h"
23
#import "GoogleUtilities/Reachability/Public/GoogleUtilities/GULReachabilityChecker.h"
24
 
25
/// Constant string for request header Content-Encoding.
26
static NSString *const kGULNetworkContentCompressionKey = @"Content-Encoding";
27
 
28
/// Constant string for request header Content-Encoding value.
29
static NSString *const kGULNetworkContentCompressionValue = @"gzip";
30
 
31
/// Constant string for request header Content-Length.
32
static NSString *const kGULNetworkContentLengthKey = @"Content-Length";
33
 
34
/// Constant string for request header Content-Type.
35
static NSString *const kGULNetworkContentTypeKey = @"Content-Type";
36
 
37
/// Constant string for request header Content-Type value.
38
static NSString *const kGULNetworkContentTypeValue = @"application/x-www-form-urlencoded";
39
 
40
/// Constant string for GET request method.
41
static NSString *const kGULNetworkGETRequestMethod = @"GET";
42
 
43
/// Constant string for POST request method.
44
static NSString *const kGULNetworkPOSTRequestMethod = @"POST";
45
 
46
/// Default constant string as a prefix for network logger.
47
static NSString *const kGULNetworkLogTag = @"Google/Utilities/Network";
48
 
49
@interface GULNetwork () <GULReachabilityDelegate, GULNetworkLoggerDelegate>
50
@end
51
 
52
@implementation GULNetwork {
53
  /// Network reachability.
54
  GULReachabilityChecker *_reachability;
55
 
56
  /// The dictionary of requests by session IDs { NSString : id }.
57
  GULMutableDictionary *_requests;
58
}
59
 
60
- (instancetype)init {
61
  return [self initWithReachabilityHost:kGULNetworkReachabilityHost];
62
}
63
 
64
- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost {
65
  self = [super init];
66
  if (self) {
67
    // Setup reachability.
68
    _reachability = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self
69
                                                                        withHost:reachabilityHost];
70
    if (![_reachability start]) {
71
      return nil;
72
    }
73
 
74
    _requests = [[GULMutableDictionary alloc] init];
75
    _timeoutInterval = kGULNetworkTimeOutInterval;
76
  }
77
  return self;
78
}
79
 
80
- (void)dealloc {
81
  _reachability.reachabilityDelegate = nil;
82
  [_reachability stop];
83
}
84
 
85
#pragma mark - External Methods
86
 
87
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
88
                            completionHandler:(GULNetworkSystemCompletionHandler)completionHandler {
89
  [GULNetworkURLSession handleEventsForBackgroundURLSessionID:sessionID
90
                                            completionHandler:completionHandler];
91
}
92
 
93
- (NSString *)postURL:(NSURL *)url
94
                   payload:(NSData *)payload
95
                     queue:(dispatch_queue_t)queue
96
    usingBackgroundSession:(BOOL)usingBackgroundSession
97
         completionHandler:(GULNetworkCompletionHandler)handler {
98
  if (!url.absoluteString.length) {
99
    [self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
100
    return nil;
101
  }
102
 
103
  NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
104
 
105
  NSMutableURLRequest *request =
106
      [[NSMutableURLRequest alloc] initWithURL:url
107
                                   cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
108
                               timeoutInterval:timeOutInterval];
109
 
110
  if (!request) {
111
    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
112
                        queue:queue
113
                  withHandler:handler];
114
    return nil;
115
  }
116
 
117
  NSError *compressError = nil;
118
  NSData *compressedData = [NSData gul_dataByGzippingData:payload error:&compressError];
119
  if (!compressedData || compressError) {
120
    if (compressError || payload.length > 0) {
121
      // If the payload is not empty but it fails to compress the payload, something has been wrong.
122
      [self handleErrorWithCode:GULErrorCodeNetworkPayloadCompression
123
                          queue:queue
124
                    withHandler:handler];
125
      return nil;
126
    }
127
    compressedData = [[NSData alloc] init];
128
  }
129
 
130
  NSString *postLength = @(compressedData.length).stringValue;
131
 
132
  // Set up the request with the compressed data.
133
  [request setValue:postLength forHTTPHeaderField:kGULNetworkContentLengthKey];
134
  request.HTTPBody = compressedData;
135
  request.HTTPMethod = kGULNetworkPOSTRequestMethod;
136
  [request setValue:kGULNetworkContentTypeValue forHTTPHeaderField:kGULNetworkContentTypeKey];
137
  [request setValue:kGULNetworkContentCompressionValue
138
      forHTTPHeaderField:kGULNetworkContentCompressionKey];
139
 
140
  GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
141
  fetcher.backgroundNetworkEnabled = usingBackgroundSession;
142
 
143
  __weak GULNetwork *weakSelf = self;
144
  NSString *requestID = [fetcher
145
      sessionIDFromAsyncPOSTRequest:request
146
                  completionHandler:^(NSHTTPURLResponse *response, NSData *data,
147
                                      NSString *sessionID, NSError *error) {
148
                    GULNetwork *strongSelf = weakSelf;
149
                    if (!strongSelf) {
150
                      return;
151
                    }
152
                    dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
153
                    dispatch_async(queueToDispatch, ^{
154
                      if (sessionID.length) {
155
                        [strongSelf->_requests removeObjectForKey:sessionID];
156
                      }
157
                      if (handler) {
158
                        handler(response, data, error);
159
                      }
160
                    });
161
                  }];
162
  if (!requestID) {
163
    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
164
                        queue:queue
165
                  withHandler:handler];
166
    return nil;
167
  }
168
 
169
  [self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
170
                    messageCode:kGULNetworkMessageCodeNetwork000
171
                        message:@"Uploading data. Host"
172
                        context:url];
173
  _requests[requestID] = fetcher;
174
  return requestID;
175
}
176
 
177
- (NSString *)getURL:(NSURL *)url
178
                   headers:(NSDictionary *)headers
179
                     queue:(dispatch_queue_t)queue
180
    usingBackgroundSession:(BOOL)usingBackgroundSession
181
         completionHandler:(GULNetworkCompletionHandler)handler {
182
  if (!url.absoluteString.length) {
183
    [self handleErrorWithCode:GULErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
184
    return nil;
185
  }
186
 
187
  NSTimeInterval timeOutInterval = _timeoutInterval ?: kGULNetworkTimeOutInterval;
188
  NSMutableURLRequest *request =
189
      [[NSMutableURLRequest alloc] initWithURL:url
190
                                   cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
191
                               timeoutInterval:timeOutInterval];
192
 
193
  if (!request) {
194
    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
195
                        queue:queue
196
                  withHandler:handler];
197
    return nil;
198
  }
199
 
200
  request.HTTPMethod = kGULNetworkGETRequestMethod;
201
  request.allHTTPHeaderFields = headers;
202
 
203
  GULNetworkURLSession *fetcher = [[GULNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
204
  fetcher.backgroundNetworkEnabled = usingBackgroundSession;
205
 
206
  __weak GULNetwork *weakSelf = self;
207
  NSString *requestID = [fetcher
208
      sessionIDFromAsyncGETRequest:request
209
                 completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSString *sessionID,
210
                                     NSError *error) {
211
                   GULNetwork *strongSelf = weakSelf;
212
                   if (!strongSelf) {
213
                     return;
214
                   }
215
                   dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
216
                   dispatch_async(queueToDispatch, ^{
217
                     if (sessionID.length) {
218
                       [strongSelf->_requests removeObjectForKey:sessionID];
219
                     }
220
                     if (handler) {
221
                       handler(response, data, error);
222
                     }
223
                   });
224
                 }];
225
 
226
  if (!requestID) {
227
    [self handleErrorWithCode:GULErrorCodeNetworkSessionTaskCreation
228
                        queue:queue
229
                  withHandler:handler];
230
    return nil;
231
  }
232
 
233
  [self GULNetwork_logWithLevel:kGULNetworkLogLevelDebug
234
                    messageCode:kGULNetworkMessageCodeNetwork001
235
                        message:@"Downloading data. Host"
236
                        context:url];
237
  _requests[requestID] = fetcher;
238
  return requestID;
239
}
240
 
241
- (BOOL)hasUploadInProgress {
242
  return _requests.count > 0;
243
}
244
 
245
#pragma mark - Network Reachability
246
 
247
/// Tells reachability delegate to call reachabilityDidChangeToStatus: to notify the network
248
/// reachability has changed.
249
- (void)reachability:(GULReachabilityChecker *)reachability
250
       statusChanged:(GULReachabilityStatus)status {
251
  _networkConnected = (status == kGULReachabilityViaCellular || status == kGULReachabilityViaWifi);
252
  [_reachabilityDelegate reachabilityDidChange];
253
}
254
 
255
#pragma mark - Network logger delegate
256
 
257
- (void)setLoggerDelegate:(id<GULNetworkLoggerDelegate>)loggerDelegate {
258
  // Explicitly check whether the delegate responds to the methods because conformsToProtocol does
259
  // not work correctly even though the delegate does respond to the methods.
260
  if (!loggerDelegate ||
261
      ![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
262
                                                                messageCode:message:contexts:)] ||
263
      ![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
264
                                                                messageCode:message:context:)] ||
265
      ![loggerDelegate respondsToSelector:@selector(GULNetwork_logWithLevel:
266
                                                                messageCode:message:)]) {
267
    GULLogError(kGULLoggerNetwork, NO,
268
                [NSString stringWithFormat:@"I-NET%06ld", (long)kGULNetworkMessageCodeNetwork002],
269
                @"Cannot set the network logger delegate: delegate does not conform to the network "
270
                 "logger protocol.");
271
    return;
272
  }
273
  _loggerDelegate = loggerDelegate;
274
}
275
 
276
#pragma mark - Private methods
277
 
278
/// Handles network error and calls completion handler with the error.
279
- (void)handleErrorWithCode:(NSInteger)code
280
                      queue:(dispatch_queue_t)queue
281
                withHandler:(GULNetworkCompletionHandler)handler {
282
  NSDictionary *userInfo = @{kGULNetworkErrorContext : @"Failed to create network request"};
283
  NSError *error = [[NSError alloc] initWithDomain:kGULNetworkErrorDomain
284
                                              code:code
285
                                          userInfo:userInfo];
286
  [self GULNetwork_logWithLevel:kGULNetworkLogLevelWarning
287
                    messageCode:kGULNetworkMessageCodeNetwork002
288
                        message:@"Failed to create network request. Code, error"
289
                       contexts:@[ @(code), error ]];
290
  if (handler) {
291
    dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
292
    dispatch_async(queueToDispatch, ^{
293
      handler(nil, nil, error);
294
    });
295
  }
296
}
297
 
298
#pragma mark - Network logger
299
 
300
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
301
                    messageCode:(GULNetworkMessageCode)messageCode
302
                        message:(NSString *)message
303
                       contexts:(NSArray *)contexts {
304
  // Let the delegate log the message if there is a valid logger delegate. Otherwise, just log
305
  // errors/warnings/info messages to the console log.
306
  if (_loggerDelegate) {
307
    [_loggerDelegate GULNetwork_logWithLevel:logLevel
308
                                 messageCode:messageCode
309
                                     message:message
310
                                    contexts:contexts];
311
    return;
312
  }
313
  if (_isDebugModeEnabled || logLevel == kGULNetworkLogLevelError ||
314
      logLevel == kGULNetworkLogLevelWarning || logLevel == kGULNetworkLogLevelInfo) {
315
    NSString *formattedMessage = GULStringWithLogMessage(message, logLevel, contexts);
316
    NSLog(@"%@", formattedMessage);
317
    GULLogBasic((GULLoggerLevel)logLevel, kGULLoggerNetwork, NO,
318
                [NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
319
                NULL);
320
  }
321
}
322
 
323
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
324
                    messageCode:(GULNetworkMessageCode)messageCode
325
                        message:(NSString *)message
326
                        context:(id)context {
327
  if (_loggerDelegate) {
328
    [_loggerDelegate GULNetwork_logWithLevel:logLevel
329
                                 messageCode:messageCode
330
                                     message:message
331
                                     context:context];
332
    return;
333
  }
334
  NSArray *contexts = context ? @[ context ] : @[];
335
  [self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:contexts];
336
}
337
 
338
- (void)GULNetwork_logWithLevel:(GULNetworkLogLevel)logLevel
339
                    messageCode:(GULNetworkMessageCode)messageCode
340
                        message:(NSString *)message {
341
  if (_loggerDelegate) {
342
    [_loggerDelegate GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message];
343
    return;
344
  }
345
  [self GULNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:@[]];
346
}
347
 
348
/// Returns a string for the given log level (e.g. kGULNetworkLogLevelError -> @"ERROR").
349
static NSString *GULLogLevelDescriptionFromLogLevel(GULNetworkLogLevel logLevel) {
350
  static NSDictionary *levelNames = nil;
351
  static dispatch_once_t onceToken;
352
  dispatch_once(&onceToken, ^{
353
    levelNames = @{
354
      @(kGULNetworkLogLevelError) : @"ERROR",
355
      @(kGULNetworkLogLevelWarning) : @"WARNING",
356
      @(kGULNetworkLogLevelInfo) : @"INFO",
357
      @(kGULNetworkLogLevelDebug) : @"DEBUG"
358
    };
359
  });
360
  return levelNames[@(logLevel)];
361
}
362
 
363
/// Returns a formatted string to be used for console logging.
364
static NSString *GULStringWithLogMessage(NSString *message,
365
                                         GULNetworkLogLevel logLevel,
366
                                         NSArray *contexts) {
367
  if (!message) {
368
    message = @"(Message was nil)";
369
  } else if (!message.length) {
370
    message = @"(Message was empty)";
371
  }
372
  NSMutableString *result = [[NSMutableString alloc]
373
      initWithFormat:@"<%@/%@> %@", kGULNetworkLogTag, GULLogLevelDescriptionFromLogLevel(logLevel),
374
                     message];
375
 
376
  if (!contexts.count) {
377
    return result;
378
  }
379
 
380
  NSMutableArray *formattedContexts = [[NSMutableArray alloc] init];
381
  for (id item in contexts) {
382
    [formattedContexts addObject:(item != [NSNull null] ? item : @"(nil)")];
383
  }
384
 
385
  [result appendString:@": "];
386
  [result appendString:[formattedContexts componentsJoinedByString:@", "]];
387
  return result;
388
}
389
 
390
@end