Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*
2
 * Copyright 2018 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 "GoogleDataTransport/GDTCORLibrary/Private/GDTCORUploadCoordinator.h"
18
 
19
#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORAssert.h"
20
#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORReachability.h"
21
#import "GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport/GDTCORClock.h"
22
#import "GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport/GDTCORConsoleLogger.h"
23
 
24
#import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
25
 
26
@implementation GDTCORUploadCoordinator
27
 
28
+ (instancetype)sharedInstance {
29
  static GDTCORUploadCoordinator *sharedUploader;
30
  static dispatch_once_t onceToken;
31
  dispatch_once(&onceToken, ^{
32
    sharedUploader = [[GDTCORUploadCoordinator alloc] init];
33
    [sharedUploader startTimer];
34
  });
35
  return sharedUploader;
36
}
37
 
38
- (instancetype)init {
39
  self = [super init];
40
  if (self) {
41
    _coordinationQueue =
42
        dispatch_queue_create("com.google.GDTCORUploadCoordinator", DISPATCH_QUEUE_SERIAL);
43
    _registrar = [GDTCORRegistrar sharedInstance];
44
    _timerInterval = 30 * NSEC_PER_SEC;
45
    _timerLeeway = 5 * NSEC_PER_SEC;
46
  }
47
  return self;
48
}
49
 
50
- (void)forceUploadForTarget:(GDTCORTarget)target {
51
  dispatch_async(_coordinationQueue, ^{
52
    GDTCORLogDebug(@"Forcing an upload of target %ld", (long)target);
53
    GDTCORUploadConditions conditions = [self uploadConditions];
54
    conditions |= GDTCORUploadConditionHighPriority;
55
    [self uploadTargets:@[ @(target) ] conditions:conditions];
56
  });
57
}
58
 
59
#pragma mark - Private helper methods
60
 
61
/** Starts a timer that checks whether or not events can be uploaded at regular intervals. It will
62
 * check the next-upload clocks of all targets to determine if an upload attempt can be made.
63
 */
64
- (void)startTimer {
65
  dispatch_async(_coordinationQueue, ^{
66
    if (self->_timer) {
67
      // The timer has been already started.
68
      return;
69
    }
70
 
71
    // Delay the timer slightly so it doesn't run while +load calls are still running.
72
    dispatch_time_t deadline = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC / 2);
73
 
74
    self->_timer =
75
        dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self->_coordinationQueue);
76
    dispatch_source_set_timer(self->_timer, deadline, self->_timerInterval, self->_timerLeeway);
77
 
78
    dispatch_source_set_event_handler(self->_timer, ^{
79
      if (![[GDTCORApplication sharedApplication] isRunningInBackground]) {
80
        GDTCORUploadConditions conditions = [self uploadConditions];
81
        GDTCORLogDebug(@"%@", @"Upload timer fired");
82
        [self uploadTargets:[self.registrar.targetToUploader allKeys] conditions:conditions];
83
      }
84
    });
85
    GDTCORLogDebug(@"%@", @"Upload timer started");
86
    dispatch_resume(self->_timer);
87
  });
88
}
89
 
90
/** Stops the currently running timer. */
91
- (void)stopTimer {
92
  if (_timer) {
93
    dispatch_source_cancel(_timer);
94
    _timer = nil;
95
  }
96
}
97
 
98
/** Triggers the uploader implementations for the given targets to upload.
99
 *
100
 * @param targets An array of targets to trigger.
101
 * @param conditions The set of upload conditions.
102
 */
103
- (void)uploadTargets:(NSArray<NSNumber *> *)targets conditions:(GDTCORUploadConditions)conditions {
104
  dispatch_async(_coordinationQueue, ^{
105
    // TODO: The reachability signal may be not reliable enough to prevent an upload attempt.
106
    // See https://developer.apple.com/videos/play/wwdc2019/712/ (49:40) for more details.
107
    if ((conditions & GDTCORUploadConditionNoNetwork) == GDTCORUploadConditionNoNetwork) {
108
      return;
109
    }
110
    for (NSNumber *target in targets) {
111
      id<GDTCORUploader> uploader = self->_registrar.targetToUploader[target];
112
      [uploader uploadTarget:target.intValue withConditions:conditions];
113
    }
114
  });
115
}
116
 
117
- (void)signalToStoragesToCheckExpirations {
118
  // The same storage may be associated with several targets. Make sure to check for expirations
119
  // only once per storage.
120
  NSSet<id<GDTCORStorageProtocol>> *storages =
121
      [NSSet setWithArray:[_registrar.targetToStorage allValues]];
122
  for (id<GDTCORStorageProtocol> storage in storages) {
123
    [storage checkForExpirations];
124
  }
125
}
126
 
127
/** Returns the registered storage for the given NSNumber wrapped GDTCORTarget.
128
 *
129
 * @param target The NSNumber wrapping of a GDTCORTarget to find the storage instance of.
130
 * @return The storage instance for the given target.
131
 */
132
- (nullable id<GDTCORStorageProtocol>)storageForTarget:(NSNumber *)target {
133
  id<GDTCORStorageProtocol> storage = [GDTCORRegistrar sharedInstance].targetToStorage[target];
134
  GDTCORAssert(storage, @"A storage must be registered for target %@", target);
135
  return storage;
136
}
137
 
138
/** Returns the current upload conditions after making determinations about the network connection.
139
 *
140
 * @return The current upload conditions.
141
 */
142
- (GDTCORUploadConditions)uploadConditions {
143
  GDTCORNetworkReachabilityFlags currentFlags = [GDTCORReachability currentFlags];
144
  BOOL networkConnected = GDTCORReachabilityFlagsReachable(currentFlags);
145
  if (!networkConnected) {
146
    return GDTCORUploadConditionNoNetwork;
147
  }
148
  BOOL isWWAN = GDTCORReachabilityFlagsContainWWAN(currentFlags);
149
  if (isWWAN) {
150
    return GDTCORUploadConditionMobileData;
151
  } else {
152
    return GDTCORUploadConditionWifiData;
153
  }
154
}
155
 
156
#pragma mark - GDTCORLifecycleProtocol
157
 
158
- (void)appWillForeground:(GDTCORApplication *)app {
159
  // -startTimer is thread-safe.
160
  [self startTimer];
161
  [self signalToStoragesToCheckExpirations];
162
}
163
 
164
- (void)appWillBackground:(GDTCORApplication *)app {
165
  dispatch_async(_coordinationQueue, ^{
166
    [self stopTimer];
167
  });
168
}
169
 
170
- (void)appWillTerminate:(GDTCORApplication *)application {
171
  dispatch_sync(_coordinationQueue, ^{
172
    [self stopTimer];
173
  });
174
}
175
 
176
@end