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 <TargetConditionals.h>
18
#if TARGET_OS_IOS || TARGET_OS_TV
19
 
20
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
21
 
22
#import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
23
#import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMFetchResponseParser.h"
24
#import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMMessageContentDataWithImageURL.h"
25
#import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMMessageDefinition.h"
26
#import "FirebaseInAppMessaging/Sources/Private/Flows/FIRIAMMsgFetcherUsingRestful.h"
27
#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMFetchFlow.h"
28
#import "FirebaseInAppMessaging/Sources/Private/Runtime/FIRIAMSDKSettings.h"
29
 
30
static NSInteger const SuccessHTTPStatusCode = 200;
31
 
32
@interface FIRIAMMsgFetcherUsingRestful ()
33
@property(readonly) NSURLSession *URLSession;
34
@property(readonly, copy, nonatomic) NSString *serverHostName;
35
@property(readonly, copy, nonatomic) NSString *appBundleID;
36
@property(readonly, copy, nonatomic) NSString *httpProtocol;
37
@property(readonly, copy, nonatomic) NSString *fbProjectNumber;
38
@property(readonly, copy, nonatomic) NSString *apiKey;
39
@property(readonly, copy, nonatomic) NSString *firebaseAppId;
40
@property(readonly, nonatomic) FIRIAMServerMsgFetchStorage *fetchStorage;
41
@property(readonly, nonatomic) FIRIAMClientInfoFetcher *clientInfoFetcher;
42
@property(readonly, nonatomic) FIRIAMFetchResponseParser *responseParser;
43
@end
44
 
45
@implementation FIRIAMMsgFetcherUsingRestful
46
- (instancetype)initWithHost:(NSString *)serverHost
47
                HTTPProtocol:(NSString *)HTTPProtocol
48
                     project:(NSString *)fbProjectNumber
49
                 firebaseApp:(NSString *)fbAppId
50
                      APIKey:(NSString *)apiKey
51
                fetchStorage:(FIRIAMServerMsgFetchStorage *)fetchStorage
52
           instanceIDFetcher:(FIRIAMClientInfoFetcher *)clientInfoFetcher
53
             usingURLSession:(nullable NSURLSession *)URLSession
54
              responseParser:(FIRIAMFetchResponseParser *)responseParser {
55
  if (self = [super init]) {
56
    _URLSession = URLSession ? URLSession : [NSURLSession sharedSession];
57
    _serverHostName = [serverHost copy];
58
    _fbProjectNumber = [fbProjectNumber copy];
59
    _firebaseAppId = [fbAppId copy];
60
    _httpProtocol = [HTTPProtocol copy];
61
    _apiKey = [apiKey copy];
62
    _clientInfoFetcher = clientInfoFetcher;
63
    _fetchStorage = fetchStorage;
64
    _appBundleID = [NSBundle mainBundle].bundleIdentifier;
65
    _responseParser = responseParser;
66
  }
67
  return self;
68
}
69
 
70
- (void)updatePostFetchData:(NSMutableDictionary *)postData
71
         withImpressionList:(NSArray<FIRIAMImpressionRecord *> *)impressionList
72
           instanceIDString:(nonnull NSString *)IIDValue
73
                   IIDToken:(nonnull NSString *)IIDToken {
74
  NSMutableArray *impressionListForPost = [[NSMutableArray alloc] init];
75
  for (FIRIAMImpressionRecord *nextImpressionRecord in impressionList) {
76
    NSDictionary *nextImpression = @{
77
      @"campaign_id" : nextImpressionRecord.messageID,
78
      @"impression_timestamp_millis" : @(nextImpressionRecord.impressionTimeInSeconds * 1000)
79
    };
80
    [impressionListForPost addObject:nextImpression];
81
  }
82
  [postData setObject:impressionListForPost forKey:@"already_seen_campaigns"];
83
 
84
  if (IIDValue) {
85
    NSDictionary *clientAppInfo = @{
86
      @"gmp_app_id" : self.firebaseAppId,
87
      @"app_instance_id" : IIDValue,
88
      @"app_instance_id_token" : IIDToken
89
    };
90
    [postData setObject:clientAppInfo forKey:@"requesting_client_app"];
91
  }
92
 
93
  NSMutableArray *clientSignals = [@{} mutableCopy];
94
 
95
  // set client signal fields only when they are present
96
  if ([self.clientInfoFetcher getAppVersion]) {
97
    [clientSignals setValue:[self.clientInfoFetcher getAppVersion] forKey:@"app_version"];
98
  }
99
 
100
  if ([self.clientInfoFetcher getOSVersion]) {
101
    [clientSignals setValue:[self.clientInfoFetcher getOSVersion] forKey:@"platform_version"];
102
  }
103
 
104
  if ([self.clientInfoFetcher getDeviceLanguageCode]) {
105
    [clientSignals setValue:[self.clientInfoFetcher getDeviceLanguageCode] forKey:@"language_code"];
106
  }
107
 
108
  if ([self.clientInfoFetcher getTimezone]) {
109
    [clientSignals setValue:[self.clientInfoFetcher getTimezone] forKey:@"time_zone"];
110
  }
111
 
112
  [postData setObject:clientSignals forKey:@"client_signals"];
113
}
114
 
115
- (void)fetchMessagesWithImpressionList:(NSArray<FIRIAMImpressionRecord *> *)impressonList
116
                           withIIDvalue:(NSString *)iidValue
117
                               IIDToken:(NSString *)iidToken
118
                             completion:(FIRIAMFetchMessageCompletionHandler)completion {
119
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
120
  [request setHTTPMethod:@"POST"];
121
 
122
  if (_appBundleID.length) {
123
    // Handle the case in which the API key is being restricted to specific iOS app bundle,
124
    // which can be set on Google Cloud console side for API key credentials.
125
    [request addValue:_appBundleID forHTTPHeaderField:@"X-Ios-Bundle-Identifier"];
126
  }
127
 
128
  [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
129
  [request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
130
  [request addValue:iidToken forHTTPHeaderField:@"x-goog-firebase-installations-auth"];
131
 
132
  NSMutableDictionary *postFetchDict = [[NSMutableDictionary alloc] init];
133
  [self updatePostFetchData:postFetchDict
134
         withImpressionList:impressonList
135
           instanceIDString:iidValue
136
                   IIDToken:iidToken];
137
 
138
  NSData *postFetchData = [NSJSONSerialization dataWithJSONObject:postFetchDict
139
                                                          options:0
140
                                                            error:nil];
141
 
142
  NSString *requestURLString = [NSString
143
      stringWithFormat:@"%@://%@/v1/sdkServing/projects/%@/eligibleCampaigns:fetch?key=%@",
144
                       self.httpProtocol, self.serverHostName, self.fbProjectNumber, self.apiKey];
145
  [request setURL:[NSURL URLWithString:requestURLString]];
146
  [request setHTTPBody:postFetchData];
147
 
148
  FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM130001",
149
              @"Making a restful API request for pulling messages with fetch POST body as %@ "
150
               "and request headers as %@",
151
              postFetchDict, request.allHTTPHeaderFields);
152
 
153
  NSURLSessionDataTask *postDataTask = [self.URLSession
154
      dataTaskWithRequest:request
155
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
156
          if (error) {
157
            FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130002",
158
                          @"Internal error: encountered error in pulling messages from server"
159
                           ":%@",
160
                          error);
161
            completion(nil, nil, 0, error);
162
          } else {
163
            if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
164
              NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
165
              if (httpResponse.statusCode == SuccessHTTPStatusCode) {
166
                // got response data successfully
167
                FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM130007",
168
                            @"Fetch API response headers are %@", [httpResponse allHeaderFields]);
169
 
170
                NSError *errorJson = nil;
171
                NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data
172
                                                                             options:kNilOptions
173
                                                                               error:&errorJson];
174
                if (errorJson) {
175
                  FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130003",
176
                                @"Failed to parse the response body as JSON string %@", errorJson);
177
                  completion(nil, nil, 0, errorJson);
178
                } else {
179
                  NSInteger discardCount;
180
                  NSNumber *nextFetchWaitTimeFromResponse;
181
                  NSArray<FIRIAMMessageDefinition *> *messages = [self.responseParser
182
                      parseAPIResponseDictionary:responseDict
183
                               discardedMsgCount:&discardCount
184
                          fetchWaitTimeInSeconds:&nextFetchWaitTimeFromResponse];
185
 
186
                  if (messages) {
187
                    FIRLogDebug(kFIRLoggerInAppMessaging, @"I-IAM130012",
188
                                @"API request for fetching messages and parsing the response was "
189
                                 "successful.");
190
 
191
                    [self.fetchStorage
192
                        saveResponseDictionary:responseDict
193
                                withCompletion:^(BOOL success) {
194
                                  if (!success)
195
                                    FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130010",
196
                                                  @"Failed to persist server fetch response");
197
                                }];
198
                    // always report success regardless of whether we are able to persist into
199
                    // storage. they should get fixed in the next fetch cycle if it happens.
200
                    completion(messages, nextFetchWaitTimeFromResponse, discardCount, nil);
201
                  } else {
202
                    NSString *errorDesc =
203
                        @"Failed to recognize the fiam messages in the server response";
204
                    FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130011", @"%@", errorDesc);
205
                    NSError *error =
206
                        [NSError errorWithDomain:kFirebaseInAppMessagingErrorDomain
207
                                            code:0
208
                                        userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
209
                    completion(nil, nil, 0, error);
210
                  }
211
                }
212
              } else {
213
                NSString *responseBody = [[NSString alloc] initWithData:data
214
                                                               encoding:NSUTF8StringEncoding];
215
 
216
                FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130004",
217
                              @"Failed restful api request to fetch in-app messages: seeing http "
218
                              @"status code as %ld with body as %@",
219
                              (long)httpResponse.statusCode, responseBody);
220
 
221
                NSError *error = [NSError errorWithDomain:NSURLErrorDomain
222
                                                     code:httpResponse.statusCode
223
                                                 userInfo:nil];
224
                completion(nil, nil, 0, error);
225
              }
226
            } else {
227
              NSString *errorDesc = @"Got a non http response type from fetch endpoint";
228
              FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130005", @"%@", errorDesc);
229
 
230
              NSError *error = [NSError errorWithDomain:kFirebaseInAppMessagingErrorDomain
231
                                                   code:0
232
                                               userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
233
              completion(nil, nil, 0, error);
234
            }
235
          }
236
        }];
237
 
238
  if (postDataTask == nil) {
239
    NSString *errorDesc =
240
        @"Internal error: NSURLSessionDataTask failed to be created due to possibly "
241
         "incorrect parameters";
242
    FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM130006", @"%@", errorDesc);
243
    NSError *error = [NSError errorWithDomain:kFirebaseInAppMessagingErrorDomain
244
                                         code:0
245
                                     userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
246
    completion(nil, nil, 0, error);
247
  } else {
248
    [postDataTask resume];
249
  }
250
}
251
 
252
#pragma mark - protocol FIRIAMMessageFetcher
253
- (void)fetchMessagesWithImpressionList:(NSArray<FIRIAMImpressionRecord *> *)impressonList
254
                         withCompletion:(FIRIAMFetchMessageCompletionHandler)completion {
255
  // First step is to fetch the instance id value and token on the fly. We are not caching the data
256
  // since the fetch operation frequency is low enough that we are not concerned about its impact
257
  // on server load and this guarantees that we always have an up-to-date iid values and tokens.
258
  [self.clientInfoFetcher
259
      fetchFirebaseInstallationDataWithProjectNumber:self.fbProjectNumber
260
                                      withCompletion:^(NSString *_Nullable FID,
261
                                                       NSString *_Nullable FISToken,
262
                                                       NSError *_Nullable error) {
263
                                        if (error) {
264
                                          FIRLogWarning(
265
                                              kFIRLoggerInAppMessaging, @"I-IAM130008",
266
                                              @"Not able to get iid value and/or token for "
267
                                              @"talking to server: %@",
268
                                              error.localizedDescription);
269
                                          completion(nil, nil, 0, error);
270
                                        } else {
271
                                          [self fetchMessagesWithImpressionList:impressonList
272
                                                                   withIIDvalue:FID
273
                                                                       IIDToken:FISToken
274
                                                                     completion:completion];
275
                                        }
276
                                      }];
277
}
278
@end
279
 
280
#endif  // TARGET_OS_IOS || TARGET_OS_TV