diff --git a/Extension App/ContentExtension/Info.plist b/Extension App/ContentExtension/Info.plist index 671bd94..337085a 100644 --- a/Extension App/ContentExtension/Info.plist +++ b/Extension App/ContentExtension/Info.plist @@ -1,26 +1,26 @@ - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ContentExtension - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - XPC! - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - NSExtension + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ContentExtension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension NSExtensionAttributes @@ -30,19 +30,27 @@ WEG_CAROUSEL_V1 WEG_RATING_V1 + WEG_RICH_V1 + WEG_RICH_V2 + WEG_RICH_V3 + WEG_RICH_V4 + WEG_RICH_V5 + WEG_RICH_V6 + WEG_RICH_V7 + WEG_RICH_V8 UNNotificationExtensionInitialContentSizeRatio - 1 + 1 NSExtensionMainStoryboard MainInterface NSExtensionPointIdentifier com.apple.usernotifications.content-extension - NSAppTransportSecurity - - NSAllowsArbitraryLoads - + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + - diff --git a/Extension App/WEGExtensionApp.xcodeproj/project.pbxproj b/Extension App/WEGExtensionApp.xcodeproj/project.pbxproj index a7e32a4..36a5f29 100644 --- a/Extension App/WEGExtensionApp.xcodeproj/project.pbxproj +++ b/Extension App/WEGExtensionApp.xcodeproj/project.pbxproj @@ -62,6 +62,14 @@ 56A49FB8EB9E003121E4C723 /* Pods-WEGExtensionApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WEGExtensionApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WEGExtensionApp/Pods-WEGExtensionApp.debug.xcconfig"; sourceTree = ""; }; 8167C6E259A361F9B2183151 /* libPods-WEGExtensionApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-WEGExtensionApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8288374F4CC9F6A69DC1929F /* Pods-ContentExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ContentExtension/Pods-ContentExtension.debug.xcconfig"; sourceTree = ""; }; + A67089DF27A019E200DF27AC /* UIColor+DarkMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIColor+DarkMode.h"; sourceTree = ""; }; + A67089E027A019EF00DF27AC /* UIColor+DarkMode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIColor+DarkMode.m"; sourceTree = ""; }; + A693721927C0134F00045C60 /* WEXTextPushNotificationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WEXTextPushNotificationViewController.h; sourceTree = ""; }; + A693721A27C0135D00045C60 /* WEXTextPushNotificationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WEXTextPushNotificationViewController.m; sourceTree = ""; }; + A6DDEE99279A7A8D007FADE1 /* NSMutableAttributedString+Additions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMutableAttributedString+Additions.h"; sourceTree = ""; }; + A6DDEE9A279A7B22007FADE1 /* NSMutableAttributedString+Additions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSMutableAttributedString+Additions.m"; sourceTree = ""; }; + A6F5D066278D6EA1005A96AA /* WEXBannerPushNotificationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WEXBannerPushNotificationViewController.h; sourceTree = ""; }; + A6F5D067278D6EAE005A96AA /* WEXBannerPushNotificationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WEXBannerPushNotificationViewController.m; sourceTree = ""; }; B554331A99838E3647290F86 /* Pods-ServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ServiceExtension/Pods-ServiceExtension.debug.xcconfig"; sourceTree = ""; }; BF5C12043F19FA4C36D82355 /* Pods-WEGExtensionApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WEGExtensionApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-WEGExtensionApp/Pods-WEGExtensionApp.release.xcconfig"; sourceTree = ""; }; CF34E9CCD03C284D4C70479B /* Pods-ContentExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-ContentExtension/Pods-ContentExtension.release.xcconfig"; sourceTree = ""; }; @@ -265,6 +273,14 @@ EC9A3DBA21B6B15300C63B7D /* WEXRichPushNotificationViewController.h */, EC9A3DB721B6B15300C63B7D /* WEXRichPushNotificationViewController.m */, EC9A3DB521B6B15300C63B7D /* WEXRichPushNotificationViewController+Private.h */, + A6F5D066278D6EA1005A96AA /* WEXBannerPushNotificationViewController.h */, + A6F5D067278D6EAE005A96AA /* WEXBannerPushNotificationViewController.m */, + A6DDEE99279A7A8D007FADE1 /* NSMutableAttributedString+Additions.h */, + A6DDEE9A279A7B22007FADE1 /* NSMutableAttributedString+Additions.m */, + A67089DF27A019E200DF27AC /* UIColor+DarkMode.h */, + A67089E027A019EF00DF27AC /* UIColor+DarkMode.m */, + A693721927C0134F00045C60 /* WEXTextPushNotificationViewController.h */, + A693721A27C0135D00045C60 /* WEXTextPushNotificationViewController.m */, ); path = ContentExtension; sourceTree = ""; diff --git a/Extension App/WEGExtensionApp/AppDelegate.swift b/Extension App/WEGExtensionApp/AppDelegate.swift index e900faf..0c3a085 100644 --- a/Extension App/WEGExtensionApp/AppDelegate.swift +++ b/Extension App/WEGExtensionApp/AppDelegate.swift @@ -8,6 +8,7 @@ import UIKit import WebEngage +import UserNotifications @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -15,12 +16,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - WebEngage.sharedInstance()?.application(application, didFinishLaunchingWithOptions: launchOptions, autoRegister: true) - - NSLog("Extension Demo app launched " + #function) - + WebEngage.sharedInstance().user.login("ExtensionQWE1") return true } } - diff --git a/Extension App/WEGExtensionApp/Info.plist b/Extension App/WEGExtensionApp/Info.plist index 50aabe6..d08776d 100644 --- a/Extension App/WEGExtensionApp/Info.plist +++ b/Extension App/WEGExtensionApp/Info.plist @@ -53,7 +53,7 @@ WEGEnableLocationAuthorizationRequest IN_USE WEGLicenseCode - 311c5607 + d3a4a436 WEGLogLevel VERBOSE NSAppTransportSecurity diff --git a/WebEngageAppEx.podspec b/WebEngageAppEx.podspec index 527c4e2..46bf928 100644 --- a/WebEngageAppEx.podspec +++ b/WebEngageAppEx.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = 'WebEngageAppEx' - spec.version = '0.12.0' + spec.version = '1.0.0' spec.summary = 'App Extension Target SDK for WebEngage for Rich Push Notifications support.' @@ -10,7 +10,7 @@ Pod::Spec.new do |spec| DESC spec.license = 'MIT' - spec.author = 'Saumitra Bhave', 'Uzma Sayyed', 'Unmesh Rathod' + spec.author = 'Saumitra Bhave', 'Uzma Sayyed', 'Unmesh Rathod', 'Bhavesh Sarwar' spec.homepage = 'https://webengage.com' spec.social_media_url = 'http://twitter.com/webengage' spec.documentation_url = 'https://docs.webengage.com/docs/ios-getting-started' diff --git a/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.h b/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.h new file mode 100644 index 0000000..76b791d --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.h @@ -0,0 +1,18 @@ +// +// NSMutableAttributedString+Additions.h +// WebEngage +// +// Copyright (c) 2017 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import + +@interface NSMutableAttributedString (Additions) + +- (void)updateDefaultTextColor; + +- (void)setFontFaceWithFont:(UIFont *)font; + +- (void)trimWhiteSpace; + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.m b/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.m new file mode 100644 index 0000000..531bb60 --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/NSMutableAttributedString+Additions.m @@ -0,0 +1,82 @@ +// +// NSMutableAttributedString+Additions.m +// WebEngage +// +// Copyright (c) 2017 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import +#import "NSMutableAttributedString+Additions.h" +#import "UIColor+DarkMode.h" + +@implementation NSMutableAttributedString (Additions) + +- (void)updateDefaultTextColor { + if (@available(iOS 13.0, *)) { + [self beginEditing]; + [self enumerateAttribute:NSForegroundColorAttributeName + inRange:NSMakeRange(0, self.length) + options:0 + usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + + UIColor *color = (UIColor *)value; + UIColor *labelColor = [UIColor WEXLabelColor]; + NSString *colorHex = [self hexStringFromColor:color]; + + if ([colorHex isEqualToString:@"000000"]) { + [self removeAttribute:NSForegroundColorAttributeName range:range]; + [self addAttribute:NSForegroundColorAttributeName value:labelColor range:range]; + } + }]; + [self endEditing]; + } +} + +- (NSString *)hexStringFromColor:(UIColor *)color { + const CGFloat *components = CGColorGetComponents(color.CGColor); + + CGFloat r = components[0]; + CGFloat g = components[1]; + CGFloat b = components[2]; + + return [NSString stringWithFormat:@"%02lX%02lX%02lX", + lroundf(r * 255), + lroundf(g * 255), + lroundf(b * 255)]; +} + +- (void)setFontFaceWithFont:(UIFont *)font { + [self beginEditing]; + [self enumerateAttribute:NSFontAttributeName + inRange:NSMakeRange(0, self.length) + options:0 + usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + + UIFont *oldFont = (UIFont *)value; + UIFontDescriptor *newFontDescriptor = [[oldFont.fontDescriptor fontDescriptorWithFamily:font.familyName] fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits]; + UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:font.pointSize]; + + if (newFont) { + [self removeAttribute:NSFontAttributeName range:range]; + [self addAttribute:NSFontAttributeName value:newFont range:range]; + } + }]; + [self endEditing]; +} + +- (void)trimWhiteSpace { + NSCharacterSet *legalChars = [[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet]; + NSRange startRange = [self.string rangeOfCharacterFromSet: legalChars]; + NSRange endRange = [self.string rangeOfCharacterFromSet: legalChars options:NSBackwardsSearch]; + if (startRange.location == NSNotFound || endRange.location == NSNotFound) { + // there are no legal characters in self --- it is ALL whitespace, and so we 'trim' everything leaving an empty string + [self setAttributedString:[NSAttributedString new]]; + } + else { + NSUInteger startLocation = NSMaxRange(startRange), endLocation = endRange.location; + NSRange range = NSMakeRange(startLocation - 1, endLocation - startLocation + 2); + [self setAttributedString:[self attributedSubstringFromRange:range]]; + } +} + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.h b/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.h new file mode 100644 index 0000000..551427d --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.h @@ -0,0 +1,22 @@ +// +// UIColor+DarkMode.h +// WebEngage +// +// Copyright (c) 2017 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import + +@interface UIColor (DarkMode) + ++ (UIColor *)WEXWhiteColor; + ++ (UIColor *)WEXGreyColor; + ++ (UIColor *)WEXLabelColor; + ++ (UIColor *)WEXLightTextColor; + ++ (UIColor *)colorFromHexString:(NSString *)hexString defaultColor:(UIColor *)defaultColor; + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.m b/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.m new file mode 100644 index 0000000..2859dec --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/UIColor+DarkMode.m @@ -0,0 +1,63 @@ +// +// UIColor+DarkMode.m +// WebEngage +// +// Copyright (c) 2017 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import +#import "UIColor+DarkMode.h" + +@implementation UIColor (DarkMode) + ++ (UIColor *)WEXWhiteColor { + if (@available(iOS 13.0, *)) { + return UIColor.systemBackgroundColor; + } else { + return UIColor.whiteColor; + } +} + ++ (UIColor *)WEXGreyColor { + if (@available(iOS 13.0, *)) { + return UIColor.systemGrayColor; + } else { + return UIColor.lightGrayColor; + } +} + ++ (UIColor *)WEXLabelColor { + if (@available(iOS 13.0, *)) { + return UIColor.labelColor; + } else { + return UIColor.blackColor; + } +} + ++ (UIColor *)WEXLightTextColor { + if (@available(iOS 13.0, *)) { + return UIColor.systemGray4Color; + } else { + return UIColor.lightTextColor; + } +} + ++ (UIColor *)colorFromHexString:(NSString *)hexString defaultColor:(UIColor *)defaultColor { + if (hexString == (id)[NSNull null] || hexString.length == 0) { + return defaultColor; + } + + unsigned rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:hexString]; + + if ([hexString hasPrefix:@"#"]) { + [scanner setScanLocation:1]; // bypass '#' character + } else { + [scanner setScanLocation:0]; + } + + [scanner scanHexInt:&rgbValue]; + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; +} + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.h b/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.h new file mode 100644 index 0000000..b82e6a3 --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.h @@ -0,0 +1,24 @@ +// +// WEXBannerPushNotificationViewController.h +// WebEngage +// +// Copyright (c) 2022 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 +#import +#import +#import "WEXRichPushNotificationViewController+Private.h" +#import "WEXRichPushLayout.h" +#endif + +@interface WEXBannerPushNotificationViewController +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 +: WEXRichPushLayout +#else +: NSObject +#endif + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.m b/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.m new file mode 100644 index 0000000..030bb01 --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/WEXBannerPushNotificationViewController.m @@ -0,0 +1,241 @@ +// +// WEXBannerPushNotificationViewController.m +// WebEngage +// +// Copyright (c) 2022 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import "WEXBannerPushNotificationViewController.h" +#import "WEXRichPushNotificationViewController+Private.h" +#import "UIColor+DarkMode.h" + +#define CONTENT_PADDING 10 +#define TITLE_BODY_SPACE 5 +#define LANDSCAPE_ASPECT 0.5 + +API_AVAILABLE(ios(10.0)) +@interface WEXBannerPushNotificationViewController () + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 + +@property (nonatomic) UNNotification *notification; + +#endif + +@end + +@implementation WEXBannerPushNotificationViewController + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 + +- (void)didReceiveNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0)) { + self.notification = notification; + [self initialiseViewHierarchy]; +} + +- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion{ + completion(UNNotificationContentExtensionResponseOptionDismissAndForwardAction); +} + +- (void)initialiseViewHierarchy { + self.view.backgroundColor = [UIColor WEXWhiteColor]; + + UIView *superViewWrapper = [[UIView alloc] init]; + [self.view addSubview:superViewWrapper]; + + UIView *mainContentView = [[UIView alloc] init]; + [superViewWrapper addSubview:mainContentView]; + + [self setupBannerImageView]; +} + +- (void)setupBannerImageView { + UIView *superViewWrapper = self.view.subviews[0]; + UIView *mainContentView = superViewWrapper.subviews[0]; + + NSDictionary *expandableDetails = self.notification.request.content.userInfo[@"expandableDetails"]; + + UIImageView *imageView = [[UIImageView alloc] init]; + if (expandableDetails[@"image"]) { + if (self.notification.request.content.attachments + && self.notification.request.content.attachments.count > 0) { + + if (@available(iOS 10.0, *)) { + UNNotificationAttachment *attachment = self.notification.request.content.attachments.firstObject; + + if ([attachment.URL startAccessingSecurityScopedResource]) { + NSData *imageData = [NSData dataWithContentsOfFile:attachment.URL.path]; + UIImage *image = [UIImage imageWithData:imageData]; + + [attachment.URL stopAccessingSecurityScopedResource]; + + if (image) { + imageView.image = image; + } else { + NSLog(@"Image not present in cache!"); + } + } + } else { + NSLog(@"Expected to be running iOS version 10 or above"); + } + } else { + NSLog(@"Attachment not present for: %@", expandableDetails[@"image"]); + } + } else { + NSLog(@"Image not present in payload: %@", expandableDetails[@"image"]); + } + + imageView.contentMode = UIViewContentModeScaleAspectFill; + [mainContentView addSubview:imageView]; + [self setupLabelsContainer]; +} + +- (void)setupLabelsContainer { + UIView *superViewWrapper = self.view.subviews[0]; + NSString *colorHex = self.notification.request.content.userInfo[@"expandableDetails"][@"bckColor"]; + + UIView *richContentView = [[UIView alloc] init]; + richContentView.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; + + NSDictionary *expandedDetails = self.notification.request.content.userInfo[@"expandableDetails"]; + NSString *title = expandedDetails[@"rt"]; + NSString *subtitle = expandedDetails[@"rst"]; + NSString *message = expandedDetails[@"rm"]; + + BOOL titlePresent = title && ![title isEqualToString:@""]; + BOOL subTitlePresent = subtitle && ![subtitle isEqualToString:@""]; + BOOL messagePresent = message && ![message isEqualToString:@""]; + + if (!titlePresent) { + title = self.notification.request.content.title; + } + if (!subTitlePresent) { + subtitle = self.notification.request.content.subtitle; + } + if (!messagePresent) { + message = self.notification.request.content.body; + } + + UILabel *richTitleLabel = [[UILabel alloc] init]; + richTitleLabel.attributedText = [self.viewController getHtmlParsedString:title isTitle:YES bckColor:colorHex]; + richTitleLabel.textAlignment = [self.viewController naturalTextAligmentForText:richTitleLabel.text]; + + UILabel *richSubLabel = [[UILabel alloc] init]; + richSubLabel.attributedText = [self.viewController getHtmlParsedString:subtitle isTitle:YES bckColor:colorHex]; + richSubLabel.textAlignment = [self.viewController naturalTextAligmentForText:richSubLabel.text]; + + UILabel *richBodyLabel = [[UILabel alloc] init]; + richBodyLabel.attributedText = [self.viewController getHtmlParsedString:message isTitle:NO bckColor:colorHex]; + richBodyLabel.textAlignment = [self.viewController naturalTextAligmentForText:richBodyLabel.text]; + richBodyLabel.numberOfLines = 0; + + [richContentView addSubview:richTitleLabel]; + [richContentView addSubview:richSubLabel]; + [richContentView addSubview:richBodyLabel]; + + [superViewWrapper addSubview:richContentView]; + + [self setupConstraints]; +} + +- (void)setupConstraints { + UIView *superViewWrapper = self.view.subviews[0]; + UIView *mainContentView = superViewWrapper.subviews[0]; + UIView *richContentView = superViewWrapper.subviews[1]; + + if (@available(iOS 10.0, *)) { + superViewWrapper.translatesAutoresizingMaskIntoConstraints = NO; + [superViewWrapper.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES; + [superViewWrapper.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES; + [superViewWrapper.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; + [superViewWrapper.bottomAnchor constraintEqualToAnchor:richContentView.bottomAnchor].active = YES; + + //Top level view constraints + mainContentView.translatesAutoresizingMaskIntoConstraints = NO; + [mainContentView.leadingAnchor constraintEqualToAnchor:mainContentView.superview.leadingAnchor].active = YES; + [mainContentView.trailingAnchor constraintEqualToAnchor:mainContentView.superview.trailingAnchor].active = YES; + [mainContentView.topAnchor constraintEqualToAnchor:mainContentView.superview.topAnchor].active = YES; + + richContentView.translatesAutoresizingMaskIntoConstraints = NO; + [richContentView.leadingAnchor constraintEqualToAnchor:richContentView.superview.leadingAnchor].active = YES; + [richContentView.trailingAnchor constraintEqualToAnchor:richContentView.superview.trailingAnchor].active = YES; + [richContentView.topAnchor constraintEqualToAnchor:mainContentView.bottomAnchor].active = YES; + + [self.viewController.bottomLayoutGuide.topAnchor constraintEqualToAnchor:superViewWrapper.bottomAnchor].active = YES; + + //Main Content View ImageView constraints + UIImageView *imageView = mainContentView.subviews[0]; + imageView.translatesAutoresizingMaskIntoConstraints = NO; + + UIImage *bannerImage = imageView.image; + CGFloat imageAspect = LANDSCAPE_ASPECT; + if (bannerImage && bannerImage.size.height != 0) { + imageAspect = bannerImage.size.height/bannerImage.size.width; + } else { + imageAspect = 0; + } + + [imageView.topAnchor constraintEqualToAnchor:mainContentView.topAnchor].active = YES; + [imageView.leadingAnchor constraintEqualToAnchor:mainContentView.leadingAnchor].active = YES; + [imageView.trailingAnchor constraintEqualToAnchor:mainContentView.trailingAnchor].active = YES; + [imageView.heightAnchor constraintEqualToAnchor:imageView.widthAnchor multiplier:imageAspect].active = YES; + [mainContentView.bottomAnchor constraintEqualToAnchor:imageView.bottomAnchor].active = YES; + + //Rich View labels + UIView *richTitleLabel = richContentView.subviews[0]; + UIView *richSubTitleLabel = richContentView.subviews[1]; + UIView *richBodyLabel = richContentView.subviews[2]; + + richTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richTitleLabel.topAnchor + constraintEqualToAnchor:richContentView.topAnchor + constant:CONTENT_PADDING] + .active = YES; + + richSubTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richSubTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.topAnchor + constraintEqualToAnchor:richTitleLabel.bottomAnchor + constant:0] + .active = YES; + + richBodyLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richBodyLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richBodyLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richBodyLabel.topAnchor constraintEqualToAnchor:richSubTitleLabel.bottomAnchor + constant:0] + .active = YES; + [richBodyLabel.bottomAnchor + constraintEqualToAnchor:richContentView.bottomAnchor + constant:-CONTENT_PADDING] + .active = YES; + } else { + NSLog(@"Expected to be running iOS version 10 or above"); + } +} + +#endif + +@end + diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXCarouselPushNotificationViewController.m b/WebEngageAppEx/Classes/ContentExtension/WEXCarouselPushNotificationViewController.m index a86442f..8d1de2a 100644 --- a/WebEngageAppEx/Classes/ContentExtension/WEXCarouselPushNotificationViewController.m +++ b/WebEngageAppEx/Classes/ContentExtension/WEXCarouselPushNotificationViewController.m @@ -8,6 +8,7 @@ #import "WEXCarouselPushNotificationViewController.h" #import "WEXRichPushNotificationViewController+Private.h" +#import "UIColor+DarkMode.h" #define CONTENT_PADDING 10 #define TITLE_BODY_SPACE 5 @@ -38,6 +39,9 @@ @interface WEXCarouselPushNotificationViewController () @property (atomic) NSInteger nextViewIndexToReturn; @property (atomic) BOOL isRendering; +@property (nonatomic, readwrite) NSTimer *scrollTimer; +@property (nonatomic, readwrite) BOOL shouldScroll; + #endif @end @@ -153,6 +157,8 @@ - (void)didReceiveNotification:(UNNotification *)notification API_AVAILABLE(ios( [self initialiseCarouselForNotification:notification]; + [self setupAutoScroll:notification]; + if (downloadedCount < self.carouselItems.count) { [self downloadRemaining:downloadedCount]; @@ -182,6 +188,46 @@ - (void)downloadRemaining:(NSUInteger)downloadFromIndex { } } +- (void)setupAutoScroll:(UNNotification *)notification API_AVAILABLE(ios(10.0)) { + NSString *scrollTime = notification.request.content.userInfo[@"expandableDetails"][@"ast"]; + + if (scrollTime == (id)[NSNull null] || scrollTime.length == 0) { + return; + } + + [self.scrollTimer invalidate]; + self.scrollTimer = nil; + + float intervalInMili = [scrollTime floatValue]; + float intervalSeconds = intervalInMili/1000.0; + + // Scroll if interval is more than 0 + if (intervalSeconds > 0) { + _shouldScroll = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + self.scrollTimer = [NSTimer scheduledTimerWithTimeInterval:intervalSeconds + target:self + selector:@selector(scrollContent:) + userInfo:notification repeats:YES]; + }); + } +} + +- (void)scrollContent:(NSTimer *)scrollTimer { + if (_shouldScroll) { + [self renderAnimated:(UNNotification *)[scrollTimer userInfo]]; + } else { + [self stopScrollTimer]; + } +} + +- (void)stopScrollTimer { + if (self.scrollTimer) { + [self.scrollTimer invalidate]; + self.scrollTimer = nil; + } +} + - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0)) { [self initialiseViewContainers]; @@ -197,6 +243,10 @@ - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVA // for portrait float superViewHeight = viewHeight + 2 * verticalMargins; + NSString *colorHex = notification.request.content.userInfo[@"expandableDetails"][@"bckColor"]; + self.view.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; + self.viewController.view.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; + NSString *mode = notification.request.content.userInfo[@"expandableDetails"][@"mode"]; BOOL isPortrait = mode && [mode isEqualToString:@"portrait"]; @@ -240,10 +290,10 @@ - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVA } UIView *topSeparator = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, superViewWidth, 0.5)]; - topSeparator.backgroundColor = [UIColor lightGrayColor]; + topSeparator.backgroundColor = [UIColor WEXGreyColor]; UIView *bottomSeparator = [[UIView alloc] initWithFrame:CGRectMake(0.0, superViewHeight - 0.5, superViewWidth, 0.5)]; - bottomSeparator.backgroundColor = [UIColor lightGrayColor]; + bottomSeparator.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXGreyColor]; NSDictionary *extensionAttributes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSExtension"][@"NSExtensionAttributes"]; @@ -254,23 +304,43 @@ - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVA if (defaultContentHidden) { + NSString *title = notification.request.content.userInfo[@"expandableDetails"][@"rt"]; + NSString *subtitle = notification.request.content.userInfo[@"expandableDetails"][@"rst"]; + NSString *message = notification.request.content.userInfo[@"expandableDetails"][@"rm"]; + + BOOL isRichTitle = title && ![title isEqualToString:@""]; + BOOL isRichSubtitle = subtitle && ![subtitle isEqualToString:@""]; + BOOL isRichMessage = message && ![message isEqualToString:@""]; + + if (!isRichTitle) { + title = self.notification.request.content.title; + } + if (!isRichSubtitle) { + subtitle = self.notification.request.content.subtitle; + } + if (!isRichMessage) { + message = self.notification.request.content.body; + } + // Add a notification content view for displaying title and body. UIView *notificationContentView = [[UIView alloc] init]; - notificationContentView.backgroundColor = [UIColor whiteColor]; + notificationContentView.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; UILabel *titleLabel = [[UILabel alloc] init]; - titleLabel.text = notification.request.content.title; - titleLabel.textColor = [UIColor blackColor]; - titleLabel.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + titleLabel.attributedText = [self.viewController getHtmlParsedString:title isTitle:YES bckColor:colorHex]; titleLabel.textAlignment = [self.viewController naturalTextAligmentForText:titleLabel.text]; + UILabel *subTitleLabel = [[UILabel alloc] init]; + subTitleLabel.attributedText = [self.viewController getHtmlParsedString:subtitle isTitle:YES bckColor:colorHex]; + subTitleLabel.textAlignment = [self.viewController naturalTextAligmentForText:titleLabel.text]; + UILabel *bodyLabel = [[UILabel alloc] init]; - bodyLabel.text = notification.request.content.body; - bodyLabel.textColor = [UIColor blackColor]; + bodyLabel.attributedText = [self.viewController getHtmlParsedString:message isTitle:NO bckColor:colorHex]; bodyLabel.textAlignment = [self.viewController naturalTextAligmentForText:bodyLabel.text]; bodyLabel.numberOfLines = 0; [notificationContentView addSubview:titleLabel]; + [notificationContentView addSubview:subTitleLabel]; [notificationContentView addSubview:bodyLabel]; [self.view addSubview:notificationContentView]; @@ -306,6 +376,20 @@ - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVA constant:CONTENT_PADDING] .active = YES; + subTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [subTitleLabel.leadingAnchor + constraintEqualToAnchor:notificationContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [subTitleLabel.trailingAnchor + constraintEqualToAnchor:notificationContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [subTitleLabel.topAnchor + constraintEqualToAnchor:titleLabel.bottomAnchor + constant:0] + .active = YES; + bodyLabel.translatesAutoresizingMaskIntoConstraints = NO; [bodyLabel.leadingAnchor constraintEqualToAnchor:notificationContentView.leadingAnchor @@ -315,8 +399,8 @@ - (void)initialiseCarouselForNotification:(UNNotification *)notification API_AVA constraintEqualToAnchor:notificationContentView.trailingAnchor constant:0 - CONTENT_PADDING] .active = YES; - [bodyLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor - constant:TITLE_BODY_SPACE] + [bodyLabel.topAnchor constraintEqualToAnchor:subTitleLabel.bottomAnchor + constant:0] .active = YES; [bodyLabel.bottomAnchor constraintEqualToAnchor:notificationContentView.bottomAnchor @@ -497,7 +581,7 @@ - (void)slideLeft:(UIView *)view By:(CGFloat)slide { - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion API_AVAILABLE(ios(10.0)) { - + NSLog(@"PUSHDEBUG: ResponseClicked: %@", response.actionIdentifier); BOOL dismissed = NO; if (self.isRendering) { @@ -505,7 +589,7 @@ - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response } if ([response.actionIdentifier isEqualToString:@"WEG_NEXT"]) { - + _shouldScroll = NO; [self renderAnimated:response.notification]; } else if ([response.actionIdentifier isEqualToString:@"WEG_PREV"]) { @@ -564,6 +648,8 @@ - (UIView *)viewAtPosition:(NSInteger)index { self.notification.request.content.userInfo[@"expandableDetails"][@"mode"]; BOOL isPortrait = mode && [mode isEqualToString:@"portrait"]; + NSString *colorHex = self.notification.request.content.userInfo[@"expandableDetails"][@"bckColor"]; + if (!isPortrait) { viewWidth = superViewWidth; viewHeight = viewWidth * LANDSCAPE_ASPECT; @@ -574,7 +660,7 @@ - (UIView *)viewAtPosition:(NSInteger)index { UIView *viewContainer = viewToReturn; UIImage *image = self.images[index]; - viewContainer.backgroundColor = [UIColor lightGrayColor]; + viewContainer.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXGreyColor]; UIImageView *imageView = self.imageViews[cachedViewIndex]; imageView.frame = CGRectMake(0.0, 0.0, viewWidth, viewHeight); @@ -592,7 +678,7 @@ - (UIView *)viewAtPosition:(NSInteger)index { viewWidth, descriptionViewHeight); descriptionView.alpha = DESCRIPTION_VIEW_ALPHA; - descriptionView.backgroundColor = [UIColor whiteColor]; + descriptionView.backgroundColor = [UIColor WEXWhiteColor]; UILabel *descriptionLabel = self.descriptionLabels[cachedViewIndex]; descriptionLabel.frame = @@ -601,7 +687,7 @@ - (UIView *)viewAtPosition:(NSInteger)index { descriptionLabel.text = carouselItem[@"actionText"]; descriptionLabel.textAlignment = NSTextAlignmentCenter; - descriptionLabel.textColor = [UIColor blackColor]; + descriptionLabel.textColor = [UIColor WEXLabelColor]; [descriptionView addSubview:descriptionLabel]; @@ -616,7 +702,7 @@ - (UIView *)viewAtPosition:(NSInteger)index { UIView *alphaView = self.alphaViews[cachedViewIndex]; alphaView.frame = CGRectMake(0.0, 0.0, viewWidth, viewHeight); alphaView.alpha = 0.0; - alphaView.backgroundColor = [UIColor whiteColor]; + alphaView.backgroundColor = [UIColor WEXWhiteColor]; [viewContainer addSubview:alphaView]; } diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.h b/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.h index 4e25ec9..4e93d3e 100644 --- a/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.h +++ b/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.h @@ -14,6 +14,8 @@ #import "WEXRichPushLayout.h" #endif +#define CONTENT_PADDING 10 +#define TITLE_BODY_SPACE 5 @interface WEXRatingPushNotificationViewController #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.m b/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.m index 89b918f..0e315f8 100644 --- a/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.m +++ b/WebEngageAppEx/Classes/ContentExtension/WEXRatingPushNotificationViewController.m @@ -7,6 +7,7 @@ #import "WEXRatingPushNotificationViewController.h" +#import "UIColor+DarkMode.h" //#define NO_OF_STARS 5 #define STAR_BAR_HEIGHT 50 @@ -120,7 +121,7 @@ @implementation WEXRatingPushNotificationViewController - (void)initialiseViewHierarchy { - self.view.backgroundColor = [UIColor yellowColor]; + self.view.backgroundColor = [UIColor WEXWhiteColor]; UIView *superViewWrapper = [[UIView alloc] init]; @@ -179,13 +180,11 @@ - (void)initialiseViewHierarchy { textDisplayView.opaque = NO; textDisplayView.backgroundColor = [UIColor clearColor]; } else { - if (bckColor) { - textDisplayView.backgroundColor = [self colorWithHexString:bckColor]; + textDisplayView.backgroundColor = [UIColor colorFromHexString:bckColor defaultColor:UIColor.WEXLightTextColor]; } else { - textDisplayView.backgroundColor = [UIColor lightTextColor]; + textDisplayView.backgroundColor = UIColor.WEXLightTextColor; } - } UILabel *titleLabel; @@ -206,9 +205,9 @@ - (void)initialiseViewHierarchy { titleLabel.text = title; titleLabel.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; if (textColor) { - titleLabel.textColor = [self colorWithHexString:textColor]; + titleLabel.textColor = [UIColor colorFromHexString:textColor defaultColor:UIColor.WEXLabelColor]; } else { - titleLabel.textColor = [UIColor blackColor]; + titleLabel.textColor = UIColor.WEXLabelColor; } [textDisplayView addSubview:titleLabel]; @@ -220,9 +219,9 @@ - (void)initialiseViewHierarchy { messageLabel.text = message; if (textColor) { - messageLabel.textColor = [self colorWithHexString:textColor]; + messageLabel.textColor = [UIColor colorFromHexString:textColor defaultColor:UIColor.WEXLabelColor]; } else { - messageLabel.textColor = [UIColor blackColor]; + messageLabel.textColor = UIColor.WEXLabelColor; } messageLabel.numberOfLines = 3; @@ -232,14 +231,63 @@ - (void)initialiseViewHierarchy { [mainContentView addSubview:textDisplayView]; + //TODO: Add conditions for lack of rich texts + + UIView *contentSeparator = [[UIView alloc] init]; + contentSeparator.backgroundColor = UIColor.WEXGreyColor; + + [superViewWrapper addSubview:contentSeparator]; + + NSString *richTitle = self.notification.request.content.userInfo[@"expandableDetails"][@"rt"]; + NSString *richSub = self.notification.request.content.userInfo[@"expandableDetails"][@"rst"]; + NSString *richMessage = self.notification.request.content.userInfo[@"expandableDetails"][@"rm"]; + + BOOL isRichTitle = richTitle && ![richTitle isEqualToString:@""]; + BOOL isRichSubtitle = richSub && ![richSub isEqualToString:@""]; + BOOL isRichMessage = richMessage && ![richMessage isEqualToString:@""]; + + if (!isRichTitle) { + richTitle = self.notification.request.content.title; + } + if (!isRichSubtitle) { + richSub = self.notification.request.content.subtitle; + } + if (!isRichMessage) { + richMessage = self.notification.request.content.body; + } + + NSString *colorHex = self.notification.request.content.userInfo[@"expandableDetails"][@"bckColor"]; + + // Add a notification content view for displaying title and body. + UIView *richContentView = [[UIView alloc] init]; + richContentView.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; + + UILabel *richTitleLabel = [[UILabel alloc] init]; + richTitleLabel.attributedText = [self.viewController getHtmlParsedString:richTitle isTitle:YES bckColor:colorHex]; + richTitleLabel.textAlignment = [self.viewController naturalTextAligmentForText:richTitleLabel.text]; + + UILabel *richSubLabel = [[UILabel alloc] init]; + richSubLabel.attributedText = [self.viewController getHtmlParsedString:richSub isTitle:YES bckColor:colorHex]; + richSubLabel.textAlignment = [self.viewController naturalTextAligmentForText:richSubLabel.text]; + + UILabel *richBodyLabel = [[UILabel alloc] init]; + richBodyLabel.attributedText = [self.viewController getHtmlParsedString:richMessage isTitle:NO bckColor:colorHex]; + richBodyLabel.textAlignment = [self.viewController naturalTextAligmentForText:richBodyLabel.text]; + richBodyLabel.numberOfLines = 0; + + [richContentView addSubview:richTitleLabel]; + [richContentView addSubview:richSubLabel]; + [richContentView addSubview:richBodyLabel]; + + [superViewWrapper addSubview:richContentView]; + UIView *separator = [[UIView alloc] init]; - separator.backgroundColor = [UIColor lightGrayColor]; + separator.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXGreyColor]; [superViewWrapper addSubview:separator]; UIView *starRatingView = [[UIView alloc] init]; - starRatingView.backgroundColor = [UIColor whiteColor]; - + starRatingView.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; self.labelsWrapper = [[UIView alloc] init]; self.unselectedLabel = [[UILabel alloc] init]; @@ -305,7 +353,7 @@ - (void)renderStarControl { self.unselectedLabel.text = starStringUnselected; - self.unselectedLabel.textColor = [UIColor lightGrayColor]; + self.unselectedLabel.textColor = UIColor.WEXGreyColor; self.unselectedLabel.font = [self.unselectedLabel.font fontWithSize:STAR_FONT_SIZE]; } @@ -315,9 +363,11 @@ - (void)setUpConstraintsWithImageView:(BOOL)imageViewIncluded UIView *superViewWrapper = self.view.subviews[0]; UIView *mainContentView = superViewWrapper.subviews[0]; - UIView *separator = superViewWrapper.subviews[1]; - UIView *starRatingWrapper = superViewWrapper.subviews[2]; - + UIView *contentSeparator = superViewWrapper.subviews[1]; + UIView *richContentView = superViewWrapper.subviews[2]; + UIView *separator = superViewWrapper.subviews[3]; + UIView *starRatingWrapper = superViewWrapper.subviews[4]; + if (@available(iOS 10.0, *)) { superViewWrapper.translatesAutoresizingMaskIntoConstraints = NO; @@ -332,10 +382,21 @@ - (void)setUpConstraintsWithImageView:(BOOL)imageViewIncluded [mainContentView.trailingAnchor constraintEqualToAnchor:mainContentView.superview.trailingAnchor].active = YES; [mainContentView.topAnchor constraintEqualToAnchor:mainContentView.superview.topAnchor].active = YES; + contentSeparator.translatesAutoresizingMaskIntoConstraints = NO; + [contentSeparator.leadingAnchor constraintEqualToAnchor:contentSeparator.superview.leadingAnchor].active = YES; + [contentSeparator.trailingAnchor constraintEqualToAnchor:contentSeparator.superview.trailingAnchor].active = YES; + [contentSeparator.topAnchor constraintEqualToAnchor:mainContentView.bottomAnchor].active = YES; + [contentSeparator.heightAnchor constraintEqualToConstant:0.5].active = YES; + + richContentView.translatesAutoresizingMaskIntoConstraints = NO; + [richContentView.leadingAnchor constraintEqualToAnchor:richContentView.superview.leadingAnchor].active = YES; + [richContentView.trailingAnchor constraintEqualToAnchor:richContentView.superview.trailingAnchor].active = YES; + [richContentView.topAnchor constraintEqualToAnchor:contentSeparator.bottomAnchor].active = YES; + separator.translatesAutoresizingMaskIntoConstraints = NO; [separator.leadingAnchor constraintEqualToAnchor:separator.superview.leadingAnchor].active = YES; [separator.trailingAnchor constraintEqualToAnchor:separator.superview.trailingAnchor].active = YES; - [separator.topAnchor constraintEqualToAnchor:mainContentView.bottomAnchor].active = YES; + [separator.topAnchor constraintEqualToAnchor:richContentView.bottomAnchor].active = YES; [separator.heightAnchor constraintEqualToConstant:0.5].active = YES; starRatingWrapper.translatesAutoresizingMaskIntoConstraints = NO; @@ -405,6 +466,57 @@ - (void)setUpConstraintsWithImageView:(BOOL)imageViewIncluded [messageLabel.bottomAnchor constraintEqualToAnchor:textDisplayView.bottomAnchor constant:0-TEXT_PADDING].active = YES; } + // Rich View labels + + UIView *richTitleLabel = richContentView.subviews[0]; + UIView *richSubTitleLabel = richContentView.subviews[1]; + UIView *richBodyLabel = richContentView.subviews[2]; + + richTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richTitleLabel.topAnchor + constraintEqualToAnchor:richContentView.topAnchor + constant:CONTENT_PADDING] + .active = YES; + + richSubTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richSubTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.topAnchor + constraintEqualToAnchor:richTitleLabel.bottomAnchor + constant:0] + .active = YES; + + richBodyLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richBodyLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richBodyLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richBodyLabel.topAnchor constraintEqualToAnchor:richSubTitleLabel.bottomAnchor + constant:0] + .active = YES; + [richBodyLabel.bottomAnchor + constraintEqualToAnchor:richContentView.bottomAnchor + constant:-CONTENT_PADDING] + .active = YES; + //Star rating view internal constraints self.labelsWrapper.translatesAutoresizingMaskIntoConstraints = NO; [self.labelsWrapper.topAnchor constraintEqualToAnchor:self.labelsWrapper.superview.topAnchor].active = YES; @@ -438,7 +550,7 @@ - (UIView *)inputAccessoryView { CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, 50); UIView *inputAccessoryView = [[UIView alloc] initWithFrame:frame]; - inputAccessoryView.backgroundColor = [UIColor lightTextColor]; + inputAccessoryView.backgroundColor = UIColor.WEXLightTextColor; UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; NSAttributedString *attrTitle = [[NSAttributedString alloc] initWithString:@"Done" @@ -446,7 +558,7 @@ - (UIView *)inputAccessoryView { NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleNone], NSFontAttributeName: [UIFont boldSystemFontOfSize:20], - NSForegroundColorAttributeName: [self colorWithHexString:@"0077cc"] + NSForegroundColorAttributeName: [UIColor colorFromHexString:@"0077cc" defaultColor:UIColor.blueColor] }]; [doneButton setAttributedTitle:attrTitle forState:UIControlStateNormal]; @@ -563,19 +675,6 @@ - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response comple } } -- (UIColor *)colorWithHexString:(NSString *)hexString { - - unsigned rgbValue = 0; - NSScanner *scanner = [NSScanner scannerWithString:hexString]; - [scanner setScanLocation:1]; // bypass '#' character - [scanner scanHexInt:&rgbValue]; - - return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 - green:((rgbValue & 0xFF00) >> 8)/255.0 - blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; -} - - #endif @end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController+Private.h b/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController+Private.h index 8d2d3b4..655063d 100644 --- a/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController+Private.h +++ b/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController+Private.h @@ -26,6 +26,8 @@ - (NSTextAlignment)naturalTextAligmentForText:(NSString*) text; +- (NSAttributedString *)getHtmlParsedString:(NSString *)textString isTitle:(BOOL)isTitle bckColor:(NSString *)bckColor; + #endif @end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController.m b/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController.m index a0a963c..3712cad 100644 --- a/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController.m +++ b/WebEngageAppEx/Classes/ContentExtension/WEXRichPushNotificationViewController.m @@ -9,10 +9,12 @@ #import "WEXRichPushNotificationViewController+Private.h" #import "WEXCarouselPushNotificationViewController.h" #import "WEXRatingPushNotificationViewController.h" +#import "WEXBannerPushNotificationViewController.h" +#import "WEXTextPushNotificationViewController.h" #import "WEXRichPushLayout.h" #import #import - +#import "NSMutableAttributedString+Additions.h" API_AVAILABLE(ios(10.0)) @interface WEXRichPushNotificationViewController () @@ -25,6 +27,7 @@ @interface WEXRichPushNotificationViewController () @property (nonatomic) NSUserDefaults *richPushDefaults; @property (atomic) BOOL isRendering; +@property (atomic) BOOL isDarkMode; #endif @@ -85,6 +88,7 @@ - (void)didReceiveNotification:(UNNotification *)notification API_AVAILABLE(ios self.notification = notification; self.isRendering = YES; + [self updateDarkModeStatus]; NSString *appGroup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"WEX_APP_GROUP"]; @@ -126,8 +130,11 @@ - (WEXRichPushLayout *)layoutForStyle:(NSString *)style { return [[WEXCarouselPushNotificationViewController alloc] initWithNotificationViewController:self]; } else if (style && [style isEqualToString:@"RATING_V1"]) { return [[WEXRatingPushNotificationViewController alloc] initWithNotificationViewController:self]; + } else if (style && [style isEqualToString:@"BIG_PICTURE"]) { + return [[WEXBannerPushNotificationViewController alloc] initWithNotificationViewController:self]; + } else if (style && [style isEqualToString:@"BIG_TEXT"]) { + return [[WEXTextPushNotificationViewController alloc] initWithNotificationViewController:self]; } - return nil; } @@ -136,6 +143,10 @@ - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response comple [self.currentLayout didReceiveNotificationResponse:response completionHandler:completion]; } +- (void)traitCollectionDidChange: (UITraitCollection *) previousTraitCollection { + [super traitCollectionDidChange: previousTraitCollection]; + [self updateDarkModeStatus]; +} - (NSMutableDictionary *) getActivityDictionaryForCurrentNotification { @@ -215,9 +226,9 @@ - (void)addEventWithName:(NSString *)eventName if ([category isEqualToString:@"system"]) { [WEXAnalytics trackEventWithName:[@"we_" stringByAppendingString:eventName] andValue:@{ - @"system_data_overrides": systemData ? systemData : @{}, - @"event_data_overrides": customDataDictionary - }]; + @"system_data_overrides": systemData ? systemData : @{}, + @"event_data_overrides": customDataDictionary + }]; } else { [WEXAnalytics trackEventWithName:eventName andValue:customDataDictionary]; } @@ -230,7 +241,11 @@ - (void)setCTAWithId:(NSString *)ctaId andLink:(NSString *)actionLink { [self updateActivityWithObject:cta forKey:@"cta"]; } -- (NSTextAlignment)naturalTextAligmentForText:(NSString*) text{ +- (NSTextAlignment)naturalTextAligmentForText:(NSString*)text { + if (text == (id)[NSNull null] || text.length == 0 ) { + return NSTextAlignmentLeft; + } + NSArray *tagschemes = [NSArray arrayWithObjects:NSLinguisticTagSchemeLanguage, nil]; NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:tagschemes options:0]; [tagger setString:text]; @@ -242,6 +257,76 @@ - (NSTextAlignment)naturalTextAligmentForText:(NSString*) text{ } } +- (NSAttributedString *)getHtmlParsedString:(NSString *)textString isTitle:(BOOL)isTitle bckColor:(NSString *)bckColor { + BOOL containsHTML = [self containsHTML:textString]; + NSString *inputString = textString; + + // Updating font attributes overrides Italic characteristic + // Adding extra tags makes more sense + if (containsHTML && isTitle) { + inputString = [NSString stringWithFormat: @"%@", textString]; + } + + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] + initWithData: [inputString dataUsingEncoding:NSUnicodeStringEncoding] + options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } + documentAttributes: nil + error: nil + ]; + + BOOL hasBckColor = bckColor && ![bckColor isEqualToString:@""]; + if (!hasBckColor && _isDarkMode) { + [attributedString updateDefaultTextColor]; + } + + BOOL containsFontSize = [inputString rangeOfString:@"font-size"].location != NSNotFound; + + UIFont *defaultFont = [UIFont systemFontOfSize:[UIFont labelFontSize]]; + UIFont *boldFont = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]]; + + /* + If html string doesn't contain font-size, + then setting default based on title position + */ + + if (containsHTML && containsFontSize == NO) { + if (isTitle) { + [attributedString setFontFaceWithFont:boldFont]; + } else { + [attributedString setFontFaceWithFont:defaultFont]; + } + [attributedString trimWhiteSpace]; + + } else if (containsHTML == NO) { + if (isTitle) { + [attributedString addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(0, attributedString.length)]; + } else { + [attributedString addAttribute:NSFontAttributeName value:defaultFont range:NSMakeRange(0, attributedString.length)]; + } + + } else { + [attributedString trimWhiteSpace]; + } + + return attributedString; +} + +- (BOOL)containsHTML:(NSString *)value { + NSString *htmlRegex = @"<[a-z][\\s\\S]*>"; + NSPredicate *htmlText = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", htmlRegex]; + return [htmlText evaluateWithObject:value]; +} + +- (void)updateDarkModeStatus { + if (@available(iOS 12.0, *)) { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + self.isDarkMode = YES; + return; + } + } + self.isDarkMode = NO; +} + #endif @end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.h b/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.h new file mode 100644 index 0000000..00c2247 --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.h @@ -0,0 +1,24 @@ +// +// WEXTextPushNotificationViewController.h +// WebEngage +// +// Copyright (c) 2022 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 +#import +#import +#import "WEXRichPushNotificationViewController+Private.h" +#import "WEXRichPushLayout.h" +#endif + +@interface WEXTextPushNotificationViewController +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 +: WEXRichPushLayout +#else +: NSObject +#endif + +@end diff --git a/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.m b/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.m new file mode 100644 index 0000000..fd519f4 --- /dev/null +++ b/WebEngageAppEx/Classes/ContentExtension/WEXTextPushNotificationViewController.m @@ -0,0 +1,172 @@ +// +// WEXTextPushNotificationViewController.m +// WebEngage +// +// Copyright (c) 2022 Webklipper Technologies Pvt Ltd. All rights reserved. +// + +#import "WEXTextPushNotificationViewController.h" +#import "WEXRichPushNotificationViewController+Private.h" +#import "UIColor+DarkMode.h" + +#define CONTENT_PADDING 10 +#define TITLE_BODY_SPACE 5 +#define LANDSCAPE_ASPECT 0.5 + +API_AVAILABLE(ios(10.0)) +@interface WEXTextPushNotificationViewController () + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 + +@property (nonatomic) UNNotification *notification; + +#endif + +@end + +@implementation WEXTextPushNotificationViewController + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 + +- (void)didReceiveNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0)) { + self.notification = notification; + [self initialiseViewHierarchy]; +} + +- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion{ + completion(UNNotificationContentExtensionResponseOptionDismissAndForwardAction); +} + +- (void)initialiseViewHierarchy { + self.view.backgroundColor = [UIColor WEXWhiteColor]; + + UIView *superViewWrapper = [[UIView alloc] init]; + [self.view addSubview:superViewWrapper]; + + [self setupLabelsContainer]; +} + +- (void)setupLabelsContainer { + UIView *superViewWrapper = self.view.subviews[0]; + NSString *colorHex = self.notification.request.content.userInfo[@"expandableDetails"][@"bckColor"]; + + UIView *richContentView = [[UIView alloc] init]; + richContentView.backgroundColor = [UIColor colorFromHexString:colorHex defaultColor:UIColor.WEXWhiteColor]; + + NSDictionary *expandedDetails = self.notification.request.content.userInfo[@"expandableDetails"]; + NSString *title = expandedDetails[@"rt"]; + NSString *subtitle = expandedDetails[@"rst"]; + NSString *message = expandedDetails[@"rm"]; + + BOOL titlePresent = title && ![title isEqualToString:@""]; + BOOL subTitlePresent = subtitle && ![subtitle isEqualToString:@""]; + BOOL messagePresent = message && ![message isEqualToString:@""]; + + if (!titlePresent) { + title = self.notification.request.content.title; + } + if (!subTitlePresent) { + subtitle = self.notification.request.content.subtitle; + } + if (!messagePresent) { + message = self.notification.request.content.body; + } + + UILabel *richTitleLabel = [[UILabel alloc] init]; + richTitleLabel.attributedText = [self.viewController getHtmlParsedString:title isTitle:YES bckColor:colorHex]; + richTitleLabel.textAlignment = [self.viewController naturalTextAligmentForText:richTitleLabel.text]; + + UILabel *richSubLabel = [[UILabel alloc] init]; + richSubLabel.attributedText = [self.viewController getHtmlParsedString:subtitle isTitle:YES bckColor:colorHex]; + richSubLabel.textAlignment = [self.viewController naturalTextAligmentForText:richSubLabel.text]; + + UILabel *richBodyLabel = [[UILabel alloc] init]; + richBodyLabel.attributedText = [self.viewController getHtmlParsedString:message isTitle:NO bckColor:colorHex]; + richBodyLabel.textAlignment = [self.viewController naturalTextAligmentForText:richBodyLabel.text]; + richBodyLabel.numberOfLines = 0; + + [richContentView addSubview:richTitleLabel]; + [richContentView addSubview:richSubLabel]; + [richContentView addSubview:richBodyLabel]; + + [superViewWrapper addSubview:richContentView]; + + [self setupConstraints]; +} + +- (void)setupConstraints { + UIView *superViewWrapper = self.view.subviews[0]; + UIView *richContentView = superViewWrapper.subviews[0]; + + if (@available(iOS 10.0, *)) { + superViewWrapper.translatesAutoresizingMaskIntoConstraints = NO; + [superViewWrapper.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES; + [superViewWrapper.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES; + [superViewWrapper.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; + [superViewWrapper.bottomAnchor constraintEqualToAnchor:richContentView.bottomAnchor].active = YES; + + richContentView.translatesAutoresizingMaskIntoConstraints = NO; + [richContentView.leadingAnchor constraintEqualToAnchor:superViewWrapper.leadingAnchor].active = YES; + [richContentView.trailingAnchor constraintEqualToAnchor:superViewWrapper.trailingAnchor].active = YES; + [richContentView.topAnchor constraintEqualToAnchor:superViewWrapper.topAnchor].active = YES; + + [self.viewController.bottomLayoutGuide.topAnchor constraintEqualToAnchor:superViewWrapper.bottomAnchor].active = YES; + + //Rich View labels + UIView *richTitleLabel = richContentView.subviews[0]; + UIView *richSubTitleLabel = richContentView.subviews[1]; + UIView *richBodyLabel = richContentView.subviews[2]; + + richTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richTitleLabel.topAnchor + constraintEqualToAnchor:richContentView.topAnchor + constant:CONTENT_PADDING] + .active = YES; + + richSubTitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richSubTitleLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richSubTitleLabel.topAnchor + constraintEqualToAnchor:richTitleLabel.bottomAnchor + constant:0] + .active = YES; + + richBodyLabel.translatesAutoresizingMaskIntoConstraints = NO; + [richBodyLabel.leadingAnchor + constraintEqualToAnchor:richContentView.leadingAnchor + constant:CONTENT_PADDING] + .active = YES; + [richBodyLabel.trailingAnchor + constraintEqualToAnchor:richContentView.trailingAnchor + constant:0 - CONTENT_PADDING] + .active = YES; + [richBodyLabel.topAnchor constraintEqualToAnchor:richSubTitleLabel.bottomAnchor + constant:0] + .active = YES; + [richBodyLabel.bottomAnchor + constraintEqualToAnchor:richContentView.bottomAnchor + constant:-CONTENT_PADDING] + .active = YES; + } else { + NSLog(@"Expected to be running iOS version 10 or above"); + } +} + +#endif + +@end + diff --git a/WebEngageAppEx/Classes/NotificationService/WEXPushNotificationService.m b/WebEngageAppEx/Classes/NotificationService/WEXPushNotificationService.m index f9ef554..d908442 100644 --- a/WebEngageAppEx/Classes/NotificationService/WEXPushNotificationService.m +++ b/WebEngageAppEx/Classes/NotificationService/WEXPushNotificationService.m @@ -7,6 +7,7 @@ #import "WEXPushNotificationService.h" +#import @interface WEXPushNotificationService () @@ -16,6 +17,7 @@ @interface WEXPushNotificationService () @property (nonatomic) UNMutableNotificationContent *bestAttemptContent; @property (nonatomic) NSString *enviroment; @property NSDictionary *sharedUserDefaults; +@property NSArray *customCategories; #endif @end @@ -33,38 +35,89 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; - [self setExtensionDefaults]; NSLog(@"Push Notification content: %@", request.content.userInfo); NSDictionary *expandableDetails = request.content.userInfo[@"expandableDetails"]; - NSString *style = expandableDetails[@"style"]; if (expandableDetails && style && [style isEqualToString:@"CAROUSEL_V1"]) { - [self drawCarouselViewWith:expandableDetails[@"items"]]; - } - else if (expandableDetails && style && - ([style isEqualToString:@"RATING_V1"] || [style isEqualToString:@"BIG_PICTURE"])) { - [self drawBannerViewWith:expandableDetails[@"image"]]; - } - else { - [self trackEventWithCompletion:^{ - self.contentHandler(self.bestAttemptContent); + } else if (expandableDetails && style && [style isEqualToString:@"RATING_V1"]) { + [self handleContentFor:style image:expandableDetails[@"image"]]; + + } else if (expandableDetails && style && ([style isEqualToString:@"BIG_PICTURE"] || [style isEqualToString:@"BIG_TEXT"])) { + self.customCategories = @[@"WEG_RICH_V1", @"WEG_RICH_V2", @"WEG_RICH_V3", @"WEG_RICH_V4", @"WEG_RICH_V5", @"WEG_RICH_V6", @"WEG_RICH_V7", @"WEG_RICH_V8"]; + + NSString *customCategory = [self getCategoryFor:self.customCategories currentCategory:self.bestAttemptContent.categoryIdentifier]; + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + + [center getNotificationCategoriesWithCompletionHandler:^(NSSet *existingCategories) { + UNNotificationCategory *currentCategory; + BOOL isCategoryRegistered = NO; + BOOL isCurrentCatCustom = NO; + NSMutableSet *existingMutablecat = [[NSMutableSet alloc] init]; + + for(UNNotificationCategory *dict in existingCategories) { + if([dict.identifier isEqual: self.bestAttemptContent.categoryIdentifier]) { + currentCategory = dict; + isCategoryRegistered = [dict.identifier isEqualToString:customCategory]; + isCurrentCatCustom = [self.customCategories containsObject:dict.identifier]; + } else { + [existingMutablecat addObject:dict]; + } + } + + if (isCategoryRegistered) { + if (!isCurrentCatCustom) { + [self.bestAttemptContent setCategoryIdentifier:customCategory]; + } + [self handleContentFor:style image:expandableDetails[@"image"]]; + return; + } + + // Register banner layout here. + NSMutableArray *actions = [NSMutableArray arrayWithCapacity:currentCategory.actions.count]; + + for (UNNotificationAction *action in currentCategory.actions) { + UNNotificationAction *actionObject = [UNNotificationAction actionWithIdentifier:action.identifier + title:action.title + options:action.options]; + [actions addObject:actionObject]; + } + + UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:customCategory + actions:actions + intentIdentifiers:@[] + options:UNNotificationCategoryOptionCustomDismissAction]; + [existingMutablecat addObject:category]; + [center setNotificationCategories:existingMutablecat]; + [self.bestAttemptContent setCategoryIdentifier:customCategory]; + /* + Dispatching on Main thread after 2 sec delay to make sure our category is registered with NotificationCenter + Registering will make sure, contentHandler will invoke ContentExtension with this custom category + + NOTE: Use this workaround till we receive banner category in network response. + */ + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + [self handleContentFor:style image:expandableDetails[@"image"]]; + }); }]; + + } else { + [self handleContentFor:style image:@""]; } } -- (void)setExtensionDefaults { - NSUserDefaults *sharedDefaults = [self getSharedUserDefaults]; - // Write operation only if key is not present in the UserDefaults - - if ([sharedDefaults valueForKey:@"WEG_ServiceToApp"] == nil) { - [sharedDefaults setValue:@"WEG" forKey:@"WEG_ServiceToApp"]; - [sharedDefaults synchronize]; +- (void)handleContentFor:(NSString *)style image:(NSString *)image { + if ([style isEqualToString:@"BIG_PICTURE"] || [style isEqualToString:@"RATING_V1"]) { + [self drawBannerViewWith:image]; + } else { + [self trackEventWithCompletion:^{ + self.contentHandler(self.bestAttemptContent); + }]; } } @@ -73,6 +126,28 @@ - (void)serviceExtensionTimeWillExpire { self.contentHandler(self.bestAttemptContent); } +// NOTE: This mapping is a temporary workaround, Will be removed in future releases + +- (NSString *)getCategoryFor:(NSArray *)categories currentCategory:(NSString *)currentCategory { + NSDictionary *categoryMapping = @{ + @"default" : categories[0], // Default, No buttons + @"19db52de": categories[0], // Default, No buttons + @"18dfbbcc": categories[1], // Yes/No - Open App/Dismiss + @"16589g0g": categories[2], // Yes/No - Dismiss both + @"15ead296": categories[3], // Accept/Decline - Open/Dismiss + @"17543720": categories[4], // Accept/Decline - Dismiss both + @"16e66ba8": categories[5], // Shop Now + @"1c406g7a": categories[6], // Buy Now + @"1bd2a2g0": categories[7] // Download Now + }; + + NSString *category = categoryMapping[currentCategory]; + if (category) { + return category; + } else { + return currentCategory; + } +} #pragma mark - Rich Push View Helpers @@ -80,37 +155,37 @@ - (void)drawCarouselViewWith:(NSArray *)items { NSMutableArray *attachmentsArray = [[NSMutableArray alloc] initWithCapacity:items.count]; - if (items.count >= 3) { + if (items.count <= 0) { + return; + } + + NSUInteger itemCounter = 0; + NSUInteger __block imageDownloadAttemptCounter = 0; + + for (NSDictionary *carouselItem in items) { - NSUInteger itemCounter = 0; - NSUInteger __block imageDownloadAttemptCounter = 0; + NSString *imageURL = carouselItem[@"image"]; - for (NSDictionary *carouselItem in items) { + [self fetchAttachmentFor:imageURL + at:itemCounter + completionHandler:^(UNNotificationAttachment *attachment, NSUInteger index) { - NSString *imageURL = carouselItem[@"image"]; + imageDownloadAttemptCounter++; - [self fetchAttachmentFor:imageURL - at:itemCounter - completionHandler:^(UNNotificationAttachment *attachment, NSUInteger index) { - - imageDownloadAttemptCounter++; - - if (attachment) { - NSLog(@"Downloaded Attachment No. %ld", (unsigned long)index); - [attachmentsArray addObject:attachment]; - self.bestAttemptContent.attachments = attachmentsArray; - } - - if (imageDownloadAttemptCounter == items.count) { - - [self trackEventWithCompletion:^{ - NSLog(@"Ending WebEngage Rich Push Service"); - self.contentHandler(self.bestAttemptContent); - }]; - } - }]; - itemCounter++; - } + if (attachment) { + NSLog(@"Downloaded Attachment No. %ld", (unsigned long)index); + [attachmentsArray addObject:attachment]; + self.bestAttemptContent.attachments = attachmentsArray; + } + + if (imageDownloadAttemptCounter == items.count) { + [self trackEventWithCompletion:^{ + NSLog(@"Ending WebEngage Rich Push Service"); + self.contentHandler(self.bestAttemptContent); + }]; + } + }]; + itemCounter++; } } @@ -119,16 +194,16 @@ - (void)drawBannerViewWith:(NSString *)urlStr { [self fetchAttachmentFor:urlStr at:0 completionHandler:^(UNNotificationAttachment *attachment, NSUInteger index) { - - if (attachment) { - NSLog(@"WebEngage Downloaded Image for Rating Layout"); - self.bestAttemptContent.attachments = @[ attachment ]; - } - - [self trackEventWithCompletion:^{ - self.contentHandler(self.bestAttemptContent); - }]; - }]; + + if (attachment) { + NSLog(@"WebEngage Downloaded Image for Rating Layout"); + self.bestAttemptContent.attachments = @[ attachment ]; + } + + [self trackEventWithCompletion:^{ + self.contentHandler(self.bestAttemptContent); + }]; + }]; } - (void)fetchAttachmentFor:(NSString *)urlString @@ -200,19 +275,29 @@ - (void)trackEventWithCompletion:(void(^)(void))completion { else { NSLog(@"Push Tracker URLResponse: %@", response); } - + if (completion) { completion(); } }] resume]; } +- (void)setExtensionDefaults { + NSUserDefaults *sharedDefaults = [self getSharedUserDefaults]; + // Write operation only if key is not present in the UserDefaults + + if ([sharedDefaults valueForKey:@"WEG_ServiceToApp"] == nil) { + [sharedDefaults setValue:@"WEG" forKey:@"WEG_ServiceToApp"]; + [sharedDefaults synchronize]; + } +} + - (NSString *) getBaseURL{ NSString *baseURL = @"https://c.webengage.com/tracker"; [self setDataFromSharedUserDefaults]; - NSLog(@"Setting Enviroment to : %@",self.enviroment); + NSLog(@"Setting Enviroment to: %@",self.enviroment); if ([self.enviroment.uppercaseString isEqualToString:@"IN"]) { baseURL = @"https://c.in.webengage.com/tracker"; @@ -262,21 +347,20 @@ - (NSData *)getTrackerRequestBody { // Passing custom data into event tracking id customData = self.bestAttemptContent.userInfo[@"customData"]; - + if (customData && [customData isKindOfClass:[NSArray class]]) { - - NSArray *customDataArray = (NSArray *)customData; - - NSMutableDictionary *customDataDictionary = [[NSMutableDictionary alloc] - initWithCapacity:customDataArray.count]; - - for (NSDictionary *customDataItem in customDataArray) { - if (customDataItem[@"key"] && [customDataItem[@"key"] isKindOfClass:[NSString class]]) { - customDataDictionary[customDataItem[@"key"]] = - customDataItem[@"value"]; - } - } - + NSArray *customDataArray = (NSArray *)customData; + + NSMutableDictionary *customDataDictionary = [[NSMutableDictionary alloc] + initWithCapacity:customDataArray.count]; + + for (NSDictionary *customDataItem in customDataArray) { + if (customDataItem[@"key"] && [customDataItem[@"key"] isKindOfClass:[NSString class]]) { + customDataDictionary[customDataItem[@"key"]] = + customDataItem[@"value"]; + } + } + body[@"event_data"] = customDataDictionary; } else { body[@"event_data"] = @{}; diff --git a/WebEngageBannerPush.podspec b/WebEngageBannerPush.podspec index 812a8c5..d9d22c1 100644 --- a/WebEngageBannerPush.podspec +++ b/WebEngageBannerPush.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = 'WebEngageBannerPush' - spec.version = '0.12.0' + spec.version = '1.0.0' spec.summary = 'Extension Target SDK for adding WebEngage Rich Push Notifications support' spec.description = <<-DESC @@ -9,7 +9,7 @@ Pod::Spec.new do |spec| DESC spec.license = 'MIT' - spec.author = 'Saumitra Bhave', 'Uzma Sayyed', 'Unmesh Rathod' + spec.author = 'Saumitra Bhave', 'Uzma Sayyed', 'Unmesh Rathod', 'Bhavesh Sarwar' spec.homepage = 'https://webengage.com' spec.social_media_url = 'http://twitter.com/webengage' spec.documentation_url = 'https://docs.webengage.com/docs/ios-getting-started'