FIRIAMMessageContentDataWithImageURL.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. #import <TargetConditionals.h>
  17. #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION
  18. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  19. #import "FirebaseInAppMessaging/Sources/FIRCore+InAppMessaging.h"
  20. #import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMMessageContentData.h"
  21. #import "FirebaseInAppMessaging/Sources/Private/Data/FIRIAMMessageContentDataWithImageURL.h"
  22. #import "FirebaseInAppMessaging/Sources/Public/FirebaseInAppMessaging/FIRInAppMessagingErrors.h"
  23. #import "FirebaseInAppMessaging/Sources/Runtime/FIRIAMSDKRuntimeErrorCodes.h"
  24. static NSInteger const SuccessHTTPStatusCode = 200;
  25. @interface FIRIAMMessageContentDataWithImageURL ()
  26. @property(nonatomic, readwrite, nullable, copy) NSString *titleText;
  27. @property(nonatomic, readwrite, nonnull, copy) NSString *bodyText;
  28. @property(nonatomic, copy, nullable) NSString *actionButtonText;
  29. @property(nonatomic, copy, nullable) NSString *secondaryActionButtonText;
  30. @property(nonatomic, copy, nullable) NSURL *actionURL;
  31. @property(nonatomic, copy, nullable) NSURL *secondaryActionURL;
  32. @property(nonatomic, nullable, copy) NSURL *imageURL;
  33. @property(nonatomic, nullable, copy) NSURL *landscapeImageURL;
  34. @property(readonly) NSURLSession *URLSession;
  35. @end
  36. @implementation FIRIAMMessageContentDataWithImageURL
  37. - (instancetype)initWithMessageTitle:(nullable NSString *)title
  38. messageBody:(nullable NSString *)body
  39. actionButtonText:(nullable NSString *)actionButtonText
  40. secondaryActionButtonText:(nullable NSString *)secondaryActionButtonText
  41. actionURL:(nullable NSURL *)actionURL
  42. secondaryActionURL:(nullable NSURL *)secondaryActionURL
  43. imageURL:(nullable NSURL *)imageURL
  44. landscapeImageURL:(nullable NSURL *)landscapeImageURL
  45. usingURLSession:(nullable NSURLSession *)URLSession {
  46. if (self = [super init]) {
  47. _titleText = title;
  48. _bodyText = body;
  49. _imageURL = imageURL;
  50. _landscapeImageURL = landscapeImageURL;
  51. _actionButtonText = actionButtonText;
  52. _secondaryActionButtonText = secondaryActionButtonText;
  53. _actionURL = actionURL;
  54. _secondaryActionURL = secondaryActionURL;
  55. if (imageURL) {
  56. _URLSession = URLSession ? URLSession : [NSURLSession sharedSession];
  57. }
  58. }
  59. return self;
  60. }
  61. #pragma protocol FIRIAMMessageContentData
  62. - (NSString *)description {
  63. return [NSString stringWithFormat:@"Message content: title '%@',"
  64. "body '%@', imageURL '%@', action URL '%@'",
  65. self.titleText, self.bodyText, self.imageURL, self.actionURL];
  66. }
  67. - (NSString *)getTitleText {
  68. return _titleText;
  69. }
  70. - (NSString *)getBodyText {
  71. return _bodyText;
  72. }
  73. - (nullable NSString *)getActionButtonText {
  74. return _actionButtonText;
  75. }
  76. - (void)loadImageDataWithBlock:(void (^)(NSData *_Nullable standardImageData,
  77. NSData *_Nullable landscapeImageData,
  78. NSError *_Nullable error))block {
  79. if (!block) {
  80. // no need for any further action if block is nil
  81. return;
  82. }
  83. if (!_imageURL && !_landscapeImageURL) {
  84. // no image data since image url is nil
  85. block(nil, nil, nil);
  86. } else if (!_landscapeImageURL) {
  87. // Only fetch standard image.
  88. [self fetchImageFromURL:_imageURL
  89. withBlock:^(NSData *_Nullable imageData, NSError *_Nullable error) {
  90. block(imageData, nil, error);
  91. }];
  92. } else if (!_imageURL) {
  93. // Only fetch portrait image.
  94. [self fetchImageFromURL:_landscapeImageURL
  95. withBlock:^(NSData *_Nullable imageData, NSError *_Nullable error) {
  96. block(nil, imageData, error);
  97. }];
  98. } else {
  99. // Fetch both images separately, call completion when they're both fetched.
  100. __block NSData *portrait = nil;
  101. __block NSData *landscape = nil;
  102. __block NSError *landscapeImageLoadError = nil;
  103. [self fetchImageFromURL:_imageURL
  104. withBlock:^(NSData *_Nullable imageData, NSError *_Nullable error) {
  105. __weak FIRIAMMessageContentDataWithImageURL *weakSelf = self;
  106. // If the portrait image fails to load, we treat this as a failure.
  107. if (error) {
  108. // Cancel landscape image fetch.
  109. [weakSelf.URLSession invalidateAndCancel];
  110. block(nil, nil, error);
  111. return;
  112. }
  113. portrait = imageData;
  114. if (landscape || landscapeImageLoadError) {
  115. block(portrait, landscape, nil);
  116. }
  117. }];
  118. [self fetchImageFromURL:_landscapeImageURL
  119. withBlock:^(NSData *_Nullable imageData, NSError *_Nullable error) {
  120. if (error) {
  121. landscapeImageLoadError = error;
  122. } else {
  123. landscape = imageData;
  124. }
  125. if (portrait) {
  126. block(portrait, landscape, nil);
  127. }
  128. }];
  129. }
  130. }
  131. - (void)fetchImageFromURL:(NSURL *)imageURL
  132. withBlock:(void (^)(NSData *_Nullable imageData, NSError *_Nullable error))block {
  133. NSURLRequest *imageDataRequest = [NSURLRequest requestWithURL:imageURL];
  134. NSURLSessionDataTask *task = [_URLSession
  135. dataTaskWithRequest:imageDataRequest
  136. completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  137. if (error) {
  138. FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM000003", @"Error in fetching image: %@",
  139. error);
  140. block(nil, error);
  141. } else {
  142. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  143. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  144. if (httpResponse.statusCode == SuccessHTTPStatusCode) {
  145. if (httpResponse.MIMEType == nil || ![httpResponse.MIMEType hasPrefix:@"image"]) {
  146. NSString *errorDesc =
  147. [NSString stringWithFormat:@"No image MIME type %@"
  148. " detected for URL %@",
  149. httpResponse.MIMEType, self.imageURL];
  150. FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM000004", @"%@", errorDesc);
  151. NSError *error =
  152. [NSError errorWithDomain:FIRInAppMessagingErrorDomain
  153. code:FIRIAMSDKRuntimeErrorNonImageMimetypeFromImageURL
  154. userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
  155. block(nil, error);
  156. } else {
  157. block(data, nil);
  158. }
  159. } else {
  160. NSString *errorDesc =
  161. [NSString stringWithFormat:@"Failed HTTP request to crawl image %@: "
  162. "HTTP status code as %ld",
  163. self->_imageURL, (long)httpResponse.statusCode];
  164. FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM000001", @"%@", errorDesc);
  165. NSError *error = [NSError errorWithDomain:NSURLErrorDomain
  166. code:httpResponse.statusCode
  167. userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
  168. block(nil, error);
  169. }
  170. } else {
  171. NSString *errorDesc =
  172. [NSString stringWithFormat:@"Internal error: got a non HTTP response from "
  173. @"fetching image for image URL as %@",
  174. imageURL];
  175. FIRLogWarning(kFIRLoggerInAppMessaging, @"I-IAM000002", @"%@", errorDesc);
  176. NSError *error = [NSError errorWithDomain:NSURLErrorDomain
  177. code:FIRIAMSDKRuntimeErrorNonHTTPResponseForImage
  178. userInfo:@{NSLocalizedDescriptionKey : errorDesc}];
  179. block(nil, error);
  180. }
  181. }
  182. }];
  183. [task resume];
  184. }
  185. @end
  186. #endif // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION