diff --git a/.clang_format b/.clang_format index 92959d68..46a57a6e 100644 --- a/.clang_format +++ b/.clang_format @@ -2,11 +2,11 @@ BasedOnStyle: Google AlignTrailingComments: true BreakBeforeBraces: Attach ColumnLimit: 80 +PointerBindsToType: false IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true -PointerBindsToType: false SpacesBeforeTrailingComments: 1 TabWidth: 8 UseTab: Never diff --git a/.format.sh b/.format.sh new file mode 100755 index 00000000..d0b55bd6 --- /dev/null +++ b/.format.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +for DIRECTORY in Pod Examples Tests/Tests +do + echo "Formatting code under $DIRECTORY/" + find "$DIRECTORY" \( -name '*.h' -or -name '*.m' -or -name '*.mm' \) -print0 | xargs -0 "clang-format" -i +done diff --git a/BVSDK.xcodeproj/project.pbxproj b/BVSDK.xcodeproj/project.pbxproj index cbe7a1dd..28f0157d 100644 --- a/BVSDK.xcodeproj/project.pbxproj +++ b/BVSDK.xcodeproj/project.pbxproj @@ -458,7 +458,6 @@ 87F2DD481DAD585E00FB43F3 /* BVShopperProfileRequestCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 87F2DC161DAD585E00FB43F3 /* BVShopperProfileRequestCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 87F2DD4B1DAD589800FB43F3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87F2DD4A1DAD589800FB43F3 /* UIKit.framework */; }; 87F2DD4D1DAD589E00FB43F3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87F2DD4C1DAD589E00FB43F3 /* Foundation.framework */; }; - 87F2DD501DAD592A00FB43F3 /* Gimbal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87F2DD4F1DAD592A00FB43F3 /* Gimbal.framework */; }; 87F2DD521DAD597C00FB43F3 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 87F2DD511DAD597C00FB43F3 /* libz.tbd */; }; 87F2DD5E1DAD5E9400FB43F3 /* BVBaseStubTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 87F2DD551DAD5E9400FB43F3 /* BVBaseStubTestCase.m */; }; 87F2DD601DAD5E9400FB43F3 /* BVCurationsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 87F2DD571DAD5E9400FB43F3 /* BVCurationsTests.m */; }; @@ -539,6 +538,7 @@ B575C38F1FBE1A4F000F890B /* BVUASSubmissionResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B575C38D1FBE1A4F000F890B /* BVUASSubmissionResponse.m */; }; B575C3921FBE2479000F890B /* BVSubmittedUAS.h in Headers */ = {isa = PBXBuildFile; fileRef = B575C3901FBE2479000F890B /* BVSubmittedUAS.h */; settings = {ATTRIBUTES = (Public, ); }; }; B575C3931FBE2479000F890B /* BVSubmittedUAS.m in Sources */ = {isa = PBXBuildFile; fileRef = B575C3911FBE2479000F890B /* BVSubmittedUAS.m */; }; + B5E1F5331FD5F2F0000F5DB5 /* Gimbal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5E1F5321FD5F2F0000F5DB5 /* Gimbal.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -996,7 +996,6 @@ 87F2DC161DAD585E00FB43F3 /* BVShopperProfileRequestCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BVShopperProfileRequestCache.h; sourceTree = ""; }; 87F2DD4A1DAD589800FB43F3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 87F2DD4C1DAD589E00FB43F3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 87F2DD4F1DAD592A00FB43F3 /* Gimbal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Gimbal.framework; sourceTree = ""; }; 87F2DD511DAD597C00FB43F3 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 87F2DD541DAD5E9400FB43F3 /* BVBaseStubTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BVBaseStubTestCase.h; path = Tests/Tests/BVBaseStubTestCase.h; sourceTree = SOURCE_ROOT; }; 87F2DD551DAD5E9400FB43F3 /* BVBaseStubTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BVBaseStubTestCase.m; path = Tests/Tests/BVBaseStubTestCase.m; sourceTree = SOURCE_ROOT; }; @@ -1079,6 +1078,7 @@ B575C38D1FBE1A4F000F890B /* BVUASSubmissionResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BVUASSubmissionResponse.m; path = ../Auth/BVUASSubmissionResponse.m; sourceTree = ""; }; B575C3901FBE2479000F890B /* BVSubmittedUAS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BVSubmittedUAS.h; path = ../Auth/BVSubmittedUAS.h; sourceTree = ""; }; B575C3911FBE2479000F890B /* BVSubmittedUAS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BVSubmittedUAS.m; path = ../Auth/BVSubmittedUAS.m; sourceTree = ""; }; + B5E1F5321FD5F2F0000F5DB5 /* Gimbal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Gimbal.framework; path = Frameworks/Gimbal.framework; sourceTree = SOURCE_ROOT; }; EC098EFAFB725736F0751BA0 /* libPods-BVSDK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BVSDK.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -1091,7 +1091,7 @@ 87F2DD521DAD597C00FB43F3 /* libz.tbd in Frameworks */, 87F2DD4D1DAD589E00FB43F3 /* Foundation.framework in Frameworks */, 87F2DD4B1DAD589800FB43F3 /* UIKit.framework in Frameworks */, - 87F2DD501DAD592A00FB43F3 /* Gimbal.framework in Frameworks */, + B5E1F5331FD5F2F0000F5DB5 /* Gimbal.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1919,10 +1919,10 @@ isa = PBXGroup; children = ( 87F2DD511DAD597C00FB43F3 /* libz.tbd */, - 87F2DD4F1DAD592A00FB43F3 /* Gimbal.framework */, + B5E1F5321FD5F2F0000F5DB5 /* Gimbal.framework */, ); name = "3rd Party Frameworks"; - path = Pod/Frameworks; + path = Frameworks; sourceTree = ""; }; 87F2DD651DAD698400FB43F3 /* conversations */ = { @@ -2999,7 +2999,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Pod/Frameworks", + "$(PROJECT_DIR)/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; @@ -3026,7 +3026,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Pod/Frameworks", + "$(PROJECT_DIR)/Frameworks", ); GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; diff --git a/BVSDK/BVSDK.h b/BVSDK/BVSDK.h index 14f9acaa..7c475a49 100644 --- a/BVSDK/BVSDK.h +++ b/BVSDK/BVSDK.h @@ -13,70 +13,69 @@ FOUNDATION_EXPORT double BVSDKVersionNumber; //! Project version string for BVSDK. FOUNDATION_EXPORT const unsigned char BVSDKVersionString[]; -// In this header, you should import all the public headers of your framework using statements like #import +// In this header, you should import all the public headers of your framework +// using statements like #import // Core #import -#import #import +#import // Notifications -#import -#import #import #import #import -#import -#import -#import +#import +#import +#import #import #import #import -#import -#import -#import #import -#import -#import #import #import +#import +#import +#import +#import +#import // Analytics -#import #import +#import // Conversations -#import +#import #import #import #import #import #import #import -#import +#import +#import #import #import +#import #import #import #import #import -#import -#import #import #import -#import #import #import -#import -#import -#import -#import -#import #import +#import #import +#import +#import +#import +#import #import +#import #import #import @@ -84,31 +83,29 @@ FOUNDATION_EXPORT const unsigned char BVSDKVersionString[]; #import #import #import -#import -#import -#import -#import -#import -#import +#import #import +#import #import +#import #import -#import +#import +#import #import -#import +#import -#import #import -#import +#import #import -#import +#import #import -#import +#import +#import #import #import #import +#import #import -#import #import #import @@ -116,52 +113,52 @@ FOUNDATION_EXPORT const unsigned char BVSDKVersionString[]; #import #import -#import -#import #import +#import #import +#import +#import #import -#import // Conversations Auth +#import #import #import #import -#import // Curations #import -#import +#import #import #import -#import - #import -#import + #import +#import +#import #import #import // CurationsUI -#import #import +#import #import // Location -#import #import #import #import #import +#import // Recommendations -#import -#import #import #import -#import #import +#import +#import +#import // Post Interaction Notifications #import diff --git a/BVSDKTests/UIImage+BundleLocator.h b/BVSDKTests/UIImage+BundleLocator.h index 7939b343..563431f1 100644 --- a/BVSDKTests/UIImage+BundleLocator.h +++ b/BVSDKTests/UIImage+BundleLocator.h @@ -9,7 +9,7 @@ #import @interface UIImage (BundleLocator) -+(UIImage*)bundledImageNamed:(NSString*) imageName; ++ (UIImage *)bundledImageNamed:(NSString *)imageName; @end @interface BundleLocator : NSObject diff --git a/BVSDKTests/UIImage+BundleLocator.m b/BVSDKTests/UIImage+BundleLocator.m index a7bae966..17420e21 100644 --- a/BVSDKTests/UIImage+BundleLocator.m +++ b/BVSDKTests/UIImage+BundleLocator.m @@ -8,12 +8,12 @@ #import "UIImage+BundleLocator.h" @implementation UIImage (BundleLocator) -+(UIImage*)bundledImageNamed:(NSString*) imageName -{ - - return [UIImage imageNamed:imageName - inBundle:[NSBundle bundleForClass:[BundleLocator class]] - compatibleWithTraitCollection:nil]; ++ (UIImage *)bundledImageNamed:(NSString *)imageName { + + return [UIImage imageNamed:imageName + inBundle:[NSBundle + bundleForClass:[BundleLocator class]] + compatibleWithTraitCollection:nil]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.h index f6600d36..53fda56b 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.h @@ -9,7 +9,6 @@ @interface AppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; +@property(strong, nonatomic) UIWindow *window; @end - diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.m index da24346a..c0ff245e 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/AppDelegate.m @@ -9,74 +9,92 @@ #import "RootViewController.h" @import BVSDK; - @implementation AppDelegate - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - - [self setupWindow]; - - + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + [self setupWindow]; + #warning Add your own bvsdk_config files to project - - // Configure BVSDKManager - [BVSDKManager configure:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - - // Next, we have to tell BVSDK about the user. - // See the BVSDK wiki for more discussion on how to create this user auth string: https://github.com/bazaarvoice/bv-ios-sdk/wiki/BVSDK-UserAuthentication - // A user auth string would contain data in a query string format. For example: - // userid=Example&email=user@example.com&age=28&gender=female&facebookId=123abc - - - // Example auth string used, pre-populated with a small profile interested in "pets", - // "powersports", "gamefish", and others -- for testing purposes. - [[BVSDKManager sharedManager] setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d31323334267573657269643d5265636f6d6d656e646174696f6e7353646b54657374"]; - - NSString *myClientId = [[[BVSDKManager sharedManager]configuration]clientId]; - NSString *myShopperAdvertisingKey = [[[BVSDKManager sharedManager]configuration]apiKeyShopperAdvertising]; - if ([myClientId isEqualToString:@"REPLACE_ME"] || [myShopperAdvertisingKey isEqualToString:@"REPLACE_ME"]){ - [self showNotConfiguredError]; - } - - return YES; + + // Configure BVSDKManager + [BVSDKManager configure:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; + + // Next, we have to tell BVSDK about the user. + // See the BVSDK wiki for more discussion on how to create this user auth + // string: + // https://github.com/bazaarvoice/bv-ios-sdk/wiki/BVSDK-UserAuthentication A + // user auth string would contain data in a query string format. For example: + // userid=Example&email=user@example.com&age=28&gender=female&facebookId=123abc + + // Example auth string used, pre-populated with a small profile interested in + // "pets", + // "powersports", "gamefish", and others -- for testing purposes. + [[BVSDKManager sharedManager] + setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d3132333" + @"4267573657269643d5265636f6d6d656e646174696f6e7353" + @"646b54657374"]; + + NSString *myClientId = + [[[BVSDKManager sharedManager] configuration] clientId]; + NSString *myShopperAdvertisingKey = + [[[BVSDKManager sharedManager] configuration] apiKeyShopperAdvertising]; + if ([myClientId isEqualToString:@"REPLACE_ME"] || + [myShopperAdvertisingKey isEqualToString:@"REPLACE_ME"]) { + [self showNotConfiguredError]; + } + + return YES; } - --(void)setupWindow { - - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:NO]; // set status bar color - - UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:[[RootViewController alloc] init]]; - [navigationController.navigationBar setBarTintColor:[UIColor whiteColor]]; - [navigationController.navigationBar setTintColor:[UIColor whiteColor]]; - [navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}]; - [navigationController.navigationBar setTranslucent:NO]; - [navigationController.navigationBar setBarStyle:UIBarStyleBlack]; - - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - [self.window setRootViewController:navigationController]; - [self.window makeKeyAndVisible]; - + +- (void)setupWindow { + + [[UIApplication sharedApplication] + setStatusBarStyle:UIStatusBarStyleLightContent + animated:NO]; // set status bar color + + UINavigationController *navigationController = [[UINavigationController alloc] + initWithRootViewController:[[RootViewController alloc] init]]; + [navigationController.navigationBar setBarTintColor:[UIColor whiteColor]]; + [navigationController.navigationBar setTintColor:[UIColor whiteColor]]; + [navigationController.navigationBar setTitleTextAttributes:@{ + NSForegroundColorAttributeName : [UIColor whiteColor] + }]; + [navigationController.navigationBar setTranslucent:NO]; + [navigationController.navigationBar setBarStyle:UIBarStyleBlack]; + + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [self.window setRootViewController:navigationController]; + [self.window makeKeyAndVisible]; } - --(void)showNotConfiguredError { - - NSString* alertTitle = @"BVSDK Not Configured"; - NSString* alertMessage = @"Make sure you have set your API Key and Client ID in project files bvsdk_config_prod.json and bvsdk_config_staging.json . Please contact Bazaarvoice if you do not have an API key."; - - UIAlertController *alert = [UIAlertController alertControllerWithTitle:alertTitle - message:alertMessage - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * _Nonnull action) { exit(1); }]; - - [alert addAction:okAction]; - - [self.window.rootViewController presentViewController:alert animated:YES completion:nil]; - + +- (void)showNotConfiguredError { + + NSString *alertTitle = @"BVSDK Not Configured"; + NSString *alertMessage = @"Make sure you have set your API Key and Client ID " + @"in project files bvsdk_config_prod.json and " + @"bvsdk_config_staging.json . Please contact " + @"Bazaarvoice if you do not have an API key."; + + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:alertTitle + message:alertMessage + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *okAction = + [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + exit(1); + }]; + + [alert addAction:okAction]; + + [self.window.rootViewController presentViewController:alert + animated:YES + completion:nil]; } - - @end + +@end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/BVAdTypesCell.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/BVAdTypesCell.h index 159fefe7..c2b240c1 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/BVAdTypesCell.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/BVAdTypesCell.h @@ -9,8 +9,8 @@ @interface BVAdTypesCell : UICollectionViewCell -@property (weak, nonatomic) IBOutlet UIImageView* backgroundImage; -@property (weak, nonatomic) IBOutlet UILabel* cellTitleLabel; -@property (weak, nonatomic) IBOutlet UILabel* cellDescriptionLabel; +@property(weak, nonatomic) IBOutlet UIImageView *backgroundImage; +@property(weak, nonatomic) IBOutlet UILabel *cellTitleLabel; +@property(weak, nonatomic) IBOutlet UILabel *cellDescriptionLabel; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.h index 1bb048dc..06aad239 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.h @@ -10,7 +10,7 @@ @interface BannerDemoViewController : UIViewController -@property (weak, nonatomic) IBOutlet UIButton* closeButton; -@property (weak, nonatomic) IBOutlet DFPBannerView* bannerView; +@property(weak, nonatomic) IBOutlet UIButton *closeButton; +@property(weak, nonatomic) IBOutlet DFPBannerView *bannerView; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.m index 69b29027..dac86a26 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/BannerDemoViewController.m @@ -11,22 +11,24 @@ @implementation BannerDemoViewController - (void)viewDidLoad { - [super viewDidLoad]; - - - self.bannerView.adUnitID = @"/6499/example/banner"; //Test adUnitId. Replace with your targeted adUnitId. - self.bannerView.rootViewController = self; - - DFPRequest* request = [DFPRequest request]; - request.testDevices = @[ kGADSimulatorID ]; - request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; - [self.bannerView loadRequest:request]; - - [self.view addSubview:self.bannerView]; + [super viewDidLoad]; + + self.bannerView.adUnitID = @"/6499/example/banner"; // Test adUnitId. Replace + // with your targeted + // adUnitId. + self.bannerView.rootViewController = self; + + DFPRequest *request = [DFPRequest request]; + request.testDevices = @[ kGADSimulatorID ]; + request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; + [self.bannerView loadRequest:request]; + + [self.view addSubview:self.bannerView]; } - (IBAction)closeButtonPressed { - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; + [self.presentingViewController dismissViewControllerAnimated:YES + completion:nil]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/GeneralizedDemoViewController.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/GeneralizedDemoViewController.m index a13918a6..4787e86d 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/GeneralizedDemoViewController.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/GeneralizedDemoViewController.m @@ -9,574 +9,614 @@ @import GoogleMobileAds; - -@interface GeneralizedDemoViewController () -{ - CGRect sharedControlsOriginalFrame; +@interface GeneralizedDemoViewController () < + GADInterstitialDelegate, GADNativeContentAdLoaderDelegate, + GADNativeCustomTemplateAdLoaderDelegate, UITextFieldDelegate> { + CGRect sharedControlsOriginalFrame; } // type of ad buttons -@property UISegmentedControl* adTypeControl; +@property UISegmentedControl *adTypeControl; // ad unit id input -@property UITextField* adUnitIdField; +@property UITextField *adUnitIdField; // shared container with key/value pairs and the 'go' button -@property UIView* sharedControlsContainerView; +@property UIView *sharedControlsContainerView; // pick between 'content' and 'custom' native ads -@property UISegmentedControl* nativeAdTypeControl; +@property UISegmentedControl *nativeAdTypeControl; // native custom ad template id -@property UITextField* nativeAdTypeCustomId; +@property UITextField *nativeAdTypeCustomId; // choose the banner size width and height -@property UITextField* bannerSizeWidth; -@property UITextField* bannerSizeHeight; +@property UITextField *bannerSizeWidth; +@property UITextField *bannerSizeHeight; // custom key targeting input -@property UITextField* key1Field; -@property UITextField* key1Value; -@property UITextField* key2Field; -@property UITextField* key2Value; +@property UITextField *key1Field; +@property UITextField *key1Value; +@property UITextField *key2Field; +@property UITextField *key2Value; // fetch ad button -@property UIButton* goButton; +@property UIButton *goButton; // ads -@property DFPInterstitial* interstitial; -@property DFPBannerView* bannerView; -@property GADAdLoader* adLoader; +@property DFPInterstitial *interstitial; +@property DFPBannerView *bannerView; +@property GADAdLoader *adLoader; // special result container for native ads -@property UIViewController* nativeResult; +@property UIViewController *nativeResult; @end @implementation GeneralizedDemoViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.view.backgroundColor = [UIColor whiteColor]; - - self.adTypeControl = [[UISegmentedControl alloc] initWithItems:@[@"Interstitial", @"Banner", @"Native" ]]; - self.adTypeControl.frame = CGRectMake(20, 20, self.view.bounds.size.width - 40, 30); - [self.adTypeControl addTarget:self action:@selector(adTypeSwitched:) forControlEvents:UIControlEventValueChanged]; - [self.view addSubview:self.adTypeControl]; - - self.adTypeControl.selectedSegmentIndex = 0; - - - sharedControlsOriginalFrame = CGRectMake(0, CGRectGetMaxY(self.adTypeControl.frame), self.view.bounds.size.width, 200); - self.sharedControlsContainerView = [[UIView alloc] initWithFrame:sharedControlsOriginalFrame]; - - self.bannerSizeWidth = [[UITextField alloc] initWithFrame:CGRectMake(20, - CGRectGetMaxY(self.adTypeControl.frame) + 20, - self.view.bounds.size.width/2 - 40, - 30)]; - [self.bannerSizeWidth setBorderStyle:UITextBorderStyleRoundedRect]; - [self.bannerSizeWidth setFont:[UIFont systemFontOfSize:14]]; - [self.bannerSizeWidth setPlaceholder:@"Banner width"]; - [self.bannerSizeWidth setDelegate:self]; - [self.bannerSizeWidth setReturnKeyType:UIReturnKeyDone]; - - CGRect bannerHeight = self.bannerSizeWidth.frame; - bannerHeight.origin.x += self.view.bounds.size.width/2; - self.bannerSizeHeight = [[UITextField alloc] initWithFrame:bannerHeight]; - [self.bannerSizeHeight setBorderStyle:UITextBorderStyleRoundedRect]; - [self.bannerSizeHeight setFont:[UIFont systemFontOfSize:14]]; - [self.bannerSizeHeight setPlaceholder:@"Banner height"]; - [self.bannerSizeHeight setDelegate:self]; - [self.bannerSizeHeight setReturnKeyType:UIReturnKeyDone]; - - CGRect nativeAdTypeControlFrame = self.bannerSizeWidth.frame; - nativeAdTypeControlFrame.size.width = self.view.bounds.size.width - 40; - self.nativeAdTypeControl = [[UISegmentedControl alloc] initWithItems:@[@"Content", @"Custom"]]; - self.nativeAdTypeControl.frame = nativeAdTypeControlFrame; - self.nativeAdTypeControl.selectedSegmentIndex = 0; - [self.nativeAdTypeControl addTarget:self action:@selector(nativeAdTypeSwitch:) forControlEvents:UIControlEventValueChanged]; - self.nativeAdTypeCustomId = [[UITextField alloc] initWithFrame:CGRectMake(20, - CGRectGetMaxY(nativeAdTypeControlFrame) + 10, - self.view.bounds.size.width-40, - 30)]; - [self.nativeAdTypeCustomId setPlaceholder:@"Custom ad template id"]; - [self.nativeAdTypeCustomId setDelegate:self]; - [self.nativeAdTypeCustomId setReturnKeyType:UIReturnKeyDone]; - [self.nativeAdTypeCustomId setBorderStyle:UITextBorderStyleRoundedRect]; - - CGRect adUnitIdFrame = CGRectMake(20, - 20, - self.view.bounds.size.width - 40, - 40); - - - self.adUnitIdField = [[UITextField alloc] initWithFrame:adUnitIdFrame]; - [self.adUnitIdField setPlaceholder:@"Ad Unit Id"]; - [self.adUnitIdField setBorderStyle:UITextBorderStyleRoundedRect]; - [self.adUnitIdField setAdjustsFontSizeToFitWidth:true]; - [self.adUnitIdField setMinimumFontSize:4]; - [self.adUnitIdField setDelegate:self]; - [self.adUnitIdField setReturnKeyType:UIReturnKeyDone]; - [self.sharedControlsContainerView addSubview:self.adUnitIdField]; - - CGRect keyValFrame = CGRectMake(20, - CGRectGetMaxY(self.adUnitIdField.frame) + 10, - self.view.bounds.size.width/2 - 40, - 40); - - self.key1Field = [[UITextField alloc] initWithFrame:keyValFrame]; - [self.key1Field setPlaceholder:@"key"]; - [self.key1Field setDelegate:self]; - [self.key1Field setReturnKeyType:UIReturnKeyDone]; - [self.key1Field setBorderStyle:UITextBorderStyleRoundedRect]; - - CGRect key1ValFrame = keyValFrame; - key1ValFrame.origin.x += self.view.bounds.size.width/2; - self.key1Value = [[UITextField alloc] initWithFrame:key1ValFrame]; - [self.key1Value setPlaceholder:@"value"]; - [self.key1Value setDelegate:self]; - [self.key1Value setReturnKeyType:UIReturnKeyDone]; - [self.key1Value setBorderStyle:UITextBorderStyleRoundedRect]; - - CGRect key2Frame = keyValFrame; - key2Frame.origin.y += key2Frame.size.height + 10; - self.key2Field = [[UITextField alloc] initWithFrame:key2Frame]; - [self.key2Field setPlaceholder:@"key2"]; - [self.key2Field setDelegate:self]; - [self.key2Field setReturnKeyType:UIReturnKeyDone]; - [self.key2Field setBorderStyle:UITextBorderStyleRoundedRect]; - - CGRect val2Frame = key2Frame; - val2Frame.origin.x += self.view.bounds.size.width/2; - self.key2Value = [[UITextField alloc] initWithFrame:val2Frame]; - [self.key2Value setPlaceholder:@"value2"]; - [self.key2Value setDelegate:self]; - [self.key2Value setReturnKeyType:UIReturnKeyDone]; - [self.key2Value setBorderStyle:UITextBorderStyleRoundedRect]; - - [self.sharedControlsContainerView addSubview:self.key1Field]; - [self.sharedControlsContainerView addSubview:self.key1Value]; - [self.sharedControlsContainerView addSubview:self.key2Field]; - [self.sharedControlsContainerView addSubview:self.key2Value]; - - - - self.goButton = [UIButton buttonWithType:UIButtonTypeSystem]; - self.goButton.frame = CGRectMake(20, - CGRectGetMaxY(self.key2Field.frame) + 20, - self.view.bounds.size.width - 40, - 40); - [self.goButton setTitle:@"Request Ad" forState:UIControlStateNormal]; - [self.goButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - [self.goButton setTitleColor:[UIColor grayColor] forState:UIControlStateSelected]; - - self.goButton.layer.borderColor = [[UIColor lightGrayColor] CGColor]; - self.goButton.layer.borderWidth = 0.5; - self.goButton.layer.cornerRadius = 4; - self.goButton.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.99 alpha:1.0]; - [self.goButton addTarget:self action:@selector(goPressed) forControlEvents:UIControlEventTouchUpInside]; - [self.sharedControlsContainerView addSubview:self.goButton]; - - [self.view addSubview:self.sharedControlsContainerView]; + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + + self.adTypeControl = [[UISegmentedControl alloc] + initWithItems:@[ @"Interstitial", @"Banner", @"Native" ]]; + self.adTypeControl.frame = + CGRectMake(20, 20, self.view.bounds.size.width - 40, 30); + [self.adTypeControl addTarget:self + action:@selector(adTypeSwitched:) + forControlEvents:UIControlEventValueChanged]; + [self.view addSubview:self.adTypeControl]; + + self.adTypeControl.selectedSegmentIndex = 0; + + sharedControlsOriginalFrame = + CGRectMake(0, CGRectGetMaxY(self.adTypeControl.frame), + self.view.bounds.size.width, 200); + self.sharedControlsContainerView = + [[UIView alloc] initWithFrame:sharedControlsOriginalFrame]; + + self.bannerSizeWidth = [[UITextField alloc] + initWithFrame:CGRectMake(20, CGRectGetMaxY(self.adTypeControl.frame) + 20, + self.view.bounds.size.width / 2 - 40, 30)]; + [self.bannerSizeWidth setBorderStyle:UITextBorderStyleRoundedRect]; + [self.bannerSizeWidth setFont:[UIFont systemFontOfSize:14]]; + [self.bannerSizeWidth setPlaceholder:@"Banner width"]; + [self.bannerSizeWidth setDelegate:self]; + [self.bannerSizeWidth setReturnKeyType:UIReturnKeyDone]; + + CGRect bannerHeight = self.bannerSizeWidth.frame; + bannerHeight.origin.x += self.view.bounds.size.width / 2; + self.bannerSizeHeight = [[UITextField alloc] initWithFrame:bannerHeight]; + [self.bannerSizeHeight setBorderStyle:UITextBorderStyleRoundedRect]; + [self.bannerSizeHeight setFont:[UIFont systemFontOfSize:14]]; + [self.bannerSizeHeight setPlaceholder:@"Banner height"]; + [self.bannerSizeHeight setDelegate:self]; + [self.bannerSizeHeight setReturnKeyType:UIReturnKeyDone]; + + CGRect nativeAdTypeControlFrame = self.bannerSizeWidth.frame; + nativeAdTypeControlFrame.size.width = self.view.bounds.size.width - 40; + self.nativeAdTypeControl = + [[UISegmentedControl alloc] initWithItems:@[ @"Content", @"Custom" ]]; + self.nativeAdTypeControl.frame = nativeAdTypeControlFrame; + self.nativeAdTypeControl.selectedSegmentIndex = 0; + [self.nativeAdTypeControl addTarget:self + action:@selector(nativeAdTypeSwitch:) + forControlEvents:UIControlEventValueChanged]; + self.nativeAdTypeCustomId = [[UITextField alloc] + initWithFrame:CGRectMake(20, CGRectGetMaxY(nativeAdTypeControlFrame) + 10, + self.view.bounds.size.width - 40, 30)]; + [self.nativeAdTypeCustomId setPlaceholder:@"Custom ad template id"]; + [self.nativeAdTypeCustomId setDelegate:self]; + [self.nativeAdTypeCustomId setReturnKeyType:UIReturnKeyDone]; + [self.nativeAdTypeCustomId setBorderStyle:UITextBorderStyleRoundedRect]; + + CGRect adUnitIdFrame = + CGRectMake(20, 20, self.view.bounds.size.width - 40, 40); + + self.adUnitIdField = [[UITextField alloc] initWithFrame:adUnitIdFrame]; + [self.adUnitIdField setPlaceholder:@"Ad Unit Id"]; + [self.adUnitIdField setBorderStyle:UITextBorderStyleRoundedRect]; + [self.adUnitIdField setAdjustsFontSizeToFitWidth:true]; + [self.adUnitIdField setMinimumFontSize:4]; + [self.adUnitIdField setDelegate:self]; + [self.adUnitIdField setReturnKeyType:UIReturnKeyDone]; + [self.sharedControlsContainerView addSubview:self.adUnitIdField]; + + CGRect keyValFrame = + CGRectMake(20, CGRectGetMaxY(self.adUnitIdField.frame) + 10, + self.view.bounds.size.width / 2 - 40, 40); + + self.key1Field = [[UITextField alloc] initWithFrame:keyValFrame]; + [self.key1Field setPlaceholder:@"key"]; + [self.key1Field setDelegate:self]; + [self.key1Field setReturnKeyType:UIReturnKeyDone]; + [self.key1Field setBorderStyle:UITextBorderStyleRoundedRect]; + + CGRect key1ValFrame = keyValFrame; + key1ValFrame.origin.x += self.view.bounds.size.width / 2; + self.key1Value = [[UITextField alloc] initWithFrame:key1ValFrame]; + [self.key1Value setPlaceholder:@"value"]; + [self.key1Value setDelegate:self]; + [self.key1Value setReturnKeyType:UIReturnKeyDone]; + [self.key1Value setBorderStyle:UITextBorderStyleRoundedRect]; + + CGRect key2Frame = keyValFrame; + key2Frame.origin.y += key2Frame.size.height + 10; + self.key2Field = [[UITextField alloc] initWithFrame:key2Frame]; + [self.key2Field setPlaceholder:@"key2"]; + [self.key2Field setDelegate:self]; + [self.key2Field setReturnKeyType:UIReturnKeyDone]; + [self.key2Field setBorderStyle:UITextBorderStyleRoundedRect]; + + CGRect val2Frame = key2Frame; + val2Frame.origin.x += self.view.bounds.size.width / 2; + self.key2Value = [[UITextField alloc] initWithFrame:val2Frame]; + [self.key2Value setPlaceholder:@"value2"]; + [self.key2Value setDelegate:self]; + [self.key2Value setReturnKeyType:UIReturnKeyDone]; + [self.key2Value setBorderStyle:UITextBorderStyleRoundedRect]; + + [self.sharedControlsContainerView addSubview:self.key1Field]; + [self.sharedControlsContainerView addSubview:self.key1Value]; + [self.sharedControlsContainerView addSubview:self.key2Field]; + [self.sharedControlsContainerView addSubview:self.key2Value]; + + self.goButton = [UIButton buttonWithType:UIButtonTypeSystem]; + self.goButton.frame = CGRectMake(20, CGRectGetMaxY(self.key2Field.frame) + 20, + self.view.bounds.size.width - 40, 40); + [self.goButton setTitle:@"Request Ad" forState:UIControlStateNormal]; + [self.goButton setTitleColor:[UIColor blackColor] + forState:UIControlStateNormal]; + [self.goButton setTitleColor:[UIColor grayColor] + forState:UIControlStateSelected]; + + self.goButton.layer.borderColor = [[UIColor lightGrayColor] CGColor]; + self.goButton.layer.borderWidth = 0.5; + self.goButton.layer.cornerRadius = 4; + self.goButton.backgroundColor = + [UIColor colorWithRed:1.0 green:1.0 blue:0.99 alpha:1.0]; + [self.goButton addTarget:self + action:@selector(goPressed) + forControlEvents:UIControlEventTouchUpInside]; + [self.sharedControlsContainerView addSubview:self.goButton]; + + [self.view addSubview:self.sharedControlsContainerView]; } -- (void) adTypeSwitched:(UISegmentedControl *)sender{ - - NSInteger selectedIndex = [sender selectedSegmentIndex]; - - switch (selectedIndex) { - case 0: - [self interstitialPressed]; - break; - case 1: - [self bannerPressed]; - break; - case 2: - if(self.nativeAdTypeControl.selectedSegmentIndex == 0){ - [self nativePressed]; - } - else { - [self nativeCustomPressed]; - } - break; - - default: - break; +- (void)adTypeSwitched:(UISegmentedControl *)sender { + + NSInteger selectedIndex = [sender selectedSegmentIndex]; + + switch (selectedIndex) { + case 0: + [self interstitialPressed]; + break; + case 1: + [self bannerPressed]; + break; + case 2: + if (self.nativeAdTypeControl.selectedSegmentIndex == 0) { + [self nativePressed]; + } else { + [self nativeCustomPressed]; } + break; + + default: + break; + } } --(void)nativeAdTypeSwitch:(UISegmentedControl*) sender { - - NSInteger selectedIndex = sender.selectedSegmentIndex; - - switch (selectedIndex) { - case 0: - [self nativePressed]; - break; - case 1: - [self nativeCustomPressed]; - break; - } +- (void)nativeAdTypeSwitch:(UISegmentedControl *)sender { + + NSInteger selectedIndex = sender.selectedSegmentIndex; + + switch (selectedIndex) { + case 0: + [self nativePressed]; + break; + case 1: + [self nativeCustomPressed]; + break; + } } --(void)interstitialPressed { - [self.nativeAdTypeControl removeFromSuperview]; - [self.bannerSizeHeight removeFromSuperview]; - [self.bannerSizeWidth removeFromSuperview]; - - [self resetSharedControls]; +- (void)interstitialPressed { + [self.nativeAdTypeControl removeFromSuperview]; + [self.bannerSizeHeight removeFromSuperview]; + [self.bannerSizeWidth removeFromSuperview]; + + [self resetSharedControls]; } --(void)bannerPressed { - [self.view addSubview:self.bannerSizeHeight]; - [self.view addSubview:self.bannerSizeWidth]; - [self.nativeAdTypeControl removeFromSuperview]; - [self.nativeAdTypeCustomId removeFromSuperview]; - - [self offsetSharedControls]; +- (void)bannerPressed { + [self.view addSubview:self.bannerSizeHeight]; + [self.view addSubview:self.bannerSizeWidth]; + [self.nativeAdTypeControl removeFromSuperview]; + [self.nativeAdTypeCustomId removeFromSuperview]; + + [self offsetSharedControls]; } --(void)nativePressed { - [self.view addSubview:self.nativeAdTypeControl]; - [self.nativeAdTypeCustomId removeFromSuperview]; - [self.bannerSizeHeight removeFromSuperview]; - [self.bannerSizeWidth removeFromSuperview]; - - [self offsetSharedControls]; +- (void)nativePressed { + [self.view addSubview:self.nativeAdTypeControl]; + [self.nativeAdTypeCustomId removeFromSuperview]; + [self.bannerSizeHeight removeFromSuperview]; + [self.bannerSizeWidth removeFromSuperview]; + + [self offsetSharedControls]; } --(void)nativeCustomPressed { - [self.view addSubview:self.nativeAdTypeControl]; - [self.view addSubview:self.nativeAdTypeCustomId]; - [self.bannerSizeHeight removeFromSuperview]; - [self.bannerSizeWidth removeFromSuperview]; - - [self offsetSharedControlsCustom]; +- (void)nativeCustomPressed { + [self.view addSubview:self.nativeAdTypeControl]; + [self.view addSubview:self.nativeAdTypeCustomId]; + [self.bannerSizeHeight removeFromSuperview]; + [self.bannerSizeWidth removeFromSuperview]; + + [self offsetSharedControlsCustom]; } --(void)resetSharedControls { - self.sharedControlsContainerView.frame = sharedControlsOriginalFrame; +- (void)resetSharedControls { + self.sharedControlsContainerView.frame = sharedControlsOriginalFrame; } --(void)offsetSharedControls { - CGRect offsetFrame = sharedControlsOriginalFrame; - offsetFrame.origin.y += 60; - self.sharedControlsContainerView.frame = offsetFrame; +- (void)offsetSharedControls { + CGRect offsetFrame = sharedControlsOriginalFrame; + offsetFrame.origin.y += 60; + self.sharedControlsContainerView.frame = offsetFrame; } --(void)offsetSharedControlsCustom { - CGRect offsetFrame = sharedControlsOriginalFrame; - offsetFrame.origin.y += 90; - self.sharedControlsContainerView.frame = offsetFrame; +- (void)offsetSharedControlsCustom { + CGRect offsetFrame = sharedControlsOriginalFrame; + offsetFrame.origin.y += 90; + self.sharedControlsContainerView.frame = offsetFrame; } --(void)setSelected:(bool)selected button:(UIButton*)button { - button.backgroundColor = selected ? [UIColor colorWithRed:102.0/255.0 green:211.0/255.0 blue:3.0/255.0 alpha:1.0] : [UIColor whiteColor]; - [button setSelected:selected]; +- (void)setSelected:(bool)selected button:(UIButton *)button { + button.backgroundColor = selected ? [UIColor colorWithRed:102.0 / 255.0 + green:211.0 / 255.0 + blue:3.0 / 255.0 + alpha:1.0] + : [UIColor whiteColor]; + [button setSelected:selected]; } --(void)goPressed { - - [self.adUnitIdField resignFirstResponder]; - [self.bannerSizeHeight resignFirstResponder]; - [self.bannerSizeWidth resignFirstResponder]; - [self.nativeAdTypeCustomId resignFirstResponder]; - [self.key1Field resignFirstResponder]; - [self.key1Value resignFirstResponder]; - [self.key2Field resignFirstResponder]; - [self.key2Value resignFirstResponder]; - - - NSString* adUnitId = self.adUnitIdField.text; - - NSString* key1 = self.key1Field.text; - NSString* val1 = self.key1Value.text; - - NSString* key2 = self.key2Field.text; - NSString* val2 = self.key2Value.text; - - NSMutableDictionary* targeting = [NSMutableDictionary dictionary]; - - if(key1 && val1 && [key1 length] > 0 && [val1 length] > 0) { - [targeting setObject:val1 forKey:key1]; - } - if(key2 && val2 && [key2 length] > 0 && [val2 length] > 0) { - [targeting setObject:val2 forKey:key2]; - } - - - if(self.adTypeControl.selectedSegmentIndex == 0) { - [self requestInterstitial:adUnitId targeting:targeting]; - } - else if (self.adTypeControl.selectedSegmentIndex == 1) { - - NSInteger bannerWidth = [self.bannerSizeWidth.text integerValue]; - NSInteger bannerHeight = [self.bannerSizeHeight.text integerValue]; - - CGSize bannerSize = CGSizeMake(bannerWidth, bannerHeight); - - [self requestBanner:adUnitId targeting:targeting size:bannerSize]; - } - else if (self.adTypeControl.selectedSegmentIndex == 2) { - bool isCustomTemlate = self.nativeAdTypeControl.selectedSegmentIndex == 1; +- (void)goPressed { - [self requestNativeAd:adUnitId targeting:targeting customTemplate:isCustomTemlate]; - } -} + [self.adUnitIdField resignFirstResponder]; + [self.bannerSizeHeight resignFirstResponder]; + [self.bannerSizeWidth resignFirstResponder]; + [self.nativeAdTypeCustomId resignFirstResponder]; + [self.key1Field resignFirstResponder]; + [self.key1Value resignFirstResponder]; + [self.key2Field resignFirstResponder]; + [self.key2Value resignFirstResponder]; + + NSString *adUnitId = self.adUnitIdField.text; + + NSString *key1 = self.key1Field.text; + NSString *val1 = self.key1Value.text; + + NSString *key2 = self.key2Field.text; + NSString *val2 = self.key2Value.text; + + NSMutableDictionary *targeting = [NSMutableDictionary dictionary]; + + if (key1 && val1 && [key1 length] > 0 && [val1 length] > 0) { + [targeting setObject:val1 forKey:key1]; + } + if (key2 && val2 && [key2 length] > 0 && [val2 length] > 0) { + [targeting setObject:val2 forKey:key2]; + } + + if (self.adTypeControl.selectedSegmentIndex == 0) { + [self requestInterstitial:adUnitId targeting:targeting]; + } else if (self.adTypeControl.selectedSegmentIndex == 1) { + + NSInteger bannerWidth = [self.bannerSizeWidth.text integerValue]; + NSInteger bannerHeight = [self.bannerSizeHeight.text integerValue]; + + CGSize bannerSize = CGSizeMake(bannerWidth, bannerHeight); + [self requestBanner:adUnitId targeting:targeting size:bannerSize]; + } else if (self.adTypeControl.selectedSegmentIndex == 2) { + bool isCustomTemlate = self.nativeAdTypeControl.selectedSegmentIndex == 1; + [self requestNativeAd:adUnitId + targeting:targeting + customTemplate:isCustomTemlate]; + } +} #pragma mark - Interstitial ads --(void)requestInterstitial:(NSString*)adUnitId targeting:(NSDictionary*)customTargeting { - - self.interstitial = [[DFPInterstitial alloc] initWithAdUnitID:adUnitId]; // @"/6499/example/interstitial" - [self.interstitial setDelegate:self]; - - DFPRequest* request = [DFPRequest request]; - request.customTargeting = customTargeting; // override targeting for manual testing purposes - [self.interstitial loadRequest:request]; +- (void)requestInterstitial:(NSString *)adUnitId + targeting:(NSDictionary *)customTargeting { + + self.interstitial = [[DFPInterstitial alloc] + initWithAdUnitID:adUnitId]; // @"/6499/example/interstitial" + [self.interstitial setDelegate:self]; + + DFPRequest *request = [DFPRequest request]; + request.customTargeting = + customTargeting; // override targeting for manual testing purposes + [self.interstitial loadRequest:request]; } -- (void)interstitialDidReceiveAd:(GADInterstitial *)ad{ - [self.interstitial presentFromRootViewController:self]; +- (void)interstitialDidReceiveAd:(GADInterstitial *)ad { + [self.interstitial presentFromRootViewController:self]; } -- (void)interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error { - - [[[UIAlertView alloc] initWithTitle:@"Error" - message:[NSString stringWithFormat:@"Failed to get ad: %@", error] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"INTERSTITIAL_ERROR" object:nil]; +- (void)interstitial:(GADInterstitial *)ad + didFailToReceiveAdWithError:(GADRequestError *)error { + + [[[UIAlertView alloc] + initWithTitle:@"Error" + message:[NSString + stringWithFormat:@"Failed to get ad: %@", error] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; + + [[NSNotificationCenter defaultCenter] + postNotificationName:@"INTERSTITIAL_ERROR" + object:nil]; } #pragma mark - Banner ads --(void)requestBanner:(NSString*)adUnitId targeting:(NSDictionary*)customTargeting size:(CGSize)bannerSize { - if(self.bannerView){ - [self.bannerView removeFromSuperview]; - } - - GADAdSize size = [self getAdSize:bannerSize]; - if(size.size.width == kGADAdSizeInvalid.size.width && size.size.height == kGADAdSizeInvalid.size.height){ - return; - } - - self.bannerView = [[DFPBannerView alloc] initWithAdSize:size]; - self.bannerView.adUnitID = adUnitId; // @"/6499/example/banner" - self.bannerView.rootViewController = self; - DFPRequest* request = [DFPRequest request]; - request.customTargeting = customTargeting; // override targeting for manual testing purposes - [self.bannerView loadRequest:request]; - - self.bannerView.backgroundColor = [UIColor colorWithRed:0.93 green:0.93 blue:0.93 alpha:1.0]; - self.bannerView.frame = CGRectMake(0, - self.view.bounds.size.height - self.bannerView.bounds.size.height, - self.bannerView.bounds.size.width, - self.bannerView.bounds.size.height); - [self.view addSubview:self.bannerView]; +- (void)requestBanner:(NSString *)adUnitId + targeting:(NSDictionary *)customTargeting + size:(CGSize)bannerSize { + if (self.bannerView) { + [self.bannerView removeFromSuperview]; + } + + GADAdSize size = [self getAdSize:bannerSize]; + if (size.size.width == kGADAdSizeInvalid.size.width && + size.size.height == kGADAdSizeInvalid.size.height) { + return; + } + + self.bannerView = [[DFPBannerView alloc] initWithAdSize:size]; + self.bannerView.adUnitID = adUnitId; // @"/6499/example/banner" + self.bannerView.rootViewController = self; + DFPRequest *request = [DFPRequest request]; + request.customTargeting = + customTargeting; // override targeting for manual testing purposes + [self.bannerView loadRequest:request]; + + self.bannerView.backgroundColor = + [UIColor colorWithRed:0.93 green:0.93 blue:0.93 alpha:1.0]; + self.bannerView.frame = CGRectMake( + 0, self.view.bounds.size.height - self.bannerView.bounds.size.height, + self.bannerView.bounds.size.width, self.bannerView.bounds.size.height); + [self.view addSubview:self.bannerView]; } --(GADAdSize) getAdSize:(CGSize)size { - if(size.width == 320 && size.height == 50) { - return kGADAdSizeBanner; - } - if(size.width == 320 && size.height == 100) { - return kGADAdSizeLargeBanner; - } - if(size.width == 300 && size.height == 250) { - return kGADAdSizeMediumRectangle; - } - if(size.width == 468 && size.height == 60) { - return kGADAdSizeFullBanner; - } - if(size.width == 728 && size.height == 90) { - return kGADAdSizeLeaderboard; - } - - [[[UIAlertView alloc] initWithTitle:@"Invalid banner size" message:@"invalid banner ad size (for this demo app)." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show]; - - return kGADAdSizeInvalid; - +- (GADAdSize)getAdSize:(CGSize)size { + if (size.width == 320 && size.height == 50) { + return kGADAdSizeBanner; + } + if (size.width == 320 && size.height == 100) { + return kGADAdSizeLargeBanner; + } + if (size.width == 300 && size.height == 250) { + return kGADAdSizeMediumRectangle; + } + if (size.width == 468 && size.height == 60) { + return kGADAdSizeFullBanner; + } + if (size.width == 728 && size.height == 90) { + return kGADAdSizeLeaderboard; + } + + [[[UIAlertView alloc] + initWithTitle:@"Invalid banner size" + message:@"invalid banner ad size (for this demo app)." + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; + + return kGADAdSizeInvalid; } #pragma mark - Native ads --(void)requestNativeAd:(NSString*)adUnitId targeting:(NSDictionary*)customTargeting customTemplate:(bool)custom{ - - NSArray* adTypes = @[ custom ? kGADAdLoaderAdTypeNativeCustomTemplate : kGADAdLoaderAdTypeNativeContent ]; - - self.adLoader = [[GADAdLoader alloc] - initWithAdUnitID:adUnitId // @"/6499/example/native" - rootViewController:self - adTypes:adTypes - options:nil]; - - [self.adLoader setDelegate:self]; - DFPRequest* request = [DFPRequest request]; - request.customTargeting = customTargeting; // override targeting for manual testing purposes - [self.adLoader loadRequest:request]; -} +- (void)requestNativeAd:(NSString *)adUnitId + targeting:(NSDictionary *)customTargeting + customTemplate:(bool)custom { -- (void)adLoader:(GADAdLoader *)adLoader didReceiveNativeContentAd:(GADNativeContentAd *)nativeContentAd { - - if(self.nativeResult){ - [self.nativeResult removeFromParentViewController]; - } - - self.nativeResult = [[UIViewController alloc] init]; - self.nativeResult.view.frame = self.view.bounds; - UIView* overlay = [[UIView alloc] initWithFrame:self.nativeResult.view.bounds]; - overlay.backgroundColor = [UIColor blackColor]; - overlay.alpha = 0.8; - [self.nativeResult.view addSubview:overlay]; - - GADNativeContentAdView* contentAdView = - [[[NSBundle mainBundle] loadNibNamed:@"NativeContentAdView" - owner:nil - options:nil] firstObject]; - - // Associate the app install ad view with the app install ad object. - // This is required to make the ad clickable. - contentAdView.nativeContentAd = nativeContentAd; - - // Populate the app install ad view with the app install ad assets. - ((UIImageView *)contentAdView.logoView).image = nativeContentAd.logo.image; - ((UIImageView *)contentAdView.imageView).image = ((GADNativeAdImage *)[nativeContentAd.images firstObject]).image; - ((UILabel *)contentAdView.headlineView).text = nativeContentAd.headline; - ((UILabel *)contentAdView.advertiserView).text = nativeContentAd.advertiser; - ((UILabel *)contentAdView.bodyView).text = nativeContentAd.body; - [((UIButton *)contentAdView.callToActionView) setTitle:nativeContentAd.callToAction forState:UIControlStateNormal]; - - // In order for the SDK to process touch events properly, user interaction - // should be disabled on UIButtons. Must be disabled in nib -- just highlighted here for completeness. - contentAdView.callToActionView.userInteractionEnabled = NO; - - // size appropriately - CGFloat padding = self.view.bounds.size.width * 0.1; - contentAdView.frame = CGRectMake(padding, - self.view.bounds.size.height * 0.2, - self.view.bounds.size.width - padding*2, - self.view.bounds.size.height * 0.6); - - // add a border and shadow, just to highlight in this demo application - contentAdView.layer.borderColor = [[UIColor grayColor] CGColor]; - contentAdView.layer.borderWidth = 0.5; - contentAdView.layer.cornerRadius = 2; - contentAdView.layer.shadowColor = [[UIColor grayColor] CGColor]; - contentAdView.layer.shadowOffset = CGSizeMake(0,5); - contentAdView.layer.shadowRadius = 5; - contentAdView.layer.shadowOpacity = 0.6; - contentAdView.layer.masksToBounds = NO; - - UIImage* closeButtonImage = [UIImage imageNamed:@"closeButton.png"]; - UIButton* closeButton = [[UIButton alloc] init]; - [closeButton setImage:closeButtonImage forState:UIControlStateNormal]; - [closeButton addTarget:self action:@selector(nativeCloseButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - closeButton.frame = CGRectMake(0,0,closeButtonImage.size.width, closeButtonImage.size.height); - [self.nativeResult.view addSubview:closeButton]; - - [self.nativeResult.view addSubview:contentAdView]; - - [self.view addSubview:self.nativeResult.view]; - [self addChildViewController:self.nativeResult]; - [self.nativeResult didMoveToParentViewController:self]; + NSArray *adTypes = @[ custom ? kGADAdLoaderAdTypeNativeCustomTemplate + : kGADAdLoaderAdTypeNativeContent ]; + + self.adLoader = + [[GADAdLoader alloc] initWithAdUnitID:adUnitId // @"/6499/example/native" + rootViewController:self + adTypes:adTypes + options:nil]; + + [self.adLoader setDelegate:self]; + DFPRequest *request = [DFPRequest request]; + request.customTargeting = + customTargeting; // override targeting for manual testing purposes + [self.adLoader loadRequest:request]; } --(void)nativeCloseButtonPressed { - [self.nativeResult willMoveToParentViewController:nil]; +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeContentAd:(GADNativeContentAd *)nativeContentAd { + + if (self.nativeResult) { [self.nativeResult removeFromParentViewController]; - [self.nativeResult.view removeFromSuperview]; + } + + self.nativeResult = [[UIViewController alloc] init]; + self.nativeResult.view.frame = self.view.bounds; + UIView *overlay = + [[UIView alloc] initWithFrame:self.nativeResult.view.bounds]; + overlay.backgroundColor = [UIColor blackColor]; + overlay.alpha = 0.8; + [self.nativeResult.view addSubview:overlay]; + + GADNativeContentAdView *contentAdView = + [[[NSBundle mainBundle] loadNibNamed:@"NativeContentAdView" + owner:nil + options:nil] firstObject]; + + // Associate the app install ad view with the app install ad object. + // This is required to make the ad clickable. + contentAdView.nativeContentAd = nativeContentAd; + + // Populate the app install ad view with the app install ad assets. + ((UIImageView *)contentAdView.logoView).image = nativeContentAd.logo.image; + ((UIImageView *)contentAdView.imageView).image = + ((GADNativeAdImage *)[nativeContentAd.images firstObject]).image; + ((UILabel *)contentAdView.headlineView).text = nativeContentAd.headline; + ((UILabel *)contentAdView.advertiserView).text = nativeContentAd.advertiser; + ((UILabel *)contentAdView.bodyView).text = nativeContentAd.body; + [((UIButton *)contentAdView.callToActionView) + setTitle:nativeContentAd.callToAction + forState:UIControlStateNormal]; + + // In order for the SDK to process touch events properly, user interaction + // should be disabled on UIButtons. Must be disabled in nib -- just + // highlighted here for completeness. + contentAdView.callToActionView.userInteractionEnabled = NO; + + // size appropriately + CGFloat padding = self.view.bounds.size.width * 0.1; + contentAdView.frame = CGRectMake(padding, self.view.bounds.size.height * 0.2, + self.view.bounds.size.width - padding * 2, + self.view.bounds.size.height * 0.6); + + // add a border and shadow, just to highlight in this demo application + contentAdView.layer.borderColor = [[UIColor grayColor] CGColor]; + contentAdView.layer.borderWidth = 0.5; + contentAdView.layer.cornerRadius = 2; + contentAdView.layer.shadowColor = [[UIColor grayColor] CGColor]; + contentAdView.layer.shadowOffset = CGSizeMake(0, 5); + contentAdView.layer.shadowRadius = 5; + contentAdView.layer.shadowOpacity = 0.6; + contentAdView.layer.masksToBounds = NO; + + UIImage *closeButtonImage = [UIImage imageNamed:@"closeButton.png"]; + UIButton *closeButton = [[UIButton alloc] init]; + [closeButton setImage:closeButtonImage forState:UIControlStateNormal]; + [closeButton addTarget:self + action:@selector(nativeCloseButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + closeButton.frame = CGRectMake(0, 0, closeButtonImage.size.width, + closeButtonImage.size.height); + [self.nativeResult.view addSubview:closeButton]; + + [self.nativeResult.view addSubview:contentAdView]; + + [self.view addSubview:self.nativeResult.view]; + [self addChildViewController:self.nativeResult]; + [self.nativeResult didMoveToParentViewController:self]; } -- (void)adLoader:(GADAdLoader *)adLoader didFailToReceiveAdWithError:(GADRequestError *)error { - - [[[UIAlertView alloc] initWithTitle:@"Error" - message:[NSString stringWithFormat:@"Failed to get ad: %@", error] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; +- (void)nativeCloseButtonPressed { + [self.nativeResult willMoveToParentViewController:nil]; + [self.nativeResult removeFromParentViewController]; + [self.nativeResult.view removeFromSuperview]; +} + +- (void)adLoader:(GADAdLoader *)adLoader + didFailToReceiveAdWithError:(GADRequestError *)error { + + [[[UIAlertView alloc] + initWithTitle:@"Error" + message:[NSString + stringWithFormat:@"Failed to get ad: %@", error] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; } - (NSArray *)nativeCustomTemplateIDsForAdLoader:(GADAdLoader *)adLoader { - NSString* customTemplateId = self.nativeAdTypeCustomId.text; - NSLog(@"native custom template: %@", customTemplateId); - return @[customTemplateId]; + NSString *customTemplateId = self.nativeAdTypeCustomId.text; + NSLog(@"native custom template: %@", customTemplateId); + return @[ customTemplateId ]; } /// Tells the delegate that a native custom template ad was received. -- (void)adLoader:(GADAdLoader *)adLoader didReceiveNativeCustomTemplateAd:(GADNativeCustomTemplateAd *)nativeCustomTemplateAd { - NSLog(@"Got a custom native ad! %@", nativeCustomTemplateAd); - - - if(self.nativeResult){ - [self.nativeResult removeFromParentViewController]; - } - - self.nativeResult = [[UIViewController alloc] init]; - self.nativeResult.view.frame = self.view.bounds; - UIView* overlay = [[UIView alloc] initWithFrame:self.nativeResult.view.bounds]; - overlay.backgroundColor = [UIColor blackColor]; - overlay.alpha = 0.8; - [self.nativeResult.view addSubview:overlay]; - - CGRect contentView = CGRectInset(self.nativeResult.view.bounds, 30, 50); - UIView* content = [[UIView alloc] initWithFrame: contentView]; - content.layer.cornerRadius = 3; - content.backgroundColor = [UIColor whiteColor]; - - int labelY = 0; - int labelWidth = contentView.size.width; - int labelHeight = 30; - for(NSString* assetKey in nativeCustomTemplateAd.availableAssetKeys) { - NSString* str = [nativeCustomTemplateAd stringForKey:assetKey]; - if(str){ - UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0, labelY, labelWidth, labelHeight)]; - label.text = str; - label.font = [UIFont systemFontOfSize:10]; - label.textColor = [UIColor blackColor]; - [content addSubview:label]; - labelY += labelHeight; - } +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeCustomTemplateAd: + (GADNativeCustomTemplateAd *)nativeCustomTemplateAd { + NSLog(@"Got a custom native ad! %@", nativeCustomTemplateAd); + + if (self.nativeResult) { + [self.nativeResult removeFromParentViewController]; + } + + self.nativeResult = [[UIViewController alloc] init]; + self.nativeResult.view.frame = self.view.bounds; + UIView *overlay = + [[UIView alloc] initWithFrame:self.nativeResult.view.bounds]; + overlay.backgroundColor = [UIColor blackColor]; + overlay.alpha = 0.8; + [self.nativeResult.view addSubview:overlay]; + + CGRect contentView = CGRectInset(self.nativeResult.view.bounds, 30, 50); + UIView *content = [[UIView alloc] initWithFrame:contentView]; + content.layer.cornerRadius = 3; + content.backgroundColor = [UIColor whiteColor]; + + int labelY = 0; + int labelWidth = contentView.size.width; + int labelHeight = 30; + for (NSString *assetKey in nativeCustomTemplateAd.availableAssetKeys) { + NSString *str = [nativeCustomTemplateAd stringForKey:assetKey]; + if (str) { + UILabel *label = [[UILabel alloc] + initWithFrame:CGRectMake(0, labelY, labelWidth, labelHeight)]; + label.text = str; + label.font = [UIFont systemFontOfSize:10]; + label.textColor = [UIColor blackColor]; + [content addSubview:label]; + labelY += labelHeight; } - - int imgY = labelY; - int imgWidth = 60; - int imgHeight = 60; - - for(NSString* assetKey in nativeCustomTemplateAd.availableAssetKeys) { - GADNativeAdImage* adImage = [nativeCustomTemplateAd imageForKey:assetKey]; - if(adImage){ - UIImageView* img = [[UIImageView alloc] initWithImage:adImage.image]; - img.frame = CGRectMake(0, imgY, imgWidth, imgHeight); - [content addSubview:img]; - imgY += imgHeight; - } + } + + int imgY = labelY; + int imgWidth = 60; + int imgHeight = 60; + + for (NSString *assetKey in nativeCustomTemplateAd.availableAssetKeys) { + GADNativeAdImage *adImage = [nativeCustomTemplateAd imageForKey:assetKey]; + if (adImage) { + UIImageView *img = [[UIImageView alloc] initWithImage:adImage.image]; + img.frame = CGRectMake(0, imgY, imgWidth, imgHeight); + [content addSubview:img]; + imgY += imgHeight; } - - UIImage* closeButtonImage = [UIImage imageNamed:@"closeButton.png"]; - UIButton* closeButton = [[UIButton alloc] init]; - [closeButton setImage:closeButtonImage forState:UIControlStateNormal]; - [closeButton addTarget:self action:@selector(nativeCloseButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - closeButton.frame = CGRectMake(0,0,closeButtonImage.size.width, closeButtonImage.size.height); - [self.nativeResult.view addSubview:closeButton]; - - [self.nativeResult.view addSubview:content]; - - [self.view addSubview:self.nativeResult.view]; - [self addChildViewController:self.nativeResult]; - [self.nativeResult didMoveToParentViewController:self]; + } + + UIImage *closeButtonImage = [UIImage imageNamed:@"closeButton.png"]; + UIButton *closeButton = [[UIButton alloc] init]; + [closeButton setImage:closeButtonImage forState:UIControlStateNormal]; + [closeButton addTarget:self + action:@selector(nativeCloseButtonPressed) + forControlEvents:UIControlEventTouchUpInside]; + closeButton.frame = CGRectMake(0, 0, closeButtonImage.size.width, + closeButtonImage.size.height); + [self.nativeResult.view addSubview:closeButton]; + + [self.nativeResult.view addSubview:content]; + + [self.view addSubview:self.nativeResult.view]; + [self addChildViewController:self.nativeResult]; + [self.nativeResult didMoveToParentViewController:self]; } #pragma mark - UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField { - [textField resignFirstResponder]; - return NO; + [textField resignFirstResponder]; + return NO; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.h index f49fbdd4..d712762e 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.h @@ -7,10 +7,10 @@ #import -@interface UIColor(BVColor) +@interface UIColor (BVColor) -+(UIColor*)bazaarvoiceNavy; -+(UIColor*)bazaarvoiceTeal; -+(UIColor*)bazaarvoiceGold; ++ (UIColor *)bazaarvoiceNavy; ++ (UIColor *)bazaarvoiceTeal; ++ (UIColor *)bazaarvoiceGold; @end \ No newline at end of file diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.m index 184f621d..f4499444 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UIColor+BVColor.m @@ -7,18 +7,18 @@ #import "UIColor+BVColor.h" -@implementation UIColor(BVColor) +@implementation UIColor (BVColor) -+(UIColor*)bazaarvoiceNavy { - return [UIColor colorWithRed:0 green:0.24 blue:0.3 alpha:1.0]; ++ (UIColor *)bazaarvoiceNavy { + return [UIColor colorWithRed:0 green:0.24 blue:0.3 alpha:1.0]; } -+(UIColor*)bazaarvoiceTeal { - return [UIColor colorWithRed:0 green:0.67 blue:0.56 alpha:1.0]; ++ (UIColor *)bazaarvoiceTeal { + return [UIColor colorWithRed:0 green:0.67 blue:0.56 alpha:1.0]; } -+(UIColor*)bazaarvoiceGold { - return [UIColor colorWithRed:0.99 green:0.72 blue:0.07 alpha:1.0]; ++ (UIColor *)bazaarvoiceGold { + return [UIColor colorWithRed:0.99 green:0.72 blue:0.07 alpha:1.0]; } @end \ No newline at end of file diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.h index 44e0e4df..32333779 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.h @@ -9,6 +9,6 @@ @interface UILabelWithInset : UILabel --(id)initWithInset:(UIEdgeInsets)insets; +- (id)initWithInset:(UIEdgeInsets)insets; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.m index f97005af..9be06b51 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/Helper/UILabelWithInset.m @@ -7,7 +7,7 @@ #import "UILabelWithInset.h" -@interface UILabelWithInset() +@interface UILabelWithInset () @property UIEdgeInsets insets; @@ -15,18 +15,18 @@ @interface UILabelWithInset() @implementation UILabelWithInset --(id)initWithInset:(UIEdgeInsets)insets { - self = [super init]; - if(self){ - self.insets = insets; - } - return self; +- (id)initWithInset:(UIEdgeInsets)insets { + self = [super init]; + if (self) { + self.insets = insets; + } + return self; } --(void)drawTextInRect:(CGRect)rect { +- (void)drawTextInRect:(CGRect)rect { - CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.insets); - [super drawTextInRect:insetRect]; + CGRect insetRect = UIEdgeInsetsInsetRect(rect, self.insets); + [super drawTextInRect:insetRect]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.h index 4f7862da..97314a16 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.h @@ -10,7 +10,7 @@ @interface InterstitialDemo : NSObject --(id)initWithRootViewController:(UIViewController*)rootViewController; --(void)requestInterstitial; +- (id)initWithRootViewController:(UIViewController *)rootViewController; +- (void)requestInterstitial; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.m index 8cdb0f5f..97542456 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/InterstitialDemo.m @@ -10,58 +10,65 @@ @import GoogleMobileAds; +@interface InterstitialDemo () -@interface InterstitialDemo() - -@property DFPInterstitial* interstitial; -@property UIViewController* rootViewController; +@property DFPInterstitial *interstitial; +@property UIViewController *rootViewController; @end @implementation InterstitialDemo --(id)initWithRootViewController:(UIViewController*)rootViewController { - self = [super init]; - if(self){ - self.rootViewController = rootViewController; - } - return self; +- (id)initWithRootViewController:(UIViewController *)rootViewController { + self = [super init]; + if (self) { + self.rootViewController = rootViewController; + } + return self; } --(void)requestInterstitial { - - self.interstitial = [[DFPInterstitial alloc] initWithAdUnitID:@"/6499/example/interstitial"]; //Test adUnitId. Replace with your targeted adUnitId. - [self.interstitial setDelegate:self]; - - DFPRequest* request = [DFPRequest request]; - request.testDevices = @[ kGADSimulatorID ]; - request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; - [self.interstitial loadRequest:request]; +- (void)requestInterstitial { + + self.interstitial = [[DFPInterstitial alloc] + initWithAdUnitID:@"/6499/example/interstitial"]; // Test adUnitId. Replace + // with your targeted + // adUnitId. + [self.interstitial setDelegate:self]; + + DFPRequest *request = [DFPRequest request]; + request.testDevices = @[ kGADSimulatorID ]; + request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; + [self.interstitial loadRequest:request]; } #pragma mark - GADInterstitialDelegate -- (void)interstitialDidReceiveAd:(GADInterstitial *)ad{ - /* - * Note: do not show the interstitial from this callback. - * Instead, at the appropriate time, check: - * [self.interstitial isReady]; - * and if true, then call: - * [self.interstitial presentFromRootViewController:self]; - */ - - [self.interstitial presentFromRootViewController:self.rootViewController]; +- (void)interstitialDidReceiveAd:(GADInterstitial *)ad { + /* + * Note: do not show the interstitial from this callback. + * Instead, at the appropriate time, check: + * [self.interstitial isReady]; + * and if true, then call: + * [self.interstitial presentFromRootViewController:self]; + */ + + [self.interstitial presentFromRootViewController:self.rootViewController]; } -- (void)interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error { - - [[[UIAlertView alloc] initWithTitle:@"Error" - message:[NSString stringWithFormat:@"Failed to get ad: %@", error] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"INTERSTITIAL_ERROR" object:nil]; +- (void)interstitial:(GADInterstitial *)ad + didFailToReceiveAdWithError:(GADRequestError *)error { + + [[[UIAlertView alloc] + initWithTitle:@"Error" + message:[NSString + stringWithFormat:@"Failed to get ad: %@", error] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; + + [[NSNotificationCenter defaultCenter] + postNotificationName:@"INTERSTITIAL_ERROR" + object:nil]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.h index 02a0980e..05d09783 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.h @@ -9,6 +9,6 @@ @interface NativeContentAdDemoViewController : UIViewController -@property (weak, nonatomic) IBOutlet UIButton* closeButton; +@property(weak, nonatomic) IBOutlet UIButton *closeButton; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.m index b5cbd3df..cc91d407 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/NativeContentAdDemoViewController.m @@ -11,92 +11,99 @@ @import GoogleMobileAds; +@interface NativeContentAdDemoViewController () < + GADNativeContentAdLoaderDelegate> -@interface NativeContentAdDemoViewController() - -@property GADAdLoader* adLoader; +@property GADAdLoader *adLoader; @end @implementation NativeContentAdDemoViewController - (void)viewDidLoad { - [super viewDidLoad]; - - //Test adUnitId. Replace with your targeted adUnitId. - self.adLoader = [[GADAdLoader alloc] - initWithAdUnitID:@"/6499/example/native" - rootViewController:self - adTypes:@[ kGADAdLoaderAdTypeNativeContent ] - options:nil]; - - [self.adLoader setDelegate:self]; - DFPRequest* request = [DFPRequest request]; - request.testDevices = @[ kGADSimulatorID ]; - request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; - [self.adLoader loadRequest:request]; -} + [super viewDidLoad]; + // Test adUnitId. Replace with your targeted adUnitId. + self.adLoader = + [[GADAdLoader alloc] initWithAdUnitID:@"/6499/example/native" + rootViewController:self + adTypes:@[ kGADAdLoaderAdTypeNativeContent ] + options:nil]; + + [self.adLoader setDelegate:self]; + DFPRequest *request = [DFPRequest request]; + request.testDevices = @[ kGADSimulatorID ]; + request.customTargeting = [[BVSDKManager sharedManager] getCustomTargeting]; + [self.adLoader loadRequest:request]; +} #pragma mark - GADNativeContentAdLoaderDelegate -- (void)adLoader:(GADAdLoader *)adLoader didReceiveNativeContentAd:(GADNativeContentAd *)nativeContentAd { - - // configure our native content ad view with the given values, and display! - - GADNativeContentAdView* contentAdView = - [[[NSBundle mainBundle] loadNibNamed:@"NativeContentAdView" - owner:nil - options:nil] firstObject]; - - // Associate the app install ad view with the app install ad object. - // This is required to make the ad clickable. - contentAdView.nativeContentAd = nativeContentAd; - - // Populate the app install ad view with the app install ad assets. - ((UIImageView *)contentAdView.logoView).image = nativeContentAd.logo.image; - ((UIImageView *)contentAdView.imageView).image = ((GADNativeAdImage *)[nativeContentAd.images firstObject]).image; - ((UILabel *)contentAdView.headlineView).text = nativeContentAd.headline; - ((UILabel *)contentAdView.advertiserView).text = nativeContentAd.advertiser; - ((UILabel *)contentAdView.bodyView).text = nativeContentAd.body; - [((UIButton *)contentAdView.callToActionView) setTitle:nativeContentAd.callToAction forState:UIControlStateNormal]; - - // In order for the SDK to process touch events properly, user interaction - // should be disabled on UIButtons. Must be disabled in nib -- just highlighted here for completeness. - contentAdView.callToActionView.userInteractionEnabled = NO; - - // size appropriately - CGFloat padding = self.view.bounds.size.width * 0.1; - contentAdView.frame = CGRectMake(padding, - self.view.bounds.size.height * 0.18, - self.view.bounds.size.width - padding*2, - self.view.bounds.size.height * 0.4); - - // Add appInstallAdView to the view controller's view.. - [self.view addSubview:contentAdView]; - - // add a border and shadow, just to highlight in this demo application - contentAdView.layer.borderColor = [[UIColor grayColor] CGColor]; - contentAdView.layer.borderWidth = 0.5; - contentAdView.layer.cornerRadius = 2; - contentAdView.layer.shadowColor = [[UIColor grayColor] CGColor]; - contentAdView.layer.shadowOffset = CGSizeMake(0,5); - contentAdView.layer.shadowRadius = 5; - contentAdView.layer.shadowOpacity = 0.6; - contentAdView.layer.masksToBounds = NO; +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeContentAd:(GADNativeContentAd *)nativeContentAd { + + // configure our native content ad view with the given values, and display! + + GADNativeContentAdView *contentAdView = + [[[NSBundle mainBundle] loadNibNamed:@"NativeContentAdView" + owner:nil + options:nil] firstObject]; + + // Associate the app install ad view with the app install ad object. + // This is required to make the ad clickable. + contentAdView.nativeContentAd = nativeContentAd; + + // Populate the app install ad view with the app install ad assets. + ((UIImageView *)contentAdView.logoView).image = nativeContentAd.logo.image; + ((UIImageView *)contentAdView.imageView).image = + ((GADNativeAdImage *)[nativeContentAd.images firstObject]).image; + ((UILabel *)contentAdView.headlineView).text = nativeContentAd.headline; + ((UILabel *)contentAdView.advertiserView).text = nativeContentAd.advertiser; + ((UILabel *)contentAdView.bodyView).text = nativeContentAd.body; + [((UIButton *)contentAdView.callToActionView) + setTitle:nativeContentAd.callToAction + forState:UIControlStateNormal]; + + // In order for the SDK to process touch events properly, user interaction + // should be disabled on UIButtons. Must be disabled in nib -- just + // highlighted here for completeness. + contentAdView.callToActionView.userInteractionEnabled = NO; + + // size appropriately + CGFloat padding = self.view.bounds.size.width * 0.1; + contentAdView.frame = CGRectMake(padding, self.view.bounds.size.height * 0.18, + self.view.bounds.size.width - padding * 2, + self.view.bounds.size.height * 0.4); + + // Add appInstallAdView to the view controller's view.. + [self.view addSubview:contentAdView]; + + // add a border and shadow, just to highlight in this demo application + contentAdView.layer.borderColor = [[UIColor grayColor] CGColor]; + contentAdView.layer.borderWidth = 0.5; + contentAdView.layer.cornerRadius = 2; + contentAdView.layer.shadowColor = [[UIColor grayColor] CGColor]; + contentAdView.layer.shadowOffset = CGSizeMake(0, 5); + contentAdView.layer.shadowRadius = 5; + contentAdView.layer.shadowOpacity = 0.6; + contentAdView.layer.masksToBounds = NO; } -- (void)adLoader:(GADAdLoader *)adLoader didFailToReceiveAdWithError:(GADRequestError *)error { - - [[[UIAlertView alloc] initWithTitle:@"Error" - message:[NSString stringWithFormat:@"Failed to get ad: %@", error] - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; +- (void)adLoader:(GADAdLoader *)adLoader + didFailToReceiveAdWithError:(GADRequestError *)error { + + [[[UIAlertView alloc] + initWithTitle:@"Error" + message:[NSString + stringWithFormat:@"Failed to get ad: %@", error] + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; } - (IBAction)closeButtonPressed { - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; + [self.presentingViewController dismissViewControllerAnimated:YES + completion:nil]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.h b/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.h index d43d9a9e..343b2e53 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.h +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.h @@ -10,6 +10,6 @@ @interface RootViewController : UIViewController -@property (weak, nonatomic) IBOutlet UICollectionView* collectionView; +@property(weak, nonatomic) IBOutlet UICollectionView *collectionView; @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.m index 4455b9de..31bd0c50 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/RootViewController.m @@ -6,162 +6,186 @@ // #import "RootViewController.h" -#import -#import "UIColor+BVColor.h" #import "BVAdTypesCell.h" #import "BannerDemoViewController.h" +#import "GeneralizedDemoViewController.h" #import "InterstitialDemo.h" #import "NativeContentAdDemoViewController.h" -#import "GeneralizedDemoViewController.h" +#import "UIColor+BVColor.h" +#import -@interface RootViewController () +@interface RootViewController () -@property InterstitialDemo* interstitialDemo; +@property InterstitialDemo *interstitialDemo; @end @implementation RootViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.title = @"Ads Demo"; - self.view.backgroundColor = [UIColor whiteColor]; - - UIBarButtonItem* adUnitTestBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"gear.png"] style:UIBarButtonSystemItemCamera target:self action:@selector(adUnitTestButtonPressed)]; - self.navigationItem.rightBarButtonItem = adUnitTestBarButtonItem; - - UICollectionViewFlowLayout *layout= [[UICollectionViewFlowLayout alloc] init]; - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - layout.itemSize = CGSizeMake(200,140); - layout.sectionInset = UIEdgeInsetsMake(0,10,0,0); - - self.collectionView.collectionViewLayout = layout; - [self.collectionView setDataSource:self]; - [self.collectionView setDelegate:self]; - [self.collectionView registerNib:[UINib nibWithNibName:@"BVAdTypesCell" bundle:nil] forCellWithReuseIdentifier:@"cvCell"]; - [self.collectionView setBackgroundColor:[UIColor clearColor]]; - - [self.view addSubview:self.collectionView]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interstitialError) name:@"INTERSTITIAL_ERROR" object:nil]; + [super viewDidLoad]; + + self.title = @"Ads Demo"; + self.view.backgroundColor = [UIColor whiteColor]; + + UIBarButtonItem *adUnitTestBarButtonItem = [[UIBarButtonItem alloc] + initWithImage:[UIImage imageNamed:@"gear.png"] + style:UIBarButtonSystemItemCamera + target:self + action:@selector(adUnitTestButtonPressed)]; + self.navigationItem.rightBarButtonItem = adUnitTestBarButtonItem; + + UICollectionViewFlowLayout *layout = + [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.itemSize = CGSizeMake(200, 140); + layout.sectionInset = UIEdgeInsetsMake(0, 10, 0, 0); + + self.collectionView.collectionViewLayout = layout; + [self.collectionView setDataSource:self]; + [self.collectionView setDelegate:self]; + [self.collectionView registerNib:[UINib nibWithNibName:@"BVAdTypesCell" + bundle:nil] + forCellWithReuseIdentifier:@"cvCell"]; + [self.collectionView setBackgroundColor:[UIColor clearColor]]; + + [self.view addSubview:self.collectionView]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(interstitialError) + name:@"INTERSTITIAL_ERROR" + object:nil]; } --(void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; } --(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - return 3; +- (NSInteger)numberOfSectionsInCollectionView: + (UICollectionView *)collectionView { + return 3; } --(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return 1; +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return 1; } --(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - NSDictionary* itemData; - - switch (indexPath.section) { - case 0: - itemData = @{ - @"imageName": @"native.jpg", - @"title": @"Native Ads", - @"subtitle": @"Native ads blend into regular content inside your app" - }; - break; - case 1: - itemData = @{ - @"imageName": @"interstitial.jpg", - @"title": @"Interstitial Ad", - @"subtitle": @"Interstitial ads fill up the entire screen on your customer's device" - }; - break; - case 2: - itemData = @{ - @"imageName": @"banner.jpg", - @"title": @"Mobile Banner Ads", - @"subtitle": @"Mobile banner ads on a mobile device" - }; - break; - default: - break; - } - - BVAdTypesCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" forIndexPath:indexPath]; - cell.cellTitleLabel.text = [itemData objectForKey:@"title"]; - cell.cellDescriptionLabel.text = [itemData objectForKey:@"subtitle"]; - cell.backgroundImage.image = [UIImage imageNamed:[itemData objectForKey:@"imageName"]]; - - return cell; - +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + NSDictionary *itemData; + + switch (indexPath.section) { + case 0: + itemData = @{ + @"imageName" : @"native.jpg", + @"title" : @"Native Ads", + @"subtitle" : @"Native ads blend into regular content inside your app" + }; + break; + case 1: + itemData = @{ + @"imageName" : @"interstitial.jpg", + @"title" : @"Interstitial Ad", + @"subtitle" : @"Interstitial ads fill up the entire screen on your " + @"customer's device" + }; + break; + case 2: + itemData = @{ + @"imageName" : @"banner.jpg", + @"title" : @"Mobile Banner Ads", + @"subtitle" : @"Mobile banner ads on a mobile device" + }; + break; + default: + break; + } + + BVAdTypesCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" + forIndexPath:indexPath]; + cell.cellTitleLabel.text = [itemData objectForKey:@"title"]; + cell.cellDescriptionLabel.text = [itemData objectForKey:@"subtitle"]; + cell.backgroundImage.image = + [UIImage imageNamed:[itemData objectForKey:@"imageName"]]; + + return cell; } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; - - [self.navigationController.navigationBar setTranslucent:NO]; - [self.navigationController.navigationBar setBarTintColor:[UIColor bazaarvoiceNavy]]; - - // set "bazaarvoice:" logo in navigationBar - UILabel* titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,0,100,100)]; - titleLabel.text = @"bazaarvoice"; - titleLabel.textColor = [UIColor whiteColor]; - titleLabel.textAlignment = NSTextAlignmentCenter; - titleLabel.font = [UIFont fontWithName:@"ForalPro-Regular" size:36]; - self.navigationItem.titleView = titleLabel; + [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; + + [self.navigationController.navigationBar setTranslucent:NO]; + [self.navigationController.navigationBar + setBarTintColor:[UIColor bazaarvoiceNavy]]; + + // set "bazaarvoice:" logo in navigationBar + UILabel *titleLabel = + [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + titleLabel.text = @"bazaarvoice"; + titleLabel.textColor = [UIColor whiteColor]; + titleLabel.textAlignment = NSTextAlignmentCenter; + titleLabel.font = [UIFont fontWithName:@"ForalPro-Regular" size:36]; + self.navigationItem.titleView = titleLabel; } --(void)adUnitTestButtonPressed { - - GeneralizedDemoViewController* demoViewController = [[GeneralizedDemoViewController alloc] init]; - [self.navigationController pushViewController:demoViewController animated:YES]; +- (void)adUnitTestButtonPressed { + + GeneralizedDemoViewController *demoViewController = + [[GeneralizedDemoViewController alloc] init]; + [self.navigationController pushViewController:demoViewController + animated:YES]; } -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - switch (indexPath.section) { - case 0: { - [self presentContentNativeScreen]; - } - break; - case 1: { - [self requestInterstitial]; - } - break; - case 2: { - [self presentBannerScreen]; - } - break; - default: - break; - } +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + switch (indexPath.section) { + case 0: { + [self presentContentNativeScreen]; + } break; + case 1: { + [self requestInterstitial]; + } break; + case 2: { + [self presentBannerScreen]; + } break; + default: + break; + } } --(void)requestInterstitial { - - [MBProgressHUD showHUDAddedTo:self.view animated:YES]; - - self.interstitialDemo = [[InterstitialDemo alloc] initWithRootViewController:self]; - [self.interstitialDemo requestInterstitial]; +- (void)requestInterstitial { + + [MBProgressHUD showHUDAddedTo:self.view animated:YES]; + + self.interstitialDemo = + [[InterstitialDemo alloc] initWithRootViewController:self]; + [self.interstitialDemo requestInterstitial]; } --(void)presentContentNativeScreen { - NativeContentAdDemoViewController* viewController = [[NativeContentAdDemoViewController alloc] initWithNibName:@"NativeContentAdDemoViewController" bundle:nil]; - [self presentViewController:viewController animated:YES completion:nil]; +- (void)presentContentNativeScreen { + NativeContentAdDemoViewController *viewController = + [[NativeContentAdDemoViewController alloc] + initWithNibName:@"NativeContentAdDemoViewController" + bundle:nil]; + [self presentViewController:viewController animated:YES completion:nil]; } --(void)presentBannerScreen { - BannerDemoViewController* viewController = [[BannerDemoViewController alloc] initWithNibName:@"BannerDemoViewController" bundle:nil]; - [self presentViewController:viewController animated:YES completion:nil]; +- (void)presentBannerScreen { + BannerDemoViewController *viewController = [[BannerDemoViewController alloc] + initWithNibName:@"BannerDemoViewController" + bundle:nil]; + [self presentViewController:viewController animated:YES completion:nil]; } --(void)interstitialError{ - [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; +- (void)interstitialError { + [MBProgressHUD hideAllHUDsForView:self.view animated:YES]; } @end diff --git a/Examples/Advertising/Obj-C/TargetedAdsDemo/main.m b/Examples/Advertising/Obj-C/TargetedAdsDemo/main.m index 5f217fe0..9321176e 100644 --- a/Examples/Advertising/Obj-C/TargetedAdsDemo/main.m +++ b/Examples/Advertising/Obj-C/TargetedAdsDemo/main.m @@ -5,11 +5,12 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import #import "AppDelegate.h" +#import -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, + NSStringFromClass([AppDelegate class])); + } } diff --git a/Examples/BVSDKDemo/BVProductReviewContentExtension/BVProductReviewContentExtension-Bridging-Header.h b/Examples/BVSDKDemo/BVProductReviewContentExtension/BVProductReviewContentExtension-Bridging-Header.h index 63d594a5..4fedf32b 100644 --- a/Examples/BVSDKDemo/BVProductReviewContentExtension/BVProductReviewContentExtension-Bridging-Header.h +++ b/Examples/BVSDKDemo/BVProductReviewContentExtension/BVProductReviewContentExtension-Bridging-Header.h @@ -1,5 +1,6 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // #import "BVNotifications.h" diff --git a/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.h b/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.h index 9c001d8a..51e6f9a4 100644 --- a/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.h +++ b/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.h @@ -5,8 +5,8 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVStoreReviewNotificationViewController.h" +#import @interface NotificationViewController : BVStoreReviewNotificationViewController diff --git a/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.m b/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.m index 50e2c4a6..9a089e12 100644 --- a/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.m +++ b/Examples/BVSDKDemo/BVReviewContentExtension/NotificationViewController.m @@ -6,9 +6,9 @@ // #import "NotificationViewController.h" +#import #import #import -#import @interface NotificationViewController () @@ -16,9 +16,8 @@ @interface NotificationViewController () @implementation NotificationViewController --(void)viewDidLoad { - [super viewDidLoad]; - +- (void)viewDidLoad { + [super viewDidLoad]; } @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/BVSDKDemo-Bridging-Header.h b/Examples/BVSDKDemo/BVSDKDemo/BVSDKDemo-Bridging-Header.h index 2ec7aee0..17bec93e 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/BVSDKDemo-Bridging-Header.h +++ b/Examples/BVSDKDemo/BVSDKDemo/BVSDKDemo-Bridging-Header.h @@ -1,25 +1,27 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // - -#import -#import "LocationPickerView.h" #import "JPSThumbnail.h" +#import "LocationPickerView.h" #import "MPTextView.h" +#import #if __has_include("BVUserAuthStringGenerator.h") - // For internal testing only - #import "BVUserAuthStringGenerator.h" - #define SITE_AUTH 1 +// For internal testing only +#import "BVUserAuthStringGenerator.h" +#define SITE_AUTH 1 #else - #define SITE_AUTH 0 - // dummy declaration - // For a live Site Authentication, you migth have your own server-side call that provides a user info dictionary - // and returns a valid BV User Authentication String (UAS) - @interface BVUserAuthStringGenerator : NSObject - + (void)generateUAS:(NSDictionary *)userInfo withCompletion:(void(^)(NSString *uas, NSError *error))completionHandler; - @end +#define SITE_AUTH 0 +// dummy declaration +// For a live Site Authentication, you migth have your own server-side call that +// provides a user info dictionary and returns a valid BV User Authentication +// String (UAS) +@interface BVUserAuthStringGenerator : NSObject ++ (void)generateUAS:(NSDictionary *)userInfo + withCompletion:(void (^)(NSString *uas, NSError *error))completionHandler; +@end #endif diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h index 7c248558..e0362991 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnail.h @@ -10,19 +10,18 @@ #import "JPSThumbnailAnnotation.h" - @import MapKit; typedef void (^ActionBlock)(); @interface JPSThumbnail : NSObject -@property (nonatomic, strong) UIImage *image; -@property (nonatomic, strong) NSURL *imageURL; -@property (nonatomic, copy) NSString *title; -@property (nonatomic, assign) NSUInteger thumbId; -@property (nonatomic, copy) NSString *subtitle; -@property (nonatomic, assign) CLLocationCoordinate2D coordinate; -@property (nonatomic, copy) ActionBlock disclosureBlock; +@property(nonatomic, strong) UIImage *image; +@property(nonatomic, strong) NSURL *imageURL; +@property(nonatomic, copy) NSString *title; +@property(nonatomic, assign) NSUInteger thumbId; +@property(nonatomic, copy) NSString *subtitle; +@property(nonatomic, assign) CLLocationCoordinate2D coordinate; +@property(nonatomic, copy) ActionBlock disclosureBlock; @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.h b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.h index cee034f9..d96d82a5 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.h +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.h @@ -17,9 +17,10 @@ @end -@interface JPSThumbnailAnnotation : NSObject +@interface JPSThumbnailAnnotation + : NSObject -@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property(nonatomic, readonly) CLLocationCoordinate2D coordinate; + (instancetype)annotationWithThumbnail:(JPSThumbnail *)thumbnail; - (id)initWithThumbnail:(JPSThumbnail *)thumbnail; diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m index cc18d2cc..14cf4fc5 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotation.m @@ -10,47 +10,54 @@ @interface JPSThumbnailAnnotation () -@property (nonatomic, readwrite) JPSThumbnailAnnotationView *view; -@property (nonatomic, readonly) JPSThumbnail *thumbnail; +@property(nonatomic, readwrite) JPSThumbnailAnnotationView *view; +@property(nonatomic, readonly) JPSThumbnail *thumbnail; @end @implementation JPSThumbnailAnnotation + (instancetype)annotationWithThumbnail:(JPSThumbnail *)thumbnail { - return [[self alloc] initWithThumbnail:thumbnail]; + return [[self alloc] initWithThumbnail:thumbnail]; } - (id)initWithThumbnail:(JPSThumbnail *)thumbnail { - self = [super init]; - if (self) { - _coordinate = thumbnail.coordinate; - _thumbnail = thumbnail; - } - return self; + self = [super init]; + if (self) { + _coordinate = thumbnail.coordinate; + _thumbnail = thumbnail; + } + return self; } - (MKAnnotationView *)annotationViewInMap:(MKMapView *)mapView { - if (!self.view) { - self.view = (JPSThumbnailAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:kJPSThumbnailAnnotationViewReuseID]; - if (!self.view) self.view = [[JPSThumbnailAnnotationView alloc] initWithAnnotation:self]; - } else { - self.view.annotation = self; - } - [self updateThumbnail:self.thumbnail animated:NO]; - return self.view; + if (!self.view) { + self.view = (JPSThumbnailAnnotationView *)[mapView + dequeueReusableAnnotationViewWithIdentifier: + kJPSThumbnailAnnotationViewReuseID]; + if (!self.view) + self.view = [[JPSThumbnailAnnotationView alloc] initWithAnnotation:self]; + } else { + self.view.annotation = self; + } + [self updateThumbnail:self.thumbnail animated:NO]; + return self.view; } - (void)updateThumbnail:(JPSThumbnail *)thumbnail animated:(BOOL)animated { - if (animated) { - [UIView animateWithDuration:0.33f animations:^{ - _coordinate = thumbnail.coordinate; // use ivar to avoid triggering setter - }]; - } else { - _coordinate = thumbnail.coordinate; // use ivar to avoid triggering setter - } - - [self.view updateWithThumbnail:thumbnail]; + if (animated) { + [UIView + animateWithDuration:0.33f + animations:^{ + _coordinate = + thumbnail + .coordinate; // use ivar to avoid triggering setter + }]; + } else { + _coordinate = thumbnail.coordinate; // use ivar to avoid triggering setter + } + + [self.view updateWithThumbnail:thumbnail]; } @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.h b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.h index f577c94e..66962385 100644 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.h +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.h @@ -10,17 +10,17 @@ @class JPSThumbnail; -extern NSString * const kJPSThumbnailAnnotationViewReuseID; +extern NSString *const kJPSThumbnailAnnotationViewReuseID; typedef NS_ENUM(NSInteger, JPSThumbnailAnnotationViewAnimationDirection) { - JPSThumbnailAnnotationViewAnimationDirectionGrow, - JPSThumbnailAnnotationViewAnimationDirectionShrink, + JPSThumbnailAnnotationViewAnimationDirectionGrow, + JPSThumbnailAnnotationViewAnimationDirectionShrink, }; typedef NS_ENUM(NSInteger, JPSThumbnailAnnotationViewState) { - JPSThumbnailAnnotationViewStateCollapsed, - JPSThumbnailAnnotationViewStateExpanded, - JPSThumbnailAnnotationViewStateAnimating, + JPSThumbnailAnnotationViewStateCollapsed, + JPSThumbnailAnnotationViewStateExpanded, + JPSThumbnailAnnotationViewStateAnimating, }; @protocol JPSThumbnailAnnotationViewProtocol @@ -30,7 +30,8 @@ typedef NS_ENUM(NSInteger, JPSThumbnailAnnotationViewState) { @end -@interface JPSThumbnailAnnotationView : MKAnnotationView +@interface JPSThumbnailAnnotationView + : MKAnnotationView - (id)initWithAnnotation:(id)annotation; diff --git a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.m b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.m old mode 100755 new mode 100644 index d46a5e07..4aa30eb0 --- a/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.m +++ b/Examples/BVSDKDemo/BVSDKDemo/Curations/JPSThumbnailAnnotationView.m @@ -11,25 +11,26 @@ #import "JPSThumbnail.h" #import -NSString * const kJPSThumbnailAnnotationViewReuseID = @"JPSThumbnailAnnotationView"; +NSString *const kJPSThumbnailAnnotationViewReuseID = + @"JPSThumbnailAnnotationView"; -static CGFloat const kJPSThumbnailAnnotationViewStandardWidth = 75.0f; -static CGFloat const kJPSThumbnailAnnotationViewStandardHeight = 87.0f; -static CGFloat const kJPSThumbnailAnnotationViewExpandOffset = 200.0f; -static CGFloat const kJPSThumbnailAnnotationViewVerticalOffset = 34.0f; +static CGFloat const kJPSThumbnailAnnotationViewStandardWidth = 75.0f; +static CGFloat const kJPSThumbnailAnnotationViewStandardHeight = 87.0f; +static CGFloat const kJPSThumbnailAnnotationViewExpandOffset = 200.0f; +static CGFloat const kJPSThumbnailAnnotationViewVerticalOffset = 34.0f; static CGFloat const kJPSThumbnailAnnotationViewAnimationDuration = 0.25f; @interface JPSThumbnailAnnotationView () -@property (nonatomic, readwrite) CLLocationCoordinate2D coordinate; -@property (nonatomic, strong) UIImageView *imageView; -@property (nonatomic, strong) UILabel *titleLabel; -@property (nonatomic, strong) UILabel *subtitleLabel; -@property (nonatomic, strong) ActionBlock disclosureBlock; +@property(nonatomic, readwrite) CLLocationCoordinate2D coordinate; +@property(nonatomic, strong) UIImageView *imageView; +@property(nonatomic, strong) UILabel *titleLabel; +@property(nonatomic, strong) UILabel *subtitleLabel; +@property(nonatomic, strong) ActionBlock disclosureBlock; -@property (nonatomic, strong) CAShapeLayer *bgLayer; -@property (nonatomic, strong) UIButton *disclosureButton; -@property (nonatomic, assign) JPSThumbnailAnnotationViewState state; +@property(nonatomic, strong) CAShapeLayer *bgLayer; +@property(nonatomic, strong) UIButton *disclosureButton; +@property(nonatomic, assign) JPSThumbnailAnnotationViewState state; @end @@ -38,266 +39,321 @@ @implementation JPSThumbnailAnnotationView #pragma mark - Setup - (id)initWithAnnotation:(id)annotation { - self = [super initWithAnnotation:annotation reuseIdentifier:kJPSThumbnailAnnotationViewReuseID]; - - if (self) { - self.canShowCallout = NO; - self.frame = CGRectMake(0, 0, kJPSThumbnailAnnotationViewStandardWidth, kJPSThumbnailAnnotationViewStandardHeight); - self.backgroundColor = [UIColor clearColor]; - self.centerOffset = CGPointMake(0, -kJPSThumbnailAnnotationViewVerticalOffset); - - _state = JPSThumbnailAnnotationViewStateCollapsed; - - [self setupView]; - } - - return self; + self = [super initWithAnnotation:annotation + reuseIdentifier:kJPSThumbnailAnnotationViewReuseID]; + + if (self) { + self.canShowCallout = NO; + self.frame = CGRectMake(0, 0, kJPSThumbnailAnnotationViewStandardWidth, + kJPSThumbnailAnnotationViewStandardHeight); + self.backgroundColor = [UIColor clearColor]; + self.centerOffset = + CGPointMake(0, -kJPSThumbnailAnnotationViewVerticalOffset); + + _state = JPSThumbnailAnnotationViewStateCollapsed; + + [self setupView]; + } + + return self; } - (void)setupView { - [self setupImageView]; - [self setupTitleLabel]; - [self setupSubtitleLabel]; - [self setupDisclosureButton]; - [self setLayerProperties]; - [self setDetailGroupAlpha:0.0f]; + [self setupImageView]; + [self setupTitleLabel]; + [self setupSubtitleLabel]; + [self setupDisclosureButton]; + [self setLayerProperties]; + [self setDetailGroupAlpha:0.0f]; } - (void)setupImageView { - _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(12.5f, 12.5f, 50.0f, 47.0f)]; - _imageView.layer.cornerRadius = 4.0f; - _imageView.layer.masksToBounds = YES; - _imageView.layer.borderColor = [[UIColor lightGrayColor] CGColor]; - _imageView.layer.borderWidth = 0.5f; - [self addSubview:_imageView]; + _imageView = [[UIImageView alloc] + initWithFrame:CGRectMake(12.5f, 12.5f, 50.0f, 47.0f)]; + _imageView.layer.cornerRadius = 4.0f; + _imageView.layer.masksToBounds = YES; + _imageView.layer.borderColor = [[UIColor lightGrayColor] CGColor]; + _imageView.layer.borderWidth = 0.5f; + [self addSubview:_imageView]; } - (void)setupTitleLabel { - _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(-32.0f, 16.0f, 168.0f, 20.0f)]; - _titleLabel.textColor = [UIColor darkTextColor]; - _titleLabel.font = [UIFont boldSystemFontOfSize:17]; - _titleLabel.minimumScaleFactor = 0.8f; - _titleLabel.adjustsFontSizeToFitWidth = YES; - - UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapDisclosureButton)]; - [_titleLabel setUserInteractionEnabled:YES]; - [_titleLabel addGestureRecognizer:tap]; - - [self addSubview:_titleLabel]; + _titleLabel = + [[UILabel alloc] initWithFrame:CGRectMake(-32.0f, 16.0f, 168.0f, 20.0f)]; + _titleLabel.textColor = [UIColor darkTextColor]; + _titleLabel.font = [UIFont boldSystemFontOfSize:17]; + _titleLabel.minimumScaleFactor = 0.8f; + _titleLabel.adjustsFontSizeToFitWidth = YES; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(didTapDisclosureButton)]; + [_titleLabel setUserInteractionEnabled:YES]; + [_titleLabel addGestureRecognizer:tap]; + + [self addSubview:_titleLabel]; } - (void)setupSubtitleLabel { - _subtitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(-32.0f, 36.0f, 168.0f, 20.0f)]; - _subtitleLabel.textColor = [UIColor grayColor]; - _subtitleLabel.font = [UIFont systemFontOfSize:12.0f]; - - UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapDisclosureButton)]; - [_subtitleLabel setUserInteractionEnabled:YES]; - [_subtitleLabel addGestureRecognizer:tap]; - - [self addSubview:_subtitleLabel]; + _subtitleLabel = + [[UILabel alloc] initWithFrame:CGRectMake(-32.0f, 36.0f, 168.0f, 20.0f)]; + _subtitleLabel.textColor = [UIColor grayColor]; + _subtitleLabel.font = [UIFont systemFontOfSize:12.0f]; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(didTapDisclosureButton)]; + [_subtitleLabel setUserInteractionEnabled:YES]; + [_subtitleLabel addGestureRecognizer:tap]; + + [self addSubview:_subtitleLabel]; } - (void)setupDisclosureButton { - BOOL iOS7 = [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f; - UIButtonType buttonType = iOS7 ? UIButtonTypeSystem : UIButtonTypeCustom; - _disclosureButton = [UIButton buttonWithType:buttonType]; - _disclosureButton.tintColor = [UIColor grayColor]; - UIImage *disclosureIndicatorImage = [JPSThumbnailAnnotationView disclosureButtonImage]; - [_disclosureButton setImage:disclosureIndicatorImage forState:UIControlStateNormal]; - _disclosureButton.frame = CGRectMake(kJPSThumbnailAnnotationViewExpandOffset/2.0f + self.frame.size.width/2.0f + 8.0f, - 26.5f, - disclosureIndicatorImage.size.width, - disclosureIndicatorImage.size.height); - - [_disclosureButton addTarget:self action:@selector(didTapDisclosureButton) forControlEvents:UIControlEventTouchDown]; - [self addSubview:_disclosureButton]; + BOOL iOS7 = [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f; + UIButtonType buttonType = iOS7 ? UIButtonTypeSystem : UIButtonTypeCustom; + _disclosureButton = [UIButton buttonWithType:buttonType]; + _disclosureButton.tintColor = [UIColor grayColor]; + UIImage *disclosureIndicatorImage = + [JPSThumbnailAnnotationView disclosureButtonImage]; + [_disclosureButton setImage:disclosureIndicatorImage + forState:UIControlStateNormal]; + _disclosureButton.frame = + CGRectMake(kJPSThumbnailAnnotationViewExpandOffset / 2.0f + + self.frame.size.width / 2.0f + 8.0f, + 26.5f, disclosureIndicatorImage.size.width, + disclosureIndicatorImage.size.height); + + [_disclosureButton addTarget:self + action:@selector(didTapDisclosureButton) + forControlEvents:UIControlEventTouchDown]; + [self addSubview:_disclosureButton]; } - (void)setLayerProperties { - _bgLayer = [CAShapeLayer layer]; - CGPathRef path = [self newBubbleWithRect:self.bounds]; - _bgLayer.path = path; - CFRelease(path); - _bgLayer.fillColor = [UIColor whiteColor].CGColor; - - _bgLayer.shadowColor = [UIColor blackColor].CGColor; - _bgLayer.shadowOffset = CGSizeMake(0.0f, 3.0f); - _bgLayer.shadowRadius = 2.0f; - _bgLayer.shadowOpacity = 0.5f; - - _bgLayer.masksToBounds = NO; - - [self.layer insertSublayer:_bgLayer atIndex:0]; + _bgLayer = [CAShapeLayer layer]; + CGPathRef path = [self newBubbleWithRect:self.bounds]; + _bgLayer.path = path; + CFRelease(path); + _bgLayer.fillColor = [UIColor whiteColor].CGColor; + + _bgLayer.shadowColor = [UIColor blackColor].CGColor; + _bgLayer.shadowOffset = CGSizeMake(0.0f, 3.0f); + _bgLayer.shadowRadius = 2.0f; + _bgLayer.shadowOpacity = 0.5f; + + _bgLayer.masksToBounds = NO; + + [self.layer insertSublayer:_bgLayer atIndex:0]; } #pragma mark - Updating - (void)updateWithThumbnail:(JPSThumbnail *)thumbnail { - self.coordinate = thumbnail.coordinate; - self.titleLabel.text = thumbnail.title; - self.subtitleLabel.text = thumbnail.subtitle; - [self assignImageWithThumbnail:thumbnail]; - self.disclosureBlock = thumbnail.disclosureBlock; + self.coordinate = thumbnail.coordinate; + self.titleLabel.text = thumbnail.title; + self.subtitleLabel.text = thumbnail.subtitle; + [self assignImageWithThumbnail:thumbnail]; + self.disclosureBlock = thumbnail.disclosureBlock; } --(void)assignImageWithThumbnail:(JPSThumbnail *)thumbnail { - - //figure out if we Should directly set image or use URL to download it instead - if (thumbnail.image) { - self.imageView.image = thumbnail.image; - - } else if(thumbnail.imageURL){ - - [self.imageView sd_setImageWithURL:thumbnail.imageURL]; - } - +- (void)assignImageWithThumbnail:(JPSThumbnail *)thumbnail { + + // figure out if we Should directly set image or use URL to download it + // instead + if (thumbnail.image) { + self.imageView.image = thumbnail.image; + + } else if (thumbnail.imageURL) { + + [self.imageView sd_setImageWithURL:thumbnail.imageURL]; + } } #pragma mark - JPSThumbnailAnnotationViewProtocol - (void)didSelectAnnotationViewInMap:(MKMapView *)mapView { - // Center map at annotation point - [mapView setCenterCoordinate:self.coordinate animated:YES]; - [self expand]; + // Center map at annotation point + [mapView setCenterCoordinate:self.coordinate animated:YES]; + [self expand]; } - (void)didDeselectAnnotationViewInMap:(MKMapView *)mapView { - [self shrink]; + [self shrink]; } #pragma mark - Geometry - (CGPathRef)newBubbleWithRect:(CGRect)rect { - CGFloat stroke = 1.0f; - CGFloat radius = 7.0f; - CGMutablePathRef path = CGPathCreateMutable(); - CGFloat parentX = rect.origin.x + rect.size.width/2.0f; - - // Determine Size - rect.size.width -= stroke + 14.0f; - rect.size.height -= stroke + 29.0f; - rect.origin.x += stroke / 2.0f + 7.0f; - rect.origin.y += stroke / 2.0f + 7.0f; - - // Create Callout Bubble Path - CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y + radius); - CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height - radius); - CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI_2, 1); - CGPathAddLineToPoint(path, NULL, parentX - 14.0f, rect.origin.y + rect.size.height); - CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y + rect.size.height + 14.0f); - CGPathAddLineToPoint(path, NULL, parentX + 14.0f, rect.origin.y + rect.size.height); - CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height); - CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI_2, 0.0f, 1.0f); - CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + radius); - CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, 0.0f, -M_PI_2, 1.0f); - CGPathAddLineToPoint(path, NULL, rect.origin.x + radius, rect.origin.y); - CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + radius, radius, -M_PI_2, M_PI, 1.0f); - CGPathCloseSubpath(path); - return path; + CGFloat stroke = 1.0f; + CGFloat radius = 7.0f; + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat parentX = rect.origin.x + rect.size.width / 2.0f; + + // Determine Size + rect.size.width -= stroke + 14.0f; + rect.size.height -= stroke + 29.0f; + rect.origin.x += stroke / 2.0f + 7.0f; + rect.origin.y += stroke / 2.0f + 7.0f; + + // Create Callout Bubble Path + CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y + radius); + CGPathAddLineToPoint(path, NULL, rect.origin.x, + rect.origin.y + rect.size.height - radius); + CGPathAddArc(path, NULL, rect.origin.x + radius, + rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI_2, + 1); + CGPathAddLineToPoint(path, NULL, parentX - 14.0f, + rect.origin.y + rect.size.height); + CGPathAddLineToPoint(path, NULL, parentX, + rect.origin.y + rect.size.height + 14.0f); + CGPathAddLineToPoint(path, NULL, parentX + 14.0f, + rect.origin.y + rect.size.height); + CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width - radius, + rect.origin.y + rect.size.height); + CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, + rect.origin.y + rect.size.height - radius, radius, M_PI_2, 0.0f, + 1.0f); + CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, + rect.origin.y + radius); + CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, + rect.origin.y + radius, radius, 0.0f, -M_PI_2, 1.0f); + CGPathAddLineToPoint(path, NULL, rect.origin.x + radius, rect.origin.y); + CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + radius, + radius, -M_PI_2, M_PI, 1.0f); + CGPathCloseSubpath(path); + return path; } #pragma mark - Animations - (void)setDetailGroupAlpha:(CGFloat)alpha { - self.disclosureButton.alpha = alpha; - self.titleLabel.alpha = alpha; - self.subtitleLabel.alpha = alpha; + self.disclosureButton.alpha = alpha; + self.titleLabel.alpha = alpha; + self.subtitleLabel.alpha = alpha; } - (void)expand { - if (self.state != JPSThumbnailAnnotationViewStateCollapsed) return; - - self.state = JPSThumbnailAnnotationViewStateAnimating; - - [self animateBubbleWithDirection:JPSThumbnailAnnotationViewAnimationDirectionGrow]; - self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width+kJPSThumbnailAnnotationViewExpandOffset, self.frame.size.height); - self.centerOffset = CGPointMake(kJPSThumbnailAnnotationViewExpandOffset/2.0f, -kJPSThumbnailAnnotationViewVerticalOffset); - [UIView animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration/2.0f delay:kJPSThumbnailAnnotationViewAnimationDuration options:UIViewAnimationOptionCurveEaseInOut animations:^{ + if (self.state != JPSThumbnailAnnotationViewStateCollapsed) + return; + + self.state = JPSThumbnailAnnotationViewStateAnimating; + + [self animateBubbleWithDirection: + JPSThumbnailAnnotationViewAnimationDirectionGrow]; + self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, + self.frame.size.width + + kJPSThumbnailAnnotationViewExpandOffset, + self.frame.size.height); + self.centerOffset = + CGPointMake(kJPSThumbnailAnnotationViewExpandOffset / 2.0f, + -kJPSThumbnailAnnotationViewVerticalOffset); + [UIView + animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration / 2.0f + delay:kJPSThumbnailAnnotationViewAnimationDuration + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ [self setDetailGroupAlpha:1.0f]; - } completion:^(BOOL finished) { + } + completion:^(BOOL finished) { self.state = JPSThumbnailAnnotationViewStateExpanded; - }]; + }]; } - (void)shrink { - if (self.state != JPSThumbnailAnnotationViewStateExpanded) return; - - self.state = JPSThumbnailAnnotationViewStateAnimating; - - self.frame = CGRectMake(self.frame.origin.x, - self.frame.origin.y, - self.frame.size.width - kJPSThumbnailAnnotationViewExpandOffset, - self.frame.size.height); - - [UIView animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration/2.0f - delay:0.0f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self setDetailGroupAlpha:0.0f]; - } - completion:^(BOOL finished) { - [self animateBubbleWithDirection:JPSThumbnailAnnotationViewAnimationDirectionShrink]; - self.centerOffset = CGPointMake(0.0f, -kJPSThumbnailAnnotationViewVerticalOffset); - }]; + if (self.state != JPSThumbnailAnnotationViewStateExpanded) + return; + + self.state = JPSThumbnailAnnotationViewStateAnimating; + + self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, + self.frame.size.width - + kJPSThumbnailAnnotationViewExpandOffset, + self.frame.size.height); + + [UIView + animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration / 2.0f + delay:0.0f + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self setDetailGroupAlpha:0.0f]; + } + completion:^(BOOL finished) { + [self animateBubbleWithDirection: + JPSThumbnailAnnotationViewAnimationDirectionShrink]; + self.centerOffset = + CGPointMake(0.0f, -kJPSThumbnailAnnotationViewVerticalOffset); + }]; } -- (void)animateBubbleWithDirection:(JPSThumbnailAnnotationViewAnimationDirection)animationDirection { - BOOL growing = (animationDirection == JPSThumbnailAnnotationViewAnimationDirectionGrow); - // Image - [UIView animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration animations:^{ - CGFloat xOffset = (growing ? -1 : 1) * kJPSThumbnailAnnotationViewExpandOffset/2.0f; - self.imageView.frame = CGRectOffset(self.imageView.frame, xOffset, 0.0f); - } completion:^(BOOL finished) { - if (animationDirection == JPSThumbnailAnnotationViewAnimationDirectionShrink) { - self.state = JPSThumbnailAnnotationViewStateCollapsed; +- (void)animateBubbleWithDirection: + (JPSThumbnailAnnotationViewAnimationDirection)animationDirection { + BOOL growing = + (animationDirection == JPSThumbnailAnnotationViewAnimationDirectionGrow); + // Image + [UIView animateWithDuration:kJPSThumbnailAnnotationViewAnimationDuration + animations:^{ + CGFloat xOffset = + (growing ? -1 : 1) * kJPSThumbnailAnnotationViewExpandOffset / 2.0f; + self.imageView.frame = + CGRectOffset(self.imageView.frame, xOffset, 0.0f); + } + completion:^(BOOL finished) { + if (animationDirection == + JPSThumbnailAnnotationViewAnimationDirectionShrink) { + self.state = JPSThumbnailAnnotationViewStateCollapsed; } - }]; - - // Bubble - CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; - - animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; - animation.repeatCount = 1; - animation.removedOnCompletion = NO; - animation.fillMode = kCAFillModeForwards; - animation.duration = kJPSThumbnailAnnotationViewAnimationDuration; - - // Stroke & Shadow From/To Values - CGRect largeRect = CGRectInset(self.bounds, -kJPSThumbnailAnnotationViewExpandOffset/2.0f, 0.0f); - - CGPathRef fromPath = [self newBubbleWithRect:growing ? self.bounds : largeRect]; - animation.fromValue = (__bridge id)fromPath; - CGPathRelease(fromPath); - - CGPathRef toPath = [self newBubbleWithRect:growing ? largeRect : self.bounds]; - animation.toValue = (__bridge id)toPath; - CGPathRelease(toPath); - - [self.bgLayer addAnimation:animation forKey:animation.keyPath]; + }]; + + // Bubble + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; + + animation.timingFunction = [CAMediaTimingFunction + functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.repeatCount = 1; + animation.removedOnCompletion = NO; + animation.fillMode = kCAFillModeForwards; + animation.duration = kJPSThumbnailAnnotationViewAnimationDuration; + + // Stroke & Shadow From/To Values + CGRect largeRect = CGRectInset( + self.bounds, -kJPSThumbnailAnnotationViewExpandOffset / 2.0f, 0.0f); + + CGPathRef fromPath = + [self newBubbleWithRect:growing ? self.bounds : largeRect]; + animation.fromValue = (__bridge id)fromPath; + CGPathRelease(fromPath); + + CGPathRef toPath = [self newBubbleWithRect:growing ? largeRect : self.bounds]; + animation.toValue = (__bridge id)toPath; + CGPathRelease(toPath); + + [self.bgLayer addAnimation:animation forKey:animation.keyPath]; } #pragma mark - Disclosure Button - (void)didTapDisclosureButton { - if (self.disclosureBlock) self.disclosureBlock(); + if (self.disclosureBlock) + self.disclosureBlock(); } + (UIImage *)disclosureButtonImage { - CGSize size = CGSizeMake(21.0f, 36.0f); - UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]); - - UIBezierPath *bezierPath = [UIBezierPath bezierPath]; - [bezierPath moveToPoint:CGPointMake(2.0f, 2.0f)]; - [bezierPath addLineToPoint:CGPointMake(10.0f, 10.0f)]; - [bezierPath addLineToPoint:CGPointMake(2.0f, 18.0f)]; - [[UIColor lightGrayColor] setStroke]; - bezierPath.lineWidth = 3.0f; - [bezierPath stroke]; - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return image; + CGSize size = CGSizeMake(21.0f, 36.0f); + UIGraphicsBeginImageContextWithOptions(size, NO, + [[UIScreen mainScreen] scale]); + + UIBezierPath *bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:CGPointMake(2.0f, 2.0f)]; + [bezierPath addLineToPoint:CGPointMake(10.0f, 10.0f)]; + [bezierPath addLineToPoint:CGPointMake(2.0f, 18.0f)]; + [[UIColor lightGrayColor] setStroke]; + bezierPath.lineWidth = 3.0f; + [bezierPath stroke]; + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; } @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/MPTextView.h b/Examples/BVSDKDemo/BVSDKDemo/MPTextView.h old mode 100755 new mode 100644 index f33c2f98..fa335605 --- a/Examples/BVSDKDemo/BVSDKDemo/MPTextView.h +++ b/Examples/BVSDKDemo/BVSDKDemo/MPTextView.h @@ -11,6 +11,6 @@ // Named .placeholderText, in case UITextView gains // a .placeholder property (like UITextField) in future iOS versions. -@property (nonatomic, copy) NSString *placeholderText; +@property(nonatomic, copy) NSString *placeholderText; @end diff --git a/Examples/BVSDKDemo/BVSDKDemo/MPTextView.m b/Examples/BVSDKDemo/BVSDKDemo/MPTextView.m old mode 100755 new mode 100644 index d44388fb..b2fc61d8 --- a/Examples/BVSDKDemo/BVSDKDemo/MPTextView.m +++ b/Examples/BVSDKDemo/BVSDKDemo/MPTextView.m @@ -18,39 +18,39 @@ @interface MPTextView () -@property (nonatomic, strong) UILabel *placeholderLabel; +@property(nonatomic, strong) UILabel *placeholderLabel; -// The top offset differs when the view is instantiated from IB or programmatically. -// Use this to track the instantiation route and offset the label accordingly. -@property (nonatomic, assign) CGFloat topLabelOffset; +// The top offset differs when the view is instantiated from IB or +// programmatically. Use this to track the instantiation route and offset the +// label accordingly. +@property(nonatomic, assign) CGFloat topLabelOffset; // Handle text changed event so we can update the placeholder appropriately - (void)textChanged:(NSNotification *)note; @end - @implementation MPTextView #pragma mark - Initializers - (id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self) { - // Account for IB offset: - _topLabelOffset = kLabelTopOffsetFromIB; - [self finishInitialization]; - } - return self; + self = [super initWithCoder:aDecoder]; + if (self) { + // Account for IB offset: + _topLabelOffset = kLabelTopOffsetFromIB; + [self finishInitialization]; + } + return self; } - (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - _topLabelOffset = kLabelTopOffset; - [self finishInitialization]; - } - return self; + self = [super initWithFrame:frame]; + if (self) { + _topLabelOffset = kLabelTopOffset; + [self finishInitialization]; + } + return self; } // Private method for finishing initialization. @@ -58,137 +58,134 @@ - (id)initWithFrame:(CGRect)frame { // I don't feel comfortable changing the initializer chain. // Let's do it this way rather than overriding UIView's designated initializer. - (void)finishInitialization { - // Sign up for notifications for text changes: - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(textChanged:) - name:UITextViewTextDidChangeNotification - object:self]; - - CGFloat labelLeftOffset = kLabelLeftOffset; - // Use our calculated label offset from initWith…: - CGFloat labelTopOffset = self.topLabelOffset; - - // On retina iPhones and iPads, the label is offset by 0.5 points. - if ([[UIScreen mainScreen] scale] == 2.0) { - labelTopOffset += kLabelTopOffsetRetina; - } - - CGSize labelOffset = CGSizeMake(labelLeftOffset, labelTopOffset); - CGRect labelFrame = [self placeholderLabelFrameWithOffset:labelOffset]; - [self createPlaceholderLabel:labelFrame]; + // Sign up for notifications for text changes: + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(textChanged:) + name:UITextViewTextDidChangeNotification + object:self]; + + CGFloat labelLeftOffset = kLabelLeftOffset; + // Use our calculated label offset from initWith…: + CGFloat labelTopOffset = self.topLabelOffset; + + // On retina iPhones and iPads, the label is offset by 0.5 points. + if ([[UIScreen mainScreen] scale] == 2.0) { + labelTopOffset += kLabelTopOffsetRetina; + } + + CGSize labelOffset = CGSizeMake(labelLeftOffset, labelTopOffset); + CGRect labelFrame = [self placeholderLabelFrameWithOffset:labelOffset]; + [self createPlaceholderLabel:labelFrame]; } - #pragma mark - Placeholder label helpers // Create our label: - (void)createPlaceholderLabel:(CGRect)labelFrame { - self.placeholderLabel = [[UILabel alloc] initWithFrame:labelFrame]; - self.placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping; - self.placeholderLabel.numberOfLines = 0; - self.placeholderLabel.font = self.font; - self.placeholderLabel.backgroundColor = [UIColor clearColor]; - self.placeholderLabel.text = self.placeholderText; - // Color-matched to UITextField's placeholder text color: - self.placeholderLabel.textColor = [UIColor colorWithWhite:0.71f alpha:1.0f]; - - // UIKit effects on the UITextView, like selection ranges - // and the cursor, are done in a view above the text view, - // so no need to order this below anything else. - // Add the label as a subview. - [self addSubview:self.placeholderLabel]; + self.placeholderLabel = [[UILabel alloc] initWithFrame:labelFrame]; + self.placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping; + self.placeholderLabel.numberOfLines = 0; + self.placeholderLabel.font = self.font; + self.placeholderLabel.backgroundColor = [UIColor clearColor]; + self.placeholderLabel.text = self.placeholderText; + // Color-matched to UITextField's placeholder text color: + self.placeholderLabel.textColor = [UIColor colorWithWhite:0.71f alpha:1.0f]; + + // UIKit effects on the UITextView, like selection ranges + // and the cursor, are done in a view above the text view, + // so no need to order this below anything else. + // Add the label as a subview. + [self addSubview:self.placeholderLabel]; } - (CGRect)placeholderLabelFrameWithOffset:(CGSize)labelOffset { - return CGRectMake(labelOffset.width, - labelOffset.height, - self.bounds.size.width - (2 * labelOffset.width), - self.bounds.size.height - (2 * labelOffset.height)); + return CGRectMake(labelOffset.width, labelOffset.height, + self.bounds.size.width - (2 * labelOffset.width), + self.bounds.size.height - (2 * labelOffset.height)); } - #pragma mark - Custom accessors - (void)setPlaceholderText:(NSString *)string { - _placeholderText = [string copy]; - self.placeholderLabel.text = string; - [self.placeholderLabel sizeToFit]; + _placeholderText = [string copy]; + self.placeholderLabel.text = string; + [self.placeholderLabel sizeToFit]; } - #pragma mark - UITextView subclass methods // Keep the placeholder label font in sync with the view's text font. - (void)setFont:(UIFont *)font { - // Call super. - [super setFont:font]; + // Call super. + [super setFont:font]; - self.placeholderLabel.font = self.font; + self.placeholderLabel.font = self.font; } // Keep placeholder label alignment in sync with the view's text alignment. - (void)setTextAlignment:(NSTextAlignment)textAlignment { - // Call super. - [super setTextAlignment:textAlignment]; + // Call super. + [super setTextAlignment:textAlignment]; - self.placeholderLabel.textAlignment = textAlignment; + self.placeholderLabel.textAlignment = textAlignment; } // Todo: override setAttributedText to capture changes // in text alignment? - #pragma mark - UITextInput overrides // Listen to dictation events to hide the placeholder as is appropriate. // Hide when there's a dictation result placeholder - (id)insertDictationResultPlaceholder { - // Call super. - id placeholder = [super insertDictationResultPlaceholder]; - - // Use -[setHidden] here instead of setAlpha: - // these events also trigger -[textChanged], - // which has a different criteria by which it shows the label, - // but we undeniably know we want this placeholder hidden. - self.placeholderLabel.hidden = YES; - return placeholder; + // Call super. + id placeholder = [super insertDictationResultPlaceholder]; + + // Use -[setHidden] here instead of setAlpha: + // these events also trigger -[textChanged], + // which has a different criteria by which it shows the label, + // but we undeniably know we want this placeholder hidden. + self.placeholderLabel.hidden = YES; + return placeholder; } // Update visibility when dictation ends. -- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult { - // Call super. - [super removeDictationResultPlaceholder:placeholder willInsertResult:willInsertResult]; +- (void)removeDictationResultPlaceholder:(id)placeholder + willInsertResult:(BOOL)willInsertResult { + // Call super. + [super removeDictationResultPlaceholder:placeholder + willInsertResult:willInsertResult]; - // Unset the hidden flag from insertDictationResultPlaceholder. - self.placeholderLabel.hidden = NO; + // Unset the hidden flag from insertDictationResultPlaceholder. + self.placeholderLabel.hidden = NO; - // Update our text label based on the entered text. - [self updatePlaceholderLabelVisibility]; + // Update our text label based on the entered text. + [self updatePlaceholderLabelVisibility]; } - #pragma mark - Text change listeners - (void)updatePlaceholderLabelVisibility { - if ([self.text length] == 0) { - self.placeholderLabel.alpha = 1.f; - } else { - self.placeholderLabel.alpha = 0.f; - } + if ([self.text length] == 0) { + self.placeholderLabel.alpha = 1.f; + } else { + self.placeholderLabel.alpha = 0.f; + } } // When text is set or changed, update the label's visibility. - (void)setText:(NSString *)text { - // Call super. - [super setText:text]; + // Call super. + [super setText:text]; - [self updatePlaceholderLabelVisibility]; + [self updatePlaceholderLabelVisibility]; } - (void)textChanged:(NSNotification *)notification { - [self updatePlaceholderLabelVisibility]; + [self updatePlaceholderLabelVisibility]; } @end diff --git a/Examples/BVSDKDemo/Curations Custom Post Extension/Curations Custom Post Extension-Bridging-Header.h b/Examples/BVSDKDemo/Curations Custom Post Extension/Curations Custom Post Extension-Bridging-Header.h index 1b2cb5d6..339994e9 100644 --- a/Examples/BVSDKDemo/Curations Custom Post Extension/Curations Custom Post Extension-Bridging-Header.h +++ b/Examples/BVSDKDemo/Curations Custom Post Extension/Curations Custom Post Extension-Bridging-Header.h @@ -1,4 +1,4 @@ // -// Use this file to import your target's public headers that you would like to expose to Swift. +// Use this file to import your target's public headers that you would like to +// expose to Swift. // - diff --git a/Examples/BVSDKDemo/LocationPickerView.h b/Examples/BVSDKDemo/LocationPickerView.h index ccd9b6d5..879b39b8 100644 --- a/Examples/BVSDKDemo/LocationPickerView.h +++ b/Examples/BVSDKDemo/LocationPickerView.h @@ -7,7 +7,6 @@ #import - @class MKMapView; @class LocationPickerView; @@ -16,82 +15,82 @@ typedef void (^LocationPickerViewBlock)(LocationPickerView *locationPicker); - @interface LocationPickerView : UIView /** How much of the screen the map takes up initially and the height it returns to after scrolling is done. By default this is set to "180.0f". */ -@property (nonatomic) CGFloat defaultMapHeight; +@property(nonatomic) CGFloat defaultMapHeight; /** How fast the map scrolls with the table view. If this is set to "1.0" it scrolls at the same speed. A value less than "1.0" produces a slower scrolling map while a value greater than "1.0" makes the map scroll faster. The default value is "0.5". */ -@property (nonatomic) CGFloat parallaxScrollFactor; +@property(nonatomic) CGFloat parallaxScrollFactor; /** Determines whether or not the user can pull to a certain point - on the table view to expand the map. This is disabled by default + on the table view to expand the map. This is disabled by default because it may interfere with pull-to-refresh or other controls. */ -@property (nonatomic) BOOL pullToExpandMapEnabled; +@property(nonatomic) BOOL pullToExpandMapEnabled; -/** The amount you must "pull down" on the scroll view to make the +/** The amount you must "pull down" on the scroll view to make the map view pop-out to full screen. By default this is set to "140.0f". */ -@property (nonatomic) CGFloat amountToScrollToFullScreenMap; +@property(nonatomic) CGFloat amountToScrollToFullScreenMap; /** If set to YES, this will automatically create an "X" button to shrink the map back down when it is shown. The button hides when the map returns to it's default size. This property defaults to NO. */ -@property (nonatomic) BOOL shouldCreateHideMapButton; +@property(nonatomic) BOOL shouldCreateHideMapButton; /** If the map is tracking the user location and this variable is set to 'YES', - the map will automatically center on the user's location when shrinking / expanding. - This defaults to NO. */ -@property (nonatomic) BOOL shouldAutoCenterOnUserLocation; + the map will automatically center on the user's location when shrinking / + expanding. This defaults to NO. */ +@property(nonatomic) BOOL shouldAutoCenterOnUserLocation; /** Is the map covering the full screen? */ -@property (nonatomic, readonly) BOOL isMapFullScreen; +@property(nonatomic, readonly) BOOL isMapFullScreen; /** The delegate gets notified when the map expands, shrinks, etc. */ -@property (nonatomic, weak) IBOutlet id delegate; +@property(nonatomic, weak) IBOutlet id delegate; /** The map view, duh. */ -@property (nonatomic, strong) MKMapView *mapView; +@property(nonatomic, strong) MKMapView *mapView; /** Table view that sits below the map. */ -@property (nonatomic, strong) UITableView *tableView; +@property(nonatomic, strong) UITableView *tableView; /** The view to the tableview background view. */ -@property (nonatomic, strong) UIView *backgroundView; +@property(nonatomic, strong) UIView *backgroundView; /** The color of the backgroundView */ -@property (nonatomic, strong) UIColor *backgroundViewColor; +@property(nonatomic, strong) UIColor *backgroundViewColor; /** This UITableViewDataSource is forwarded to the LocationPickers's UITableView when it is created. */ -@property (nonatomic, weak) IBOutlet id tableViewDataSource; +@property(nonatomic, weak) IBOutlet id + tableViewDataSource; /** This UITableViewDelegate is forwarded to the LocationPickers's UITableView when it is created. */ -@property (nonatomic, weak) IBOutlet id tableViewDelegate; +@property(nonatomic, weak) IBOutlet id tableViewDelegate; /** This MKMapViewDelegate is forwarded to the LocationPickers's MKMapView when it is created. */ -@property (nonatomic, weak) IBOutlet id mapViewDelegate; +@property(nonatomic, weak) IBOutlet id mapViewDelegate; /** Called after the tableView has been loaded. Allows for additional setup. */ -@property (nonatomic, copy) LocationPickerViewBlock tableViewDidLoadBlock; +@property(nonatomic, copy) LocationPickerViewBlock tableViewDidLoadBlock; /** Called after the mapView has been loaded. Allows for additional setup. */ -@property (nonatomic, copy) LocationPickerViewBlock mapViewDidLoadBlock; +@property(nonatomic, copy) LocationPickerViewBlock mapViewDidLoadBlock; -@property (nonatomic, copy) LocationPickerViewBlock mapViewWillExpand; -@property (nonatomic, copy) LocationPickerViewBlock mapViewDidExpand; -@property (nonatomic, copy) LocationPickerViewBlock mapViewWillBeHidden; -@property (nonatomic, copy) LocationPickerViewBlock mapViewWasHidden; +@property(nonatomic, copy) LocationPickerViewBlock mapViewWillExpand; +@property(nonatomic, copy) LocationPickerViewBlock mapViewDidExpand; +@property(nonatomic, copy) LocationPickerViewBlock mapViewWillBeHidden; +@property(nonatomic, copy) LocationPickerViewBlock mapViewWasHidden; /* custom action for close map view button, overrides default action */ -@property (nonatomic, copy) LocationPickerViewBlock mapCloseButtonTapped; +@property(nonatomic, copy) LocationPickerViewBlock mapCloseButtonTapped; /** Makes the map view full screen. */ - (void)expandMapView:(id)sender animated:(BOOL)animated; @@ -108,24 +107,24 @@ typedef void (^LocationPickerViewBlock)(LocationPickerView *locationPicker); - (void)setCustomCloseButton:(UIButton *)closeButton; /** Set custom close button map at x/y point */ -- (void)setCustomCloseButton:(UIButton *)closeButton atPoint:(CGPoint)buttonPoint; +- (void)setCustomCloseButton:(UIButton *)closeButton + atPoint:(CGPoint)buttonPoint; @end - @protocol LocationPickerViewDelegate @optional -/** Called when the mapView is loaded or reloaded. Alternatively, the block +/** Called when the mapView is loaded or reloaded. Alternatively, the block properties of LocationPickerView can be used. */ - (void)locationPicker:(LocationPickerView *)locationPicker - mapViewDidLoad:(MKMapView *)mapView; + mapViewDidLoad:(MKMapView *)mapView; /** Called when the tableView is loaded or reloaded. Alternatively, the block properties of LocationPickerView can be used. */ - (void)locationPicker:(LocationPickerView *)locationPicker - tableViewDidLoad:(UITableView *)tableView; + tableViewDidLoad:(UITableView *)tableView; /** Called when the mapView is about to be expanded (made fullscreen). Use this to perform custom animations or set attributes of the map/table. */ @@ -140,7 +139,7 @@ typedef void (^LocationPickerViewBlock)(LocationPickerView *locationPicker); /** Called when the mapView is about to be hidden (made tiny). Use this to perform custom animations or set attributes of the map/table. */ - (void)locationPicker:(LocationPickerView *)locationPicker - mapViewWillBeHidden:(MKMapView *)mapView; + mapViewWillBeHidden:(MKMapView *)mapView; /** Called when the mapView was hidden (made tiny). Use this to perform custom animations or set attributes of the map/table. */ diff --git a/Examples/BVSDKDemo/LocationPickerView.m b/Examples/BVSDKDemo/LocationPickerView.m index 5735d70c..c5df2fef 100644 --- a/Examples/BVSDKDemo/LocationPickerView.m +++ b/Examples/BVSDKDemo/LocationPickerView.m @@ -5,467 +5,474 @@ // Copyright (c) 2013 Futura IO. All rights reserved. // -#import #import "LocationPickerView.h" #import "UIImage+Icons.h" +#import @interface LocationPickerView () -@property (nonatomic) BOOL isMapAnimating; -@property (nonatomic) CGRect defaultMapViewFrame; -@property (nonatomic, strong) UITapGestureRecognizer *mapTapGesture; +@property(nonatomic) BOOL isMapAnimating; +@property(nonatomic) CGRect defaultMapViewFrame; +@property(nonatomic, strong) UITapGestureRecognizer *mapTapGesture; -/** This is only created if the user does not override the +/** This is only created if the user does not override the mapViewDidExpand: method. Allows the user to shrink the map. */ -@property (nonatomic, strong) UIButton *closeMapButton; -@property (nonatomic, readwrite) CGPoint closeButtonPoint; +@property(nonatomic, strong) UIButton *closeMapButton; +@property(nonatomic, readwrite) CGPoint closeButtonPoint; - (void)didTapCloseMapViewButton:(id)sender; @end @implementation LocationPickerView -- (id)init -{ - self = [super init]; - if (self) { - [self initialize]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self initialize]; + } + return self; } -- (id)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - [self initialize]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self initialize]; + } + return self; } -- (id)initWithCoder:(NSCoder *)aDecoder -{ - self = [super initWithCoder:aDecoder]; - if (self) { - [self initialize]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self initialize]; + } + return self; } -- (void)initialize -{ - _defaultMapHeight = 130.0f; - _parallaxScrollFactor = 0.6f; - _amountToScrollToFullScreenMap = 110.0f; - _shouldAutoCenterOnUserLocation = NO; - self.autoresizesSubviews = YES; - self.autoresizingMask = UIViewAutoresizingFlexibleWidth | - UIViewAutoresizingFlexibleHeight; - - self.closeButtonPoint = CGPointMake(14.0, 14.0); - self.backgroundViewColor = [UIColor clearColor]; +- (void)initialize { + _defaultMapHeight = 130.0f; + _parallaxScrollFactor = 0.6f; + _amountToScrollToFullScreenMap = 110.0f; + _shouldAutoCenterOnUserLocation = NO; + self.autoresizesSubviews = YES; + self.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + self.closeButtonPoint = CGPointMake(14.0, 14.0); + self.backgroundViewColor = [UIColor clearColor]; } -- (void)dealloc -{ - void *context = (__bridge void *)self; - [self.tableView removeObserver:self forKeyPath:@"contentOffset" context:context]; - [self.mapView removeObserver:self forKeyPath:@"userTrackingMode" context:context]; - [self.mapView.userLocation removeObserver:self forKeyPath:@"location" context:context]; - +- (void)dealloc { + void *context = (__bridge void *)self; + [self.tableView removeObserver:self + forKeyPath:@"contentOffset" + context:context]; + [self.mapView removeObserver:self + forKeyPath:@"userTrackingMode" + context:context]; + [self.mapView.userLocation removeObserver:self + forKeyPath:@"location" + context:context]; } +- (void)layoutSubviews { + [super layoutSubviews]; -- (void)layoutSubviews -{ - [super layoutSubviews]; - - if (!self.tableView) { - _tableView = [[UITableView alloc] initWithFrame:self.bounds]; - self.tableView.backgroundColor = [UIColor clearColor]; - self.tableView.delegate = self.tableViewDelegate; - self.tableView.dataSource = self.tableViewDataSource; - self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | - UIViewAutoresizingFlexibleHeight; - - if ([self.delegate respondsToSelector:@selector(setAutomaticallyAdjustsScrollViewInsets:)]) { - self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(64.0, 0.0, 0.0, 0.0); - } - - void *context = (__bridge void *)self; - [self.tableView addObserver:self - forKeyPath:@"contentOffset" - options:NSKeyValueObservingOptionNew - context:context]; - - [self addSubview:self.tableView]; - - if ([self.delegate respondsToSelector:@selector(locationPicker:tableViewDidLoad:)]) { - [self.delegate locationPicker:self tableViewDidLoad:self.tableView]; - } - - if (self.tableViewDidLoadBlock) { - self.tableViewDidLoadBlock(self); - } + if (!self.tableView) { + _tableView = [[UITableView alloc] initWithFrame:self.bounds]; + self.tableView.backgroundColor = [UIColor clearColor]; + self.tableView.delegate = self.tableViewDelegate; + self.tableView.dataSource = self.tableViewDataSource; + self.tableView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + if ([self.delegate respondsToSelector:@selector + (setAutomaticallyAdjustsScrollViewInsets:)]) { + self.tableView.scrollIndicatorInsets = + UIEdgeInsetsMake(64.0, 0.0, 0.0, 0.0); } - - if (!self.tableView.tableHeaderView) { - CGRect tableHeaderViewFrame = CGRectMake(0.0, 0.0, self.tableView.frame.size.width, self.defaultMapHeight); - UIView *tableHeaderView = [[UIView alloc] initWithFrame:tableHeaderViewFrame]; - tableHeaderView.backgroundColor = [UIColor clearColor]; - self.tableView.tableHeaderView = tableHeaderView; + + void *context = (__bridge void *)self; + [self.tableView addObserver:self + forKeyPath:@"contentOffset" + options:NSKeyValueObservingOptionNew + context:context]; + + [self addSubview:self.tableView]; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:tableViewDidLoad:)]) { + [self.delegate locationPicker:self tableViewDidLoad:self.tableView]; } - - if (!self.mapView) { - self.defaultMapViewFrame = CGRectMake(0.0, - -self.defaultMapHeight * self.parallaxScrollFactor * 2, - self.tableView.frame.size.width, - self.defaultMapHeight + (self.defaultMapHeight * self.parallaxScrollFactor * 4)); - _mapView = [[MKMapView alloc] initWithFrame:self.defaultMapViewFrame]; - self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.mapView.scrollEnabled = NO; - self.mapView.zoomEnabled = NO; - self.mapView.delegate = self.mapViewDelegate; - [self insertSubview:self.mapView belowSubview:self.tableView]; - - void *context = (__bridge void *)self; - [self.mapView.userLocation addObserver:self - forKeyPath:@"location" - options:NSKeyValueObservingOptionNew - context:context]; - - [self.mapView addObserver:self - forKeyPath:@"userTrackingMode" - options:NSKeyValueObservingOptionNew - context:context]; - - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { - [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; - } - - if (self.mapViewDidLoadBlock) { - self.mapViewDidLoadBlock(self); - } - } else { - - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { - [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; - } - - if (self.mapViewDidLoadBlock) { - self.mapViewDidLoadBlock(self); - } - + + if (self.tableViewDidLoadBlock) { + self.tableViewDidLoadBlock(self); + } + } + + if (!self.tableView.tableHeaderView) { + CGRect tableHeaderViewFrame = CGRectMake( + 0.0, 0.0, self.tableView.frame.size.width, self.defaultMapHeight); + UIView *tableHeaderView = + [[UIView alloc] initWithFrame:tableHeaderViewFrame]; + tableHeaderView.backgroundColor = [UIColor clearColor]; + self.tableView.tableHeaderView = tableHeaderView; + } + + if (!self.mapView) { + self.defaultMapViewFrame = + CGRectMake(0.0, -self.defaultMapHeight * self.parallaxScrollFactor * 2, + self.tableView.frame.size.width, + self.defaultMapHeight + + (self.defaultMapHeight * self.parallaxScrollFactor * 4)); + _mapView = [[MKMapView alloc] initWithFrame:self.defaultMapViewFrame]; + self.mapView.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.mapView.scrollEnabled = NO; + self.mapView.zoomEnabled = NO; + self.mapView.delegate = self.mapViewDelegate; + [self insertSubview:self.mapView belowSubview:self.tableView]; + + void *context = (__bridge void *)self; + [self.mapView.userLocation addObserver:self + forKeyPath:@"location" + options:NSKeyValueObservingOptionNew + context:context]; + + [self.mapView addObserver:self + forKeyPath:@"userTrackingMode" + options:NSKeyValueObservingOptionNew + context:context]; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { + [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; } - - // Add tap gesture to table - if (!self.mapTapGesture) { - self.mapTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self - action:@selector(mapWasTapped:)]; - self.mapTapGesture.cancelsTouchesInView = YES; - self.mapTapGesture.delaysTouchesBegan = NO; - [self.tableView.tableHeaderView addGestureRecognizer:self.mapTapGesture]; + + if (self.mapViewDidLoadBlock) { + self.mapViewDidLoadBlock(self); } - - // Add the background tableView - if (!self.backgroundView) { - UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0, self.defaultMapHeight, - self.tableView.frame.size.width, - self.tableView.frame.size.height - self.defaultMapHeight)]; - view.backgroundColor = self.backgroundViewColor; - self.backgroundView = view; - self.backgroundView.userInteractionEnabled=NO; - [self.tableView insertSubview:self.backgroundView atIndex:0]; + } else { + + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { + [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; } -} -- (void)setTableViewDataSource:(id)tableViewDataSource -{ - _tableViewDataSource = tableViewDataSource; - self.tableView.dataSource = _tableViewDataSource; - - if (_tableViewDelegate) { - [self.tableView reloadData]; + if (self.mapViewDidLoadBlock) { + self.mapViewDidLoadBlock(self); } + } + + // Add tap gesture to table + if (!self.mapTapGesture) { + self.mapTapGesture = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(mapWasTapped:)]; + self.mapTapGesture.cancelsTouchesInView = YES; + self.mapTapGesture.delaysTouchesBegan = NO; + [self.tableView.tableHeaderView addGestureRecognizer:self.mapTapGesture]; + } + + // Add the background tableView + if (!self.backgroundView) { + UIView *view = [[UIView alloc] + initWithFrame:CGRectMake(0.0, self.defaultMapHeight, + self.tableView.frame.size.width, + self.tableView.frame.size.height - + self.defaultMapHeight)]; + view.backgroundColor = self.backgroundViewColor; + self.backgroundView = view; + self.backgroundView.userInteractionEnabled = NO; + [self.tableView insertSubview:self.backgroundView atIndex:0]; + } } -- (void)setTableViewDelegate:(id)tableViewDelegate -{ - _tableViewDelegate = tableViewDelegate; - self.tableView.delegate = _tableViewDelegate; - - if (_tableViewDataSource) { - [self.tableView reloadData]; - } +- (void)setTableViewDataSource:(id)tableViewDataSource { + _tableViewDataSource = tableViewDataSource; + self.tableView.dataSource = _tableViewDataSource; + + if (_tableViewDelegate) { + [self.tableView reloadData]; + } } -- (void)setMapViewDelegate:(id)mapViewDelegate -{ - _mapViewDelegate = mapViewDelegate; - self.mapView.delegate = _mapViewDelegate; +- (void)setTableViewDelegate:(id)tableViewDelegate { + _tableViewDelegate = tableViewDelegate; + self.tableView.delegate = _tableViewDelegate; + + if (_tableViewDataSource) { + [self.tableView reloadData]; + } } -- (void)setTableView:(UITableView *)tableView -{ - _tableView = tableView; - - if ([self.delegate respondsToSelector:@selector(locationPicker:tableViewDidLoad:)]) { - [self.delegate locationPicker:self tableViewDidLoad:self.tableView]; - } - - if (self.tableViewDidLoadBlock) { - self.tableViewDidLoadBlock(self); - } +- (void)setMapViewDelegate:(id)mapViewDelegate { + _mapViewDelegate = mapViewDelegate; + self.mapView.delegate = _mapViewDelegate; } -- (void)setMapView:(MKMapView *)mapView -{ - _mapView = mapView; - - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { - [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; - } - - if (self.mapViewDidLoadBlock) { - self.mapViewDidLoadBlock(self); - } +- (void)setTableView:(UITableView *)tableView { + _tableView = tableView; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:tableViewDidLoad:)]) { + [self.delegate locationPicker:self tableViewDidLoad:self.tableView]; + } + + if (self.tableViewDidLoadBlock) { + self.tableViewDidLoadBlock(self); + } } -- (void)setCustomCloseButton:(UIButton *)closeButton -{ - [self setCustomCloseButton:closeButton atPoint:self.closeButtonPoint]; +- (void)setMapView:(MKMapView *)mapView { + _mapView = mapView; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewDidLoad:)]) { + [self.delegate locationPicker:self mapViewDidLoad:self.mapView]; + } + + if (self.mapViewDidLoadBlock) { + self.mapViewDidLoadBlock(self); + } } -- (void)setCustomCloseButton:(UIButton *)closeButton atPoint:(CGPoint)buttonPoint{ - if(self.closeMapButton) [self.closeMapButton removeFromSuperview]; - self.closeMapButton = closeButton; - self.closeButtonPoint = buttonPoint; - [self.closeMapButton addTarget:self action:@selector(didTapCloseMapViewButton:) forControlEvents:UIControlEventTouchUpInside]; - self.closeMapButton.hidden = YES; - - [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; +- (void)setCustomCloseButton:(UIButton *)closeButton { + [self setCustomCloseButton:closeButton atPoint:self.closeButtonPoint]; +} + +- (void)setCustomCloseButton:(UIButton *)closeButton + atPoint:(CGPoint)buttonPoint { + if (self.closeMapButton) + [self.closeMapButton removeFromSuperview]; + self.closeMapButton = closeButton; + self.closeButtonPoint = buttonPoint; + [self.closeMapButton addTarget:self + action:@selector(didTapCloseMapViewButton:) + forControlEvents:UIControlEventTouchUpInside]; + self.closeMapButton.hidden = YES; + + [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; } #pragma mark - Internal Methods -- (void)mapWasTapped:(id)sender -{ - [self expandMapView:self]; +- (void)mapWasTapped:(id)sender { + [self expandMapView:self]; } -- (void)showCloseMapButton -{ - if (!self.closeMapButton) { - self.closeMapButton = [UIButton buttonWithType:UIButtonTypeCustom]; - self.closeMapButton.frame = CGRectMake(self.closeButtonPoint.x, self.closeButtonPoint.y, 42.0, 42.0); - [self.closeMapButton setImage:[UIImage imageForXIcon] forState:UIControlStateNormal]; - [self.closeMapButton setImage:[UIImage imageForXIcon] forState:UIControlStateHighlighted]; - [self.closeMapButton addTarget:self action:@selector(didTapCloseMapViewButton:) forControlEvents:UIControlEventTouchUpInside]; - self.closeMapButton.hidden = YES; - - [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; - } - else{ - [self.closeMapButton setFrame:CGRectMake(self.closeButtonPoint.x, self.closeButtonPoint.y, self.closeMapButton.frame.size.width, self.closeMapButton.frame.size.height)]; - } - - self.closeMapButton.alpha = 0.0; - self.closeMapButton.hidden = NO; - [UIView animateWithDuration:0.3 - delay:0.0 - options:UIViewAnimationOptionCurveLinear - animations:^{ - self.closeMapButton.alpha = 1.0; - } - completion:nil]; +- (void)showCloseMapButton { + if (!self.closeMapButton) { + self.closeMapButton = [UIButton buttonWithType:UIButtonTypeCustom]; + self.closeMapButton.frame = CGRectMake(self.closeButtonPoint.x, + self.closeButtonPoint.y, 42.0, 42.0); + [self.closeMapButton setImage:[UIImage imageForXIcon] + forState:UIControlStateNormal]; + [self.closeMapButton setImage:[UIImage imageForXIcon] + forState:UIControlStateHighlighted]; + [self.closeMapButton addTarget:self + action:@selector(didTapCloseMapViewButton:) + forControlEvents:UIControlEventTouchUpInside]; + self.closeMapButton.hidden = YES; + + [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; + } else { + [self.closeMapButton + setFrame:CGRectMake(self.closeButtonPoint.x, self.closeButtonPoint.y, + self.closeMapButton.frame.size.width, + self.closeMapButton.frame.size.height)]; + } + + self.closeMapButton.alpha = 0.0; + self.closeMapButton.hidden = NO; + [UIView animateWithDuration:0.3 + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + self.closeMapButton.alpha = 1.0; + } + completion:nil]; } -- (void)hideCloseMapButton -{ - if (self.closeMapButton) { - self.closeMapButton.hidden = NO; - [UIView animateWithDuration:0.3 - delay:0.0 - options:UIViewAnimationOptionCurveLinear - animations:^{ - self.closeMapButton.alpha = 0.0; - } - completion:^(BOOL finished) { - self.closeMapButton.hidden = YES; - }]; - } +- (void)hideCloseMapButton { + if (self.closeMapButton) { + self.closeMapButton.hidden = NO; + [UIView animateWithDuration:0.3 + delay:0.0 + options:UIViewAnimationOptionCurveLinear + animations:^{ + self.closeMapButton.alpha = 0.0; + } + completion:^(BOOL finished) { + self.closeMapButton.hidden = YES; + }]; + } } #pragma mark - Expanding / Shrinking Map Methods -- (void)expandMapView:(id)sender -{ - [self expandMapView:sender animated:YES]; +- (void)expandMapView:(id)sender { + [self expandMapView:sender animated:YES]; } -- (void)expandMapView:(id)sender - animated:(BOOL)animated -{ - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewWillExpand:)]) { - [self.delegate locationPicker:self mapViewWillExpand:self.mapView]; - } - if (self.mapViewWillExpand) { - self.mapViewWillExpand(self); - } - - self.isMapAnimating = animated; - [self.tableView.tableHeaderView removeGestureRecognizer:self.mapTapGesture]; - if (self.tableView.numberOfSections && [self.tableView numberOfRowsInSection:0]) { - [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated]; - } - - CGRect newMapFrame = self.mapView.frame; - newMapFrame = CGRectMake(self.defaultMapViewFrame.origin.x, - self.defaultMapViewFrame.origin.y + (self.defaultMapHeight * self.parallaxScrollFactor), - self.defaultMapViewFrame.size.width, - self.defaultMapHeight + (self.defaultMapHeight * self.parallaxScrollFactor * 2)); - self.mapView.frame = newMapFrame; - - [self bringSubviewToFront:self.mapView]; - [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; - - if(animated == YES) - { - [UIView animateWithDuration:0.3 - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - self.mapView.frame = self.bounds; - } completion:^(BOOL finished) { - [self mapViewDidFinishExpanding]; - }]; - } - else - { - self.mapView.frame = self.bounds; - [self mapViewDidFinishExpanding]; - } +- (void)expandMapView:(id)sender animated:(BOOL)animated { + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewWillExpand:)]) { + [self.delegate locationPicker:self mapViewWillExpand:self.mapView]; + } + if (self.mapViewWillExpand) { + self.mapViewWillExpand(self); + } + + self.isMapAnimating = animated; + [self.tableView.tableHeaderView removeGestureRecognizer:self.mapTapGesture]; + if (self.tableView.numberOfSections && + [self.tableView numberOfRowsInSection:0]) { + [self.tableView + scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] + atScrollPosition:UITableViewScrollPositionBottom + animated:animated]; + } + + CGRect newMapFrame = self.mapView.frame; + newMapFrame = + CGRectMake(self.defaultMapViewFrame.origin.x, + self.defaultMapViewFrame.origin.y + + (self.defaultMapHeight * self.parallaxScrollFactor), + self.defaultMapViewFrame.size.width, + self.defaultMapHeight + + (self.defaultMapHeight * self.parallaxScrollFactor * 2)); + self.mapView.frame = newMapFrame; + + [self bringSubviewToFront:self.mapView]; + [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; + + if (animated == YES) { + [UIView animateWithDuration:0.3 + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + self.mapView.frame = self.bounds; + } + completion:^(BOOL finished) { + [self mapViewDidFinishExpanding]; + }]; + } else { + self.mapView.frame = self.bounds; + [self mapViewDidFinishExpanding]; + } } -- (void)mapViewDidFinishExpanding -{ - self.isMapAnimating = NO; - _isMapFullScreen = YES; - self.mapView.scrollEnabled = YES; - self.mapView.zoomEnabled = YES; - - [self centerMapOnUserLocationIfNecessary]; - - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewDidExpand:)]) { - [self.delegate locationPicker:self mapViewDidExpand:self.mapView]; - } - if (self.mapViewDidExpand) { - self.mapViewDidExpand(self); - } - - if (self.shouldCreateHideMapButton) { - [self showCloseMapButton]; - } +- (void)mapViewDidFinishExpanding { + self.isMapAnimating = NO; + _isMapFullScreen = YES; + self.mapView.scrollEnabled = YES; + self.mapView.zoomEnabled = YES; + + [self centerMapOnUserLocationIfNecessary]; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewDidExpand:)]) { + [self.delegate locationPicker:self mapViewDidExpand:self.mapView]; + } + if (self.mapViewDidExpand) { + self.mapViewDidExpand(self); + } + + if (self.shouldCreateHideMapButton) { + [self showCloseMapButton]; + } } -- (void)hideMapView:(id)sender -{ - [self hideMapView:sender animated:YES]; +- (void)hideMapView:(id)sender { + [self hideMapView:sender animated:YES]; } -- (void)didTapCloseMapViewButton:(id)sender -{ - // override default close map button action if block is set - if(self.mapCloseButtonTapped) - { - self.mapCloseButtonTapped(self); - } - else - { - // default action for close map view button - [self hideMapView:self]; - } +- (void)didTapCloseMapViewButton:(id)sender { + // override default close map button action if block is set + if (self.mapCloseButtonTapped) { + self.mapCloseButtonTapped(self); + } else { + // default action for close map view button + [self hideMapView:self]; + } } -- (void)hideMapView:(id)sender animated:(BOOL)animated -{ - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewWillBeHidden:)]) { - [self.delegate locationPicker:self mapViewWillBeHidden:self.mapView]; - } - if (self.mapViewWillBeHidden) { - self.mapViewWillBeHidden(self); - } - - if (self.shouldCreateHideMapButton) { - [self hideCloseMapButton]; - } - - self.isMapAnimating = animated; - self.mapView.scrollEnabled = NO; - self.mapView.zoomEnabled = NO; - [self.tableView.tableHeaderView addGestureRecognizer:self.mapTapGesture]; - - // Store the correct tableViewFrame. - // Set table view off the bottom of the screen, and animate - // back to normal - CGRect tempFrame = self.tableView.frame; - self.tableView.frame = CGRectMake(0, 480, tempFrame.size.width, tempFrame.size.height); - [self insertSubview:self.mapView belowSubview:self.tableView]; - - if(animated == YES) - { - [UIView animateWithDuration:0.4 - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - self.mapView.frame = self.defaultMapViewFrame; - self.tableView.frame = tempFrame; - } completion:^(BOOL finished) { - [self mapViewDidFinishHiding]; - }]; - } - else - { - self.mapView.frame = self.defaultMapViewFrame; - self.tableView.frame = tempFrame; - [self mapViewDidFinishHiding]; - } +- (void)hideMapView:(id)sender animated:(BOOL)animated { + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewWillBeHidden:)]) { + [self.delegate locationPicker:self mapViewWillBeHidden:self.mapView]; + } + if (self.mapViewWillBeHidden) { + self.mapViewWillBeHidden(self); + } + + if (self.shouldCreateHideMapButton) { + [self hideCloseMapButton]; + } + + self.isMapAnimating = animated; + self.mapView.scrollEnabled = NO; + self.mapView.zoomEnabled = NO; + [self.tableView.tableHeaderView addGestureRecognizer:self.mapTapGesture]; + + // Store the correct tableViewFrame. + // Set table view off the bottom of the screen, and animate + // back to normal + CGRect tempFrame = self.tableView.frame; + self.tableView.frame = + CGRectMake(0, 480, tempFrame.size.width, tempFrame.size.height); + [self insertSubview:self.mapView belowSubview:self.tableView]; + + if (animated == YES) { + [UIView animateWithDuration:0.4 + delay:0.0 + options:UIViewAnimationOptionCurveEaseOut + animations:^{ + self.mapView.frame = self.defaultMapViewFrame; + self.tableView.frame = tempFrame; + } + completion:^(BOOL finished) { + [self mapViewDidFinishHiding]; + }]; + } else { + self.mapView.frame = self.defaultMapViewFrame; + self.tableView.frame = tempFrame; + [self mapViewDidFinishHiding]; + } } -- (void)mapViewDidFinishHiding -{ - [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; - self.isMapAnimating = NO; - _isMapFullScreen = NO; - - [self centerMapOnUserLocationIfNecessary]; - - if ([self.delegate respondsToSelector:@selector(locationPicker:mapViewWasHidden:)]) { - [self.delegate locationPicker:self mapViewWasHidden:self.mapView]; - } - if (self.mapViewWasHidden) { - self.mapViewWasHidden(self); - } +- (void)mapViewDidFinishHiding { + [self insertSubview:self.closeMapButton aboveSubview:self.mapView]; + self.isMapAnimating = NO; + _isMapFullScreen = NO; + + [self centerMapOnUserLocationIfNecessary]; + + if ([self.delegate + respondsToSelector:@selector(locationPicker:mapViewWasHidden:)]) { + [self.delegate locationPicker:self mapViewWasHidden:self.mapView]; + } + if (self.mapViewWasHidden) { + self.mapViewWasHidden(self); + } } -- (void)toggleMapView:(id)sender -{ - if (!self.isMapAnimating) { - if (self.isMapFullScreen) { - [self hideMapView:self]; - } - else { - [self expandMapView:self]; - } +- (void)toggleMapView:(id)sender { + if (!self.isMapAnimating) { + if (self.isMapFullScreen) { + [self hideMapView:self]; + } else { + [self expandMapView:self]; } + } } -- (void)centerMapOnUserLocationIfNecessary -{ - if (self.shouldAutoCenterOnUserLocation && self.mapView.userTrackingMode) { - MKCoordinateRegion centerCoordinate = MKCoordinateRegionMake(self.mapView.userLocation.coordinate, self.mapView.region.span); - [self.mapView setRegion:centerCoordinate - animated:YES]; - } +- (void)centerMapOnUserLocationIfNecessary { + if (self.shouldAutoCenterOnUserLocation && self.mapView.userTrackingMode) { + MKCoordinateRegion centerCoordinate = MKCoordinateRegionMake( + self.mapView.userLocation.coordinate, self.mapView.region.span); + [self.mapView setRegion:centerCoordinate animated:YES]; + } } #pragma mark - KVO Methods @@ -473,59 +480,58 @@ - (void)centerMapOnUserLocationIfNecessary - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change - context:(void *)context -{ - // Make sure we are observing this value. - if (context != (__bridge void *)self) { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - return; - } - - if ((object == self.tableView) && - ([keyPath isEqualToString:@"contentOffset"] == YES)) { - [self scrollViewDidScrollWithOffset:self.tableView.contentOffset.y]; + context:(void *)context { + // Make sure we are observing this value. + if (context != (__bridge void *)self) { + [super observeValueForKeyPath:keyPath + ofObject:object + change:change + context:context]; + return; + } + + if ((object == self.tableView) && + ([keyPath isEqualToString:@"contentOffset"] == YES)) { + [self scrollViewDidScrollWithOffset:self.tableView.contentOffset.y]; + } else if ((object == self.mapView) && + ([keyPath isEqualToString:@"userTrackingMode"] == YES)) { + } +} + +- (void)scrollViewDidScrollWithOffset:(CGFloat)scrollOffset { + if ((self.isMapFullScreen == NO) && (self.isMapAnimating == NO)) { + CGFloat mapFrameYAdjustment = 0.0; + + // If the user is pulling down + if (scrollOffset < 0) { + + // Pull to expand map? + if (self.pullToExpandMapEnabled && (self.isMapAnimating == NO) && + (scrollOffset <= -self.amountToScrollToFullScreenMap)) { + [self expandMapView:self]; + } else { + mapFrameYAdjustment = self.defaultMapViewFrame.origin.y - + (scrollOffset * self.parallaxScrollFactor); + } } - else if ((object == self.mapView) && - ([keyPath isEqualToString:@"userTrackingMode"] == YES)) { + + // If the user is scrolling normally, + else { + mapFrameYAdjustment = self.defaultMapViewFrame.origin.y - + (scrollOffset * self.parallaxScrollFactor); + + // Don't move the map way off-screen + if (mapFrameYAdjustment <= -(self.defaultMapViewFrame.size.height)) { + mapFrameYAdjustment = -(self.defaultMapViewFrame.size.height); + } } -} -- (void)scrollViewDidScrollWithOffset:(CGFloat)scrollOffset -{ - if ((self.isMapFullScreen == NO) && - (self.isMapAnimating == NO)) { - CGFloat mapFrameYAdjustment = 0.0; - - // If the user is pulling down - if (scrollOffset < 0) { - - // Pull to expand map? - if (self.pullToExpandMapEnabled && - (self.isMapAnimating == NO) && - (scrollOffset <= -self.amountToScrollToFullScreenMap)) { - [self expandMapView:self]; - } - else { - mapFrameYAdjustment = self.defaultMapViewFrame.origin.y - (scrollOffset * self.parallaxScrollFactor); - } - } - - // If the user is scrolling normally, - else { - mapFrameYAdjustment = self.defaultMapViewFrame.origin.y - (scrollOffset * self.parallaxScrollFactor); - - // Don't move the map way off-screen - if (mapFrameYAdjustment <= -(self.defaultMapViewFrame.size.height)) { - mapFrameYAdjustment = -(self.defaultMapViewFrame.size.height); - } - } - - if (mapFrameYAdjustment) { - CGRect newMapFrame = self.mapView.frame; - newMapFrame.origin.y = mapFrameYAdjustment; - self.mapView.frame = newMapFrame; - } + if (mapFrameYAdjustment) { + CGRect newMapFrame = self.mapView.frame; + newMapFrame.origin.y = mapFrameYAdjustment; + self.mapView.frame = newMapFrame; } + } } @end diff --git a/Examples/BVSDKDemo/UIImage+Icons.m b/Examples/BVSDKDemo/UIImage+Icons.m index 823c769b..effabc22 100644 --- a/Examples/BVSDKDemo/UIImage+Icons.m +++ b/Examples/BVSDKDemo/UIImage+Icons.m @@ -9,50 +9,52 @@ @implementation UIImage (Icons) -+ (UIImage *)imageForXIcon -{ - UIGraphicsBeginImageContextWithOptions(CGSizeMake(32, 32), NO, 0.0); - CGContextRef context = UIGraphicsGetCurrentContext(); - - UIColor *navyBlueColor = [UIColor colorWithRed:0.0 green:0.24 blue:0.3 alpha:0.75]; - - UIColor *strokeColor = navyBlueColor; - UIColor *shadowColor = [UIColor lightGrayColor]; - CGSize shadowOffset = CGSizeMake(1.0, 2.0); - CGFloat shadowBlurRadius = 3.0; - - // Line 1 - UIBezierPath *bezierPath = [UIBezierPath bezierPath]; - [bezierPath moveToPoint:CGPointMake(5, 5)]; - [bezierPath addLineToPoint:CGPointMake(20, 20)]; - bezierPath.miterLimit = 11; - bezierPath.lineCapStyle = kCGLineCapRound; - - CGContextSaveGState(context); - CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadowColor.CGColor); - [strokeColor setStroke]; - bezierPath.lineWidth = 5.0; - [bezierPath stroke]; - CGContextRestoreGState(context); - - // Line 2 - bezierPath = [UIBezierPath bezierPath]; - [bezierPath moveToPoint:CGPointMake(5, 20)]; - [bezierPath addLineToPoint:CGPointMake(20, 5)]; - bezierPath.miterLimit = 11; - bezierPath.lineCapStyle = kCGLineCapRound; - - CGContextSaveGState(context); - CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadowColor.CGColor); - [strokeColor setStroke]; - bezierPath.lineWidth = 5.0; - [bezierPath stroke]; - CGContextRestoreGState(context); - - UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return returnImage; ++ (UIImage *)imageForXIcon { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(32, 32), NO, 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + UIColor *navyBlueColor = + [UIColor colorWithRed:0.0 green:0.24 blue:0.3 alpha:0.75]; + + UIColor *strokeColor = navyBlueColor; + UIColor *shadowColor = [UIColor lightGrayColor]; + CGSize shadowOffset = CGSizeMake(1.0, 2.0); + CGFloat shadowBlurRadius = 3.0; + + // Line 1 + UIBezierPath *bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:CGPointMake(5, 5)]; + [bezierPath addLineToPoint:CGPointMake(20, 20)]; + bezierPath.miterLimit = 11; + bezierPath.lineCapStyle = kCGLineCapRound; + + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, + shadowColor.CGColor); + [strokeColor setStroke]; + bezierPath.lineWidth = 5.0; + [bezierPath stroke]; + CGContextRestoreGState(context); + + // Line 2 + bezierPath = [UIBezierPath bezierPath]; + [bezierPath moveToPoint:CGPointMake(5, 20)]; + [bezierPath addLineToPoint:CGPointMake(20, 5)]; + bezierPath.miterLimit = 11; + bezierPath.lineCapStyle = kCGLineCapRound; + + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, + shadowColor.CGColor); + [strokeColor setStroke]; + bezierPath.lineWidth = 5.0; + [bezierPath stroke]; + CGContextRestoreGState(context); + + UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return returnImage; } @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/AnswersViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/AnswersViewController.m index 30d17f6b..9d6437c0 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/AnswersViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/AnswersViewController.m @@ -10,39 +10,44 @@ @interface AnswersViewController () -@property (weak, nonatomic) IBOutlet BVAnswersTableView *answersTableView; +@property(weak, nonatomic) IBOutlet BVAnswersTableView *answersTableView; @end @implementation AnswersViewController - (void)viewDidLoad { - [super viewDidLoad]; - self.answersTableView.dataSource = self; - self.answersTableView.estimatedRowHeight = 44; - self.answersTableView.rowHeight = UITableViewAutomaticDimension; - [self.answersTableView registerNib:[UINib nibWithNibName:@"MyAnswerTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyAnswerTableViewCell"]; + [super viewDidLoad]; + self.answersTableView.dataSource = self; + self.answersTableView.estimatedRowHeight = 44; + self.answersTableView.rowHeight = UITableViewAutomaticDimension; + [self.answersTableView + registerNib:[UINib nibWithNibName:@"MyAnswerTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyAnswerTableViewCell"]; } #pragma mark UITableViewDatasource --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - return @"Questions Responses"; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return @"Questions Responses"; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [self.question.answers count]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.question.answers count]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - MyAnswerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyAnswerTableViewCell"]; - - cell.answer = [self.question.answers objectAtIndex:indexPath.row]; - - return cell; -} + MyAnswerTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyAnswerTableViewCell"]; + + cell.answer = [self.question.answers objectAtIndex:indexPath.row]; + return cell; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.h b/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.h index e4d252cb..840f18d3 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.h +++ b/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.h @@ -9,8 +9,6 @@ @interface AppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; - +@property(strong, nonatomic) UIWindow *window; @end - diff --git a/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.m b/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.m index d46ea0a6..b595cb55 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/AppDelegate.m @@ -10,15 +10,15 @@ @implementation AppDelegate +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. - #warning See bvsdk_config_staging.json and bvsdk_config_product.json in the project for API key and client ID settings. - [BVSDKManager configure:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; - - return YES; + [BVSDKManager configure:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; + + return YES; } @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m index 19a611f7..52073244 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/AuthorViewController.m @@ -6,213 +6,237 @@ // #import "AuthorViewController.h" +#import "MyAnswerTableViewCell.h" #import "MyQuestionTableViewCell.h" #import "MyReviewTableViewCell.h" -#import "MyQuestionTableViewCell.h" -#import "MyAnswerTableViewCell.h" #import "StatisticTableViewCell.h" @import BVSDK; typedef enum { - ProfileStats = 0, - IncludedReviews, - IncludedQuestions, - IncludedAnswers, - SectionCount // Keep last to keep the section count + ProfileStats = 0, + IncludedReviews, + IncludedQuestions, + IncludedAnswers, + SectionCount // Keep last to keep the section count } AuthorSections; @interface AuthorViewController () -@property (weak, nonatomic) IBOutlet UITableView *authorProfileTableView; -@property (strong, nonatomic) BVAuthorResponse *authorResponse; +@property(weak, nonatomic) IBOutlet UITableView *authorProfileTableView; +@property(strong, nonatomic) BVAuthorResponse *authorResponse; @end @implementation AuthorViewController - (void)viewDidLoad { - - [super viewDidLoad]; - - self.authorProfileTableView.estimatedRowHeight = 44; - self.authorProfileTableView.rowHeight = UITableViewAutomaticDimension; - [self.authorProfileTableView registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" bundle:nil] forCellReuseIdentifier:@"StatisticTableViewCell"]; - [self.authorProfileTableView registerNib:[UINib nibWithNibName:@"MyReviewTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyReviewTableViewCell"]; - [self.authorProfileTableView registerNib:[UINib nibWithNibName:@"MyQuestionTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyQuestionTableViewCell"]; - [self.authorProfileTableView registerNib:[UINib nibWithNibName:@"MyAnswerTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyAnswerTableViewCell"]; - - NSString *authorId = @"data-gen-user-c3k8hjvtpn03dupvxcui1rj3"; - //NSString *authorId = @"data-gen-user-3aykphgodq0ng2i2jwk67b7fy"; - //NSString *authorId = @"data-gen-user-2eiqsoaxkf78m8knj5hp9pj7l"; - - BVAuthorRequest *request = [[BVAuthorRequest alloc] initWithAuthorId:authorId]; - [request includeStatistics:BVAuthorContentTypeReviews]; - [request includeStatistics:BVAuthorContentTypeQuestions]; - [request includeStatistics:BVAuthorContentTypeAnswers]; - [request includeContent:BVAuthorContentTypeReviews limit:5]; - [request includeContent:BVAuthorContentTypeQuestions limit:5]; - [request includeContent:BVAuthorContentTypeAnswers limit:5]; - - [request load:^(BVAuthorResponse * _Nonnull response) { - - // Success! - NSLog(@"Succesfully loaded profile: %@", response); - self.authorResponse = response; - [_authorProfileTableView reloadData]; - - } failure:^(NSArray * _Nonnull errors) { - + + [super viewDidLoad]; + + self.authorProfileTableView.estimatedRowHeight = 44; + self.authorProfileTableView.rowHeight = UITableViewAutomaticDimension; + [self.authorProfileTableView + registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"StatisticTableViewCell"]; + [self.authorProfileTableView + registerNib:[UINib nibWithNibName:@"MyReviewTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyReviewTableViewCell"]; + [self.authorProfileTableView + registerNib:[UINib nibWithNibName:@"MyQuestionTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyQuestionTableViewCell"]; + [self.authorProfileTableView + registerNib:[UINib nibWithNibName:@"MyAnswerTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyAnswerTableViewCell"]; + + NSString *authorId = @"data-gen-user-c3k8hjvtpn03dupvxcui1rj3"; + // NSString *authorId = @"data-gen-user-3aykphgodq0ng2i2jwk67b7fy"; + // NSString *authorId = @"data-gen-user-2eiqsoaxkf78m8knj5hp9pj7l"; + + BVAuthorRequest *request = + [[BVAuthorRequest alloc] initWithAuthorId:authorId]; + [request includeStatistics:BVAuthorContentTypeReviews]; + [request includeStatistics:BVAuthorContentTypeQuestions]; + [request includeStatistics:BVAuthorContentTypeAnswers]; + [request includeContent:BVAuthorContentTypeReviews limit:5]; + [request includeContent:BVAuthorContentTypeQuestions limit:5]; + [request includeContent:BVAuthorContentTypeAnswers limit:5]; + + [request load:^(BVAuthorResponse *_Nonnull response) { + + // Success! + NSLog(@"Succesfully loaded profile: %@", response); + self.authorResponse = response; + [_authorProfileTableView reloadData]; + + } + failure:^(NSArray *_Nonnull errors) { + // Error : ( NSLog(@"ERROR loading author: %@", errors.description); - - }]; - -} + }]; +} #pragma mark UITableViewDatasource -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ - return SectionCount; +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return SectionCount; } --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - - switch (section) { - case ProfileStats: - return @"Author Profile Stats"; - break; - case IncludedReviews: - return @"Included Reviews"; - break; - case IncludedQuestions: - return @"Included Questions"; - break; - case IncludedAnswers: - return @"Included Answers"; - break; - default: - break; - } - - return @""; - +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + + switch (section) { + case ProfileStats: + return @"Author Profile Stats"; + break; + case IncludedReviews: + return @"Included Reviews"; + break; + case IncludedQuestions: + return @"Included Questions"; + break; + case IncludedAnswers: + return @"Included Answers"; + break; + default: + break; + } + + return @""; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - - if (!self.authorResponse) return 0; - - BVAuthor *author = self.authorResponse.results.firstObject; - - switch (section) { - case ProfileStats: - return [self.authorResponse.totalResults integerValue]; // should always be 1 - break; - case IncludedReviews: - return author.includedReviews != nil ? author.includedReviews.count : 0 ; - break; - case IncludedQuestions: - return author.includedQuestions != nil ? author.includedQuestions.count : 0 ; - break; - case IncludedAnswers: - return author.includedAnswers != nil ? author.includedAnswers.count : 0 ; - break; - default: - break; - } - +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + + if (!self.authorResponse) return 0; -} + BVAuthor *author = self.authorResponse.results.firstObject; + + switch (section) { + case ProfileStats: + return + [self.authorResponse.totalResults integerValue]; // should always be 1 + break; + case IncludedReviews: + return author.includedReviews != nil ? author.includedReviews.count : 0; + break; + case IncludedQuestions: + return author.includedQuestions != nil ? author.includedQuestions.count : 0; + break; + case IncludedAnswers: + return author.includedAnswers != nil ? author.includedAnswers.count : 0; + break; + default: + break; + } + + return 0; +} - (NSString *)createAuthorStatsString:(BVAuthor *)author { - - if (!author) return @"No author stats."; - NSString *summaryText = [NSString stringWithFormat:@"Stats for: %@\n", author.userNickname]; - - if (author.userLocation){ - summaryText = [summaryText stringByAppendingFormat:@"Location: %@\n", author.userLocation]; - } - - if (author.reviewStatistics){ - summaryText = [summaryText stringByAppendingFormat:@"Reviews (%@)\n", author.reviewStatistics.totalReviewCount]; - } - - if (author.qaStatistics){ - summaryText = [summaryText stringByAppendingFormat:@"Questions (%@)\n", author.qaStatistics.totalQuestionCount]; - summaryText = [summaryText stringByAppendingFormat:@"Answers (%@)\n", author.qaStatistics.totalAnswerCount]; - } - - if (author.contextDataValues){ - summaryText = [summaryText stringByAppendingFormat:@"Context Data Values (%lu) ", (unsigned long)author.contextDataValues.count]; - - for (BVContextDataValue *contextData in author.contextDataValues){ - summaryText = [summaryText stringByAppendingFormat:@"[%@:%@]", contextData.identifier, contextData.value]; - } - - summaryText = [summaryText stringByAppendingString:@"\n"]; - } - - if (author.badges){ - summaryText = [summaryText stringByAppendingFormat:@"Badges (%lu) ", (unsigned long)author.badges.count]; - - for (BVBadge *badge in author.badges){ - summaryText = [summaryText stringByAppendingFormat:@"[%@:%@]", badge.identifier, badge.contentType]; - } - - summaryText = [summaryText stringByAppendingString:@"\n"]; - + if (!author) + return @"No author stats."; + + NSString *summaryText = + [NSString stringWithFormat:@"Stats for: %@\n", author.userNickname]; + + if (author.userLocation) { + summaryText = [summaryText + stringByAppendingFormat:@"Location: %@\n", author.userLocation]; + } + + if (author.reviewStatistics) { + summaryText = [summaryText + stringByAppendingFormat:@"Reviews (%@)\n", + author.reviewStatistics.totalReviewCount]; + } + + if (author.qaStatistics) { + summaryText = [summaryText + stringByAppendingFormat:@"Questions (%@)\n", + author.qaStatistics.totalQuestionCount]; + summaryText = [summaryText + stringByAppendingFormat:@"Answers (%@)\n", + author.qaStatistics.totalAnswerCount]; + } + + if (author.contextDataValues) { + summaryText = [summaryText + stringByAppendingFormat:@"Context Data Values (%lu) ", + (unsigned long)author.contextDataValues.count]; + + for (BVContextDataValue *contextData in author.contextDataValues) { + summaryText = [summaryText stringByAppendingFormat:@"[%@:%@]", + contextData.identifier, + contextData.value]; } - - return summaryText; -} -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - BVAuthor *author = self.authorResponse.results.firstObject; - - switch (indexPath.section) { - - case ProfileStats: - { - StatisticTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; - cell.statTypeLabel.text = @"Author Statistics"; - cell.statValueLabel.text = [self createAuthorStatsString:author]; - return cell; - } - break; - case IncludedReviews: - { - MyReviewTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyReviewTableViewCell"]; - cell.review = [author.includedReviews objectAtIndex:indexPath.row]; - return cell; - } - break; - case IncludedQuestions: - { - MyQuestionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyQuestionTableViewCell"]; - cell.question = [author.includedQuestions objectAtIndex:indexPath.row]; - cell.accessoryType = UITableViewCellAccessoryNone; - return cell; - } - break; - case IncludedAnswers: - { - MyAnswerTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyAnswerTableViewCell"]; - cell.answer = [author.includedAnswers objectAtIndex:indexPath.row]; - return cell; - } - break; - default: - break; + summaryText = [summaryText stringByAppendingString:@"\n"]; + } + + if (author.badges) { + summaryText = [summaryText + stringByAppendingFormat:@"Badges (%lu) ", + (unsigned long)author.badges.count]; + + for (BVBadge *badge in author.badges) { + summaryText = + [summaryText stringByAppendingFormat:@"[%@:%@]", badge.identifier, + badge.contentType]; } - - return nil; + summaryText = [summaryText stringByAppendingString:@"\n"]; + } + + return summaryText; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + BVAuthor *author = self.authorResponse.results.firstObject; + + switch (indexPath.section) { + + case ProfileStats: { + StatisticTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; + cell.statTypeLabel.text = @"Author Statistics"; + cell.statValueLabel.text = [self createAuthorStatsString:author]; + return cell; + } break; + case IncludedReviews: { + MyReviewTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyReviewTableViewCell"]; + cell.review = [author.includedReviews objectAtIndex:indexPath.row]; + return cell; + } break; + case IncludedQuestions: { + MyQuestionTableViewCell *cell = [tableView + dequeueReusableCellWithIdentifier:@"MyQuestionTableViewCell"]; + cell.question = [author.includedQuestions objectAtIndex:indexPath.row]; + cell.accessoryType = UITableViewCellAccessoryNone; + return cell; + } break; + case IncludedAnswers: { + MyAnswerTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyAnswerTableViewCell"]; + cell.answer = [author.includedAnswers objectAtIndex:indexPath.row]; + return cell; + } break; + default: + break; + } + + return nil; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/CommentsViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/CommentsViewController.m index 3f280682..bbc79d00 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/CommentsViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/CommentsViewController.m @@ -11,7 +11,7 @@ @interface CommentsViewController () -@property (weak, nonatomic) IBOutlet UITableView *commentsTableView; +@property(weak, nonatomic) IBOutlet UITableView *commentsTableView; @property NSArray *comments; @@ -20,49 +20,55 @@ @interface CommentsViewController () @implementation CommentsViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.comments = [NSArray array]; - - self.commentsTableView.dataSource = self; - self.commentsTableView.estimatedRowHeight = 44; - self.commentsTableView.rowHeight = UITableViewAutomaticDimension; - [self.commentsTableView registerNib:[UINib nibWithNibName:@"MyCommentTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyCommentTableViewCell"]; - - BVCommentsRequest *request = [[BVCommentsRequest alloc] initWithReviewId:@"192548" limit:99 offset:0]; - - [request load:^(BVCommentsResponse * _Nonnull response) { - // success - self.comments = response.results; - [self.commentsTableView reloadData]; - - } failure:^(NSArray * _Nonnull errors) { - // error - NSLog(@"ERROR Loading Comments: %@", errors.firstObject.localizedDescription); - }]; - + [super viewDidLoad]; + + self.comments = [NSArray array]; + + self.commentsTableView.dataSource = self; + self.commentsTableView.estimatedRowHeight = 44; + self.commentsTableView.rowHeight = UITableViewAutomaticDimension; + [self.commentsTableView + registerNib:[UINib nibWithNibName:@"MyCommentTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyCommentTableViewCell"]; + + BVCommentsRequest *request = + [[BVCommentsRequest alloc] initWithReviewId:@"192548" limit:99 offset:0]; + + [request load:^(BVCommentsResponse *_Nonnull response) { + // success + self.comments = response.results; + [self.commentsTableView reloadData]; + } + failure:^(NSArray *_Nonnull errors) { + // error + NSLog(@"ERROR Loading Comments: %@", + errors.firstObject.localizedDescription); + }]; } #pragma mark UITableViewDatasource --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - return @"Comments"; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return @"Comments"; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [self.comments count]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.comments count]; } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - MyCommentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCommentTableViewCell"]; - - cell.comment = [self.comments objectAtIndex:indexPath.row]; - - return cell; -} +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + MyCommentTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyCommentTableViewCell"]; + cell.comment = [self.comments objectAtIndex:indexPath.row]; + + return cell; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/InlineRatingsViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/InlineRatingsViewController.m index 06c4461a..0988ae4f 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/InlineRatingsViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/InlineRatingsViewController.m @@ -10,7 +10,7 @@ @interface InlineRatingsViewController () -@property (weak, nonatomic) IBOutlet UITableView *inlineReviewsTableView; +@property(weak, nonatomic) IBOutlet UITableView *inlineReviewsTableView; @property NSArray *productStatistics; @end @@ -18,58 +18,83 @@ @interface InlineRatingsViewController () @implementation InlineRatingsViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.productStatistics = [NSArray array]; - - self.inlineReviewsTableView.dataSource = self; - self.inlineReviewsTableView.dataSource = self; - self.inlineReviewsTableView.estimatedRowHeight = 68; - self.inlineReviewsTableView.rowHeight = UITableViewAutomaticDimension; - [self.inlineReviewsTableView registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" bundle:nil] forCellReuseIdentifier:@"StatisticTableViewCell"]; - - NSArray *productIds = @[@"test1", @"test2",@"test3", @"test4", @"test5", @"test6"]; - - BVBulkRatingsRequest* request = [[BVBulkRatingsRequest alloc] initWithProductIds:productIds statistics:BulkRatingsStatsTypeAll]; - - [request load:^(BVBulkRatingsResponse * _Nonnull response) { - self.productStatistics = response.results; - [self.inlineReviewsTableView reloadData]; - } failure:^(NSArray * _Nonnull errors) { - NSLog(@"ERROR: %@", errors.description); - }]; + [super viewDidLoad]; + + self.productStatistics = [NSArray array]; + + self.inlineReviewsTableView.dataSource = self; + self.inlineReviewsTableView.dataSource = self; + self.inlineReviewsTableView.estimatedRowHeight = 68; + self.inlineReviewsTableView.rowHeight = UITableViewAutomaticDimension; + [self.inlineReviewsTableView + registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"StatisticTableViewCell"]; + NSArray *productIds = + @[ @"test1", @"test2", @"test3", @"test4", @"test5", @"test6" ]; + + BVBulkRatingsRequest *request = + [[BVBulkRatingsRequest alloc] initWithProductIds:productIds + statistics:BulkRatingsStatsTypeAll]; + + [request load:^(BVBulkRatingsResponse *_Nonnull response) { + self.productStatistics = response.results; + [self.inlineReviewsTableView reloadData]; + } + failure:^(NSArray *_Nonnull errors) { + NSLog(@"ERROR: %@", errors.description); + }]; } #pragma mark UITableViewDatasource --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - return @"Inline Review Responses"; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return @"Inline Review Responses"; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [self.productStatistics count]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.productStatistics count]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - StatisticTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; - - - cell.statTypeLabel.text = [NSString stringWithFormat:@"Product ID: %@", [self.productStatistics objectAtIndex:indexPath.row].productId]; - long totalReivewCount = [[self.productStatistics objectAtIndex:indexPath.row].reviewStatistics.totalReviewCount longValue]; - long averageOverallRating = [[self.productStatistics objectAtIndex:indexPath.row].reviewStatistics.averageOverallRating longValue]; - long overallRatingRange = [[self.productStatistics objectAtIndex:indexPath.row].reviewStatistics.overallRatingRange longValue]; - - cell.statValueLabel.text = [NSString stringWithFormat:@"Total Reivew Count: %ld\nAverage Overall Rating: %ld\nOverall Rating Range: %ld", totalReivewCount,averageOverallRating,overallRatingRange]; - - return cell; -} + StatisticTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; + + cell.statTypeLabel.text = [NSString + stringWithFormat:@"Product ID: %@", + [self.productStatistics objectAtIndex:indexPath.row] + .productId]; + long totalReivewCount = [[self.productStatistics objectAtIndex:indexPath.row] + .reviewStatistics.totalReviewCount longValue]; + long averageOverallRating = + [[self.productStatistics objectAtIndex:indexPath.row] + .reviewStatistics.averageOverallRating longValue]; + long overallRatingRange = + [[self.productStatistics objectAtIndex:indexPath.row] + .reviewStatistics.overallRatingRange longValue]; + cell.statValueLabel.text = + [NSString stringWithFormat:@"Total Reivew Count: %ld\nAverage Overall " + @"Rating: %ld\nOverall Rating Range: %ld", + totalReivewCount, averageOverallRating, + overallRatingRange]; -// cell.statValueLabel.text = "Total Review Count(\(productStatistics[indexPath.row].reviewStatistics!.totalReviewCount!.stringValue)), \nAverage Overall Rating(\(productStatistics[indexPath.row].reviewStatistics!.averageOverallRating!.stringValue)), \nOverall Rating Range(\(productStatistics[indexPath.row].reviewStatistics!.overallRatingRange!.stringValue)) " -// + return cell; +} + +// cell.statValueLabel.text = "Total Review +// Count(\(productStatistics[indexPath.row].reviewStatistics!.totalReviewCount!.stringValue)), +// \nAverage Overall +// Rating(\(productStatistics[indexPath.row].reviewStatistics!.averageOverallRating!.stringValue)), +// \nOverall Rating +// Range(\(productStatistics[indexPath.row].reviewStatistics!.overallRatingRange!.stringValue)) +// " +// // return cell //} diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyAnswerTableViewCell.m b/Examples/Conversations/Obj-C/ConversationsExample/MyAnswerTableViewCell.m index 714b2708..da66749b 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyAnswerTableViewCell.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyAnswerTableViewCell.m @@ -7,20 +7,18 @@ #import "MyAnswerTableViewCell.h" -@interface MyAnswerTableViewCell() +@interface MyAnswerTableViewCell () -@property (weak, nonatomic) IBOutlet UILabel *answerTestLabel; +@property(weak, nonatomic) IBOutlet UILabel *answerTestLabel; @end @implementation MyAnswerTableViewCell +- (void)setAnswer:(BVAnswer *)answer { -- (void)setAnswer:(BVAnswer *)answer{ - - super.answer = answer; - self.answerTestLabel.text = answer.answerText; - + super.answer = answer; + self.answerTestLabel.text = answer.answerText; } @end \ No newline at end of file diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.h b/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.h index 7f9161df..13338be5 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.h +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.h @@ -11,6 +11,6 @@ @interface MyCommentTableViewCell : UITableViewCell -@property (nonatomic, strong) BVComment *comment; +@property(nonatomic, strong) BVComment *comment; @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.m b/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.m index 3be03a4c..bdf262ce 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyCommentTableViewCell.m @@ -9,25 +9,22 @@ @interface MyCommentTableViewCell () -@property (weak, nonatomic) IBOutlet UILabel *commentTitle; -@property (weak, nonatomic) IBOutlet UILabel *commentText; - +@property(weak, nonatomic) IBOutlet UILabel *commentTitle; +@property(weak, nonatomic) IBOutlet UILabel *commentText; @end - @implementation MyCommentTableViewCell @synthesize comment = _comment; -- (void)setComment:(BVComment *)comment{ - - _comment = comment; - self.commentTitle.text = self.comment.title ? self.comment.title : @"No title"; - self.commentText.text = self.comment.commentText ? self.comment.commentText : @"No comment text"; - -} - +- (void)setComment:(BVComment *)comment { + _comment = comment; + self.commentTitle.text = + self.comment.title ? self.comment.title : @"No title"; + self.commentText.text = + self.comment.commentText ? self.comment.commentText : @"No comment text"; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyQuestionTableViewCell.m b/Examples/Conversations/Obj-C/ConversationsExample/MyQuestionTableViewCell.m index 4ec4835c..20c7d271 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyQuestionTableViewCell.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyQuestionTableViewCell.m @@ -7,22 +7,22 @@ #import "MyQuestionTableViewCell.h" -@interface MyQuestionTableViewCell() +@interface MyQuestionTableViewCell () -@property (weak, nonatomic) IBOutlet UILabel *questionSummary; -@property (weak, nonatomic) IBOutlet UILabel *questionDetails; +@property(weak, nonatomic) IBOutlet UILabel *questionSummary; +@property(weak, nonatomic) IBOutlet UILabel *questionDetails; @end @implementation MyQuestionTableViewCell +- (void)setQuestion:(BVQuestion *)question { -- (void)setQuestion:(BVQuestion *)question{ - - super.question = question; - self.questionSummary.text = [NSString stringWithFormat:@"%@ (%lu Answers)", question.questionSummary, (unsigned long)question.answers.count]; - self.questionDetails.text = question.questionDetails; - + super.question = question; + self.questionSummary.text = + [NSString stringWithFormat:@"%@ (%lu Answers)", question.questionSummary, + (unsigned long)question.answers.count]; + self.questionDetails.text = question.questionDetails; } @end \ No newline at end of file diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.h b/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.h index 8ba88417..dc3ef430 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.h +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.h @@ -9,5 +9,4 @@ @interface MyReviewTableViewCell : BVReviewTableViewCell - @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.m b/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.m index c83eba83..350dc7d9 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/MyReviewTableViewCell.m @@ -7,22 +7,20 @@ #import "MyReviewTableViewCell.h" -@interface MyReviewTableViewCell() +@interface MyReviewTableViewCell () -@property (weak, nonatomic) IBOutlet UILabel *reviewTitle; -@property (weak, nonatomic) IBOutlet UILabel *reviewText; +@property(weak, nonatomic) IBOutlet UILabel *reviewTitle; +@property(weak, nonatomic) IBOutlet UILabel *reviewText; @end @implementation MyReviewTableViewCell +- (void)setReview:(BVReview *)review { -- (void)setReview:(BVReview *)review{ - - super.review = review; - self.reviewTitle.text = review.title; - self.reviewText.text = review.reviewText; - + super.review = review; + self.reviewTitle.text = review.title; + self.reviewText.text = review.reviewText; } @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/PDPViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/PDPViewController.m index e4446a5b..660d6ef3 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/PDPViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/PDPViewController.m @@ -8,210 +8,220 @@ #import "PDPViewController.h" #import "StatisticTableViewCell.h" -typedef enum { - ReviewStats = 0, - QAStats, - StatSectionsCount -} StatSections; - +typedef enum { ReviewStats = 0, QAStats, StatSectionsCount } StatSections; typedef enum { - TotalReviewCount = 0, - AverageOverallRating, - HelpfulVoteCount, - NotHelpfulVoteCount, - RecommendedCount, - NotRecommendedCount, - OverallRatingRange, - ReviewStateRowsCount -}ReviewStateRows; - + TotalReviewCount = 0, + AverageOverallRating, + HelpfulVoteCount, + NotHelpfulVoteCount, + RecommendedCount, + NotRecommendedCount, + OverallRatingRange, + ReviewStateRowsCount +} ReviewStateRows; typedef enum { - TotalQuestions = 0, - TotalAnswers, - AnswerHelpfulVoteCount, - AnswerNotHelpfulVoteCount, - QuestionHelpfulVoteCount, - QuestionNotHelpfulVoteCount, - QAStatRowsCount -}QAStatRows; - + TotalQuestions = 0, + TotalAnswers, + AnswerHelpfulVoteCount, + AnswerNotHelpfulVoteCount, + QuestionHelpfulVoteCount, + QuestionNotHelpfulVoteCount, + QAStatRowsCount +} QAStatRows; @interface PDPViewController () -@property (weak, nonatomic) IBOutlet UITableView *demoStatsTableView; +@property(weak, nonatomic) IBOutlet UITableView *demoStatsTableView; @property BVReviewStatistics *reviewStatistics; @property BVQAStatistics *questionAnswerStats; @end - @implementation PDPViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.demoStatsTableView.estimatedRowHeight = 80; - self.demoStatsTableView.rowHeight = UITableViewAutomaticDimension; - [self.demoStatsTableView registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" bundle:nil] forCellReuseIdentifier:@"StatisticTableViewCell"]; - - BVProductDisplayPageRequest* request = [[BVProductDisplayPageRequest alloc] initWithProductId:@"test1"]; - [request includeStatistics:PDPContentTypeReviews]; - [request includeStatistics:PDPContentTypeAnswers]; - [request includeStatistics:PDPContentTypeQuestions]; - [request includeContent:PDPContentTypeQuestions limit:10]; - [request sortIncludedQuestions:BVSortOptionQuestionsTotalAnswerCount order:BVSortOrderDescending]; - - [request load:^(BVProductsResponse * _Nonnull response) { - - self.reviewStatistics = response.result.reviewStatistics; - self.questionAnswerStats = response.result.qaStatistics; - [self.demoStatsTableView reloadData]; - - } failure:^(NSArray * _Nonnull errors) { + [super viewDidLoad]; + + self.demoStatsTableView.estimatedRowHeight = 80; + self.demoStatsTableView.rowHeight = UITableViewAutomaticDimension; + [self.demoStatsTableView + registerNib:[UINib nibWithNibName:@"StatisticTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"StatisticTableViewCell"]; + + BVProductDisplayPageRequest *request = + [[BVProductDisplayPageRequest alloc] initWithProductId:@"test1"]; + [request includeStatistics:PDPContentTypeReviews]; + [request includeStatistics:PDPContentTypeAnswers]; + [request includeStatistics:PDPContentTypeQuestions]; + [request includeContent:PDPContentTypeQuestions limit:10]; + [request sortIncludedQuestions:BVSortOptionQuestionsTotalAnswerCount + order:BVSortOrderDescending]; + + [request load:^(BVProductsResponse *_Nonnull response) { + + self.reviewStatistics = response.result.reviewStatistics; + self.questionAnswerStats = response.result.qaStatistics; + [self.demoStatsTableView reloadData]; + + } + failure:^(NSArray *_Nonnull errors) { NSLog(@"ERROR: %@", errors.description); - }]; - + }]; } #pragma mark UITableViewDatasource --(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ - return StatSectionsCount; +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return StatSectionsCount; } --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - switch (section) { - case ReviewStats: - return @"Product Review Statistics"; - break; - case QAStats: - return @"Product Question & Answer Statistics"; - break; - - - default: - break; - } - - return @""; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + switch (section) { + case ReviewStats: + return @"Product Review Statistics"; + break; + case QAStats: + return @"Product Question & Answer Statistics"; + break; + + default: + break; + } + + return @""; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - - switch (section) { - case ReviewStats: - return ReviewStateRowsCount; - break; - case QAStats: - return QAStatRowsCount; - break; - default: - break; - } - - return 0; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + + switch (section) { + case ReviewStats: + return ReviewStateRowsCount; + break; + case QAStats: + return QAStatRowsCount; + break; + default: + break; + } + + return 0; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + StatisticTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; + + switch (indexPath.section) { + case ReviewStats: + + // Review stats + switch (indexPath.row) { + case TotalReviewCount: + cell.statTypeLabel.text = @"Total Review Count"; + cell.statValueLabel.text = + [self.reviewStatistics.totalReviewCount stringValue]; + break; + + case AverageOverallRating: + cell.statTypeLabel.text = @"Average Overall Rating"; + cell.statValueLabel.text = + [self.reviewStatistics.averageOverallRating stringValue]; + break; + + case HelpfulVoteCount: + cell.statTypeLabel.text = @"Helpful Vote Count"; + cell.statValueLabel.text = + [self.reviewStatistics.helpfulVoteCount stringValue]; + break; + + case NotHelpfulVoteCount: + cell.statTypeLabel.text = @"Not Helpful Vote Count"; + cell.statValueLabel.text = + [self.reviewStatistics.notHelpfulVoteCount stringValue]; + break; + + case RecommendedCount: + cell.statTypeLabel.text = @"Recommended Count"; + cell.statValueLabel.text = + [self.reviewStatistics.recommendedCount stringValue]; + break; + + case NotRecommendedCount: + cell.statTypeLabel.text = @"Not Recommended Count"; + cell.statValueLabel.text = + [self.reviewStatistics.notRecommendedCount stringValue]; + break; + + case OverallRatingRange: + cell.statTypeLabel.text = @"Overall Rating Range"; + cell.statValueLabel.text = + [self.reviewStatistics.overallRatingRange stringValue]; + break; + + default: + break; + } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - StatisticTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"StatisticTableViewCell"]; - - switch (indexPath.section) { - case ReviewStats: - - // Review stats - switch (indexPath.row) { - case TotalReviewCount: - cell.statTypeLabel.text = @"Total Review Count"; - cell.statValueLabel.text = [self.reviewStatistics.totalReviewCount stringValue]; - break; - - case AverageOverallRating: - cell.statTypeLabel.text = @"Average Overall Rating"; - cell.statValueLabel.text = [self.reviewStatistics.averageOverallRating stringValue]; - break; - - case HelpfulVoteCount: - cell.statTypeLabel.text = @"Helpful Vote Count"; - cell.statValueLabel.text = [self.reviewStatistics.helpfulVoteCount stringValue]; - break; - - case NotHelpfulVoteCount: - cell.statTypeLabel.text = @"Not Helpful Vote Count"; - cell.statValueLabel.text = [self.reviewStatistics.notHelpfulVoteCount stringValue]; - break; - - case RecommendedCount: - cell.statTypeLabel.text = @"Recommended Count"; - cell.statValueLabel.text = [self.reviewStatistics.recommendedCount stringValue]; - break; - - case NotRecommendedCount: - cell.statTypeLabel.text = @"Not Recommended Count"; - cell.statValueLabel.text = [self.reviewStatistics.notRecommendedCount stringValue]; - break; - - case OverallRatingRange: - cell.statTypeLabel.text = @"Overall Rating Range"; - cell.statValueLabel.text = [self.reviewStatistics.overallRatingRange stringValue]; - break; - - default: - break; - } - - break; - - case QAStats: - - switch (indexPath.row) { - case TotalQuestions: - cell.statTypeLabel.text = @"Total Questions"; - cell.statValueLabel.text = [self.questionAnswerStats.totalQuestionCount stringValue]; - break; - - case TotalAnswers: - cell.statTypeLabel.text = @"Total Answers"; - cell.statValueLabel.text = [self.questionAnswerStats.totalAnswerCount stringValue]; - break; - - case AnswerHelpfulVoteCount: - cell.statTypeLabel.text = @"Answer Helpful Vote Count"; - cell.statValueLabel.text = [self.questionAnswerStats.answerHelpfulVoteCount stringValue]; - break; - - case AnswerNotHelpfulVoteCount: - cell.statTypeLabel.text = @"Answer Not Helpful Vote Count"; - cell.statValueLabel.text = [self.questionAnswerStats.answerNotHelpfulVoteCount stringValue]; - break; - - case QuestionHelpfulVoteCount: - cell.statTypeLabel.text = @"Question Helpful Vote Count"; - cell.statValueLabel.text = [self.questionAnswerStats.questionHelpfulVoteCount stringValue]; - break; - - case QuestionNotHelpfulVoteCount: - cell.statTypeLabel.text = @"Question Not Helpful Vote Count"; - cell.statValueLabel.text = [self.questionAnswerStats.questionNotHelpfulVoteCount stringValue]; - break; - - default: - break; - } - - break; - - default: - break; + break; + + case QAStats: + + switch (indexPath.row) { + case TotalQuestions: + cell.statTypeLabel.text = @"Total Questions"; + cell.statValueLabel.text = + [self.questionAnswerStats.totalQuestionCount stringValue]; + break; + + case TotalAnswers: + cell.statTypeLabel.text = @"Total Answers"; + cell.statValueLabel.text = + [self.questionAnswerStats.totalAnswerCount stringValue]; + break; + + case AnswerHelpfulVoteCount: + cell.statTypeLabel.text = @"Answer Helpful Vote Count"; + cell.statValueLabel.text = + [self.questionAnswerStats.answerHelpfulVoteCount stringValue]; + break; + + case AnswerNotHelpfulVoteCount: + cell.statTypeLabel.text = @"Answer Not Helpful Vote Count"; + cell.statValueLabel.text = + [self.questionAnswerStats.answerNotHelpfulVoteCount stringValue]; + break; + + case QuestionHelpfulVoteCount: + cell.statTypeLabel.text = @"Question Helpful Vote Count"; + cell.statValueLabel.text = + [self.questionAnswerStats.questionHelpfulVoteCount stringValue]; + break; + + case QuestionNotHelpfulVoteCount: + cell.statTypeLabel.text = @"Question Not Helpful Vote Count"; + cell.statValueLabel.text = + [self.questionAnswerStats.questionNotHelpfulVoteCount stringValue]; + break; + + default: + break; } - - return cell; -} + break; + default: + break; + } + + return cell; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/QuestionsViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/QuestionsViewController.m index ce195f42..79ce3cdc 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/QuestionsViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/QuestionsViewController.m @@ -6,79 +6,93 @@ // #import "QuestionsViewController.h" -#import "MyQuestionTableViewCell.h" #import "AnswersViewController.h" +#import "MyQuestionTableViewCell.h" @import BVSDK; -@interface QuestionsViewController () +@interface QuestionsViewController () -@property (weak, nonatomic) IBOutlet BVQuestionsTableView *questionsTableView; +@property(weak, nonatomic) IBOutlet BVQuestionsTableView *questionsTableView; @property NSArray *questions; @end - @implementation QuestionsViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.questions = [NSArray array]; - - self.questionsTableView.dataSource = self; - self.questionsTableView.estimatedRowHeight = 44; - self.questionsTableView.rowHeight = UITableViewAutomaticDimension; - [self.questionsTableView registerNib:[UINib nibWithNibName:@"MyQuestionTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyQuestionTableViewCell"]; - - BVQuestionsAndAnswersRequest* request = [[BVQuestionsAndAnswersRequest alloc] initWithProductId:@"test1" limit:20 offset:0]; - // optionally add in a sort option - [request addQuestionSort:BVSortOptionQuestionsLastModeratedTime order:BVSortOrderDescending]; - // optionally add in a filter - [request addFilter:BVQuestionFilterTypeHasAnswers filterOperator:BVFilterOperatorEqualTo value:@"true"]; - - [self.questionsTableView load:request success:^(BVQuestionsAndAnswersResponse * _Nonnull response) { + [super viewDidLoad]; + + self.questions = [NSArray array]; + + self.questionsTableView.dataSource = self; + self.questionsTableView.estimatedRowHeight = 44; + self.questionsTableView.rowHeight = UITableViewAutomaticDimension; + [self.questionsTableView + registerNib:[UINib nibWithNibName:@"MyQuestionTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyQuestionTableViewCell"]; + + BVQuestionsAndAnswersRequest *request = + [[BVQuestionsAndAnswersRequest alloc] initWithProductId:@"test1" + limit:20 + offset:0]; + // optionally add in a sort option + [request addQuestionSort:BVSortOptionQuestionsLastModeratedTime + order:BVSortOrderDescending]; + // optionally add in a filter + [request addFilter:BVQuestionFilterTypeHasAnswers + filterOperator:BVFilterOperatorEqualTo + value:@"true"]; + + [self.questionsTableView load:request + success:^(BVQuestionsAndAnswersResponse *_Nonnull response) { self.questions = response.results; [self.questionsTableView reloadData]; - } failure:^(NSArray * _Nonnull errors) { + } + failure:^(NSArray *_Nonnull errors) { NSLog(@"Error loading quesitons"); - }]; - + }]; } - #pragma mark UITableViewDatasource --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - return @"Questions Responses"; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return @"Questions Responses"; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [self.questions count]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.questions count]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + MyQuestionTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyQuestionTableViewCell"]; -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - MyQuestionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyQuestionTableViewCell"]; - - cell.question = [self.questions objectAtIndex:indexPath.row]; - - return cell; + cell.question = [self.questions objectAtIndex:indexPath.row]; + + return cell; } #pragma mark UITableViewDelegate -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - BVQuestion *question = [self.questions objectAtIndex:indexPath.row]; - - UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; - AnswersViewController *answersVC = [sb instantiateViewControllerWithIdentifier:@"AnswersViewController"]; - answersVC.question = question; - - [self.navigationController pushViewController:answersVC animated:YES]; +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + BVQuestion *question = [self.questions objectAtIndex:indexPath.row]; + + UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; + AnswersViewController *answersVC = + [sb instantiateViewControllerWithIdentifier:@"AnswersViewController"]; + answersVC.question = question; + + [self.navigationController pushViewController:answersVC animated:YES]; } @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.h b/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.h index 4c8800e2..3e59c7e8 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.h +++ b/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.h @@ -7,7 +7,6 @@ #import - @interface ReviewsViewController : UIViewController @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.m index e5760f8e..c9409925 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/ReviewsViewController.m @@ -12,57 +12,64 @@ @interface ReviewsViewController () -@property (weak, nonatomic) IBOutlet BVReviewsTableView *reviewsTableView; +@property(weak, nonatomic) IBOutlet BVReviewsTableView *reviewsTableView; @property NSArray *reviews; @end - @implementation ReviewsViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.reviews = [NSArray array]; - - self.reviewsTableView.dataSource = self; - self.reviewsTableView.estimatedRowHeight = 44; - self.reviewsTableView.rowHeight = UITableViewAutomaticDimension; - [self.reviewsTableView registerNib:[UINib nibWithNibName:@"MyReviewTableViewCell" bundle:nil] forCellReuseIdentifier:@"MyReviewTableViewCell"]; - - BVReviewsRequest* request = [[BVReviewsRequest alloc] initWithProductId:@"test1" limit:20 offset:0]; - [request addReviewSort:BVSortOptionReviewsSubmissionTime order:BVSortOrderDescending]; - [request addInclude:BVReviewIncludeTypeComments]; - - [self.reviewsTableView load:request success:^(BVReviewsResponse * _Nonnull response) { + [super viewDidLoad]; + + self.reviews = [NSArray array]; + + self.reviewsTableView.dataSource = self; + self.reviewsTableView.estimatedRowHeight = 44; + self.reviewsTableView.rowHeight = UITableViewAutomaticDimension; + [self.reviewsTableView + registerNib:[UINib nibWithNibName:@"MyReviewTableViewCell" + bundle:nil] + forCellReuseIdentifier:@"MyReviewTableViewCell"]; + + BVReviewsRequest *request = + [[BVReviewsRequest alloc] initWithProductId:@"test1" limit:20 offset:0]; + [request addReviewSort:BVSortOptionReviewsSubmissionTime + order:BVSortOrderDescending]; + [request addInclude:BVReviewIncludeTypeComments]; + + [self.reviewsTableView load:request + success:^(BVReviewsResponse *_Nonnull response) { self.reviews = response.results; [self.reviewsTableView reloadData]; - } failure:^(NSArray * _Nonnull errors) { + } + failure:^(NSArray *_Nonnull errors) { NSLog(@"Error loading reviews"); - }]; - + }]; } - #pragma mark UITableViewDatasource --(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ - return @"Review Responses"; +- (NSString *)tableView:(UITableView *)tableView + titleForHeaderInSection:(NSInteger)section { + return @"Review Responses"; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [self.reviews count]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [self.reviews count]; } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - MyReviewTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyReviewTableViewCell"]; +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { - cell.review = [self.reviews objectAtIndex:indexPath.row]; + MyReviewTableViewCell *cell = + [tableView dequeueReusableCellWithIdentifier:@"MyReviewTableViewCell"]; - return cell; -} + cell.review = [self.reviews objectAtIndex:indexPath.row]; + return cell; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/RootViewController.m b/Examples/Conversations/Obj-C/ConversationsExample/RootViewController.m index e20e0f0b..54d75247 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/RootViewController.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/RootViewController.m @@ -15,166 +15,178 @@ @interface RootViewController () @implementation RootViewController - (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. + [super viewDidLoad]; + // Do any additional setup after loading the view. } - (IBAction)submitReviewTapped:(id)sender { - - BVReviewSubmission* review = [[BVReviewSubmission alloc] initWithReviewTitle:@"Review title goes here" - reviewText:@"Review text goes here. Super cali fragilistic expiali docious!!!" - rating:4 - productId:@"test1"]; - - review.userId = [NSString stringWithFormat:@"userId%d", arc4random()]; // add in a random user id for testing, avoids duplicate errors - review.action = BVSubmissionActionPreview; - review.userNickname = @"shazbat"; - review.userEmail = @"foo@bar.com"; - review.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; - review.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; - - // user added a photo to this review - [review addPhoto:[UIImage imageNamed:@"puppy"] withPhotoCaption:@"What a cute pupper!"]; - - [review submit:^(BVReviewSubmissionResponse * _Nonnull response) { - [self showSuccess:@"Success Submitting Review!"]; - } failure:^(NSArray * _Nonnull errors) { + + BVReviewSubmission *review = [[BVReviewSubmission alloc] + initWithReviewTitle:@"Review title goes here" + reviewText:@"Review text goes here. Super cali fragilistic " + @"expiali docious!!!" + rating:4 + productId:@"test1"]; + + review.userId = [NSString + stringWithFormat:@"userId%d", arc4random()]; // add in a random user id + // for testing, avoids + // duplicate errors + review.action = BVSubmissionActionPreview; + review.userNickname = @"shazbat"; + review.userEmail = @"foo@bar.com"; + review.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; + review.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; + + // user added a photo to this review + [review addPhoto:[UIImage imageNamed:@"puppy"] + withPhotoCaption:@"What a cute pupper!"]; + + [review submit:^(BVReviewSubmissionResponse *_Nonnull response) { + [self showSuccess:@"Success Submitting Review!"]; + } + failure:^(NSArray *_Nonnull errors) { [self showError:errors.description]; - }]; - + }]; } - - (IBAction)submitQuestionTapped:(id)sender { - - BVQuestionSubmission* question = [[BVQuestionSubmission alloc] initWithProductId:@"test1"]; - question.action = BVSubmissionActionPreview; - - NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; - - question.questionSummary = @"Question Summary"; - question.questionDetails = @"Question details..."; - question.userEmail = @"foo@bar.com"; - question.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; - question.userId = [NSString stringWithFormat:@"UserId%@", randomId]; - question.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; - question.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; - - [question submit:^(BVQuestionSubmissionResponse * _Nonnull response) { - [self showSuccess:@"Success Submitting Question!"]; - } failure:^(NSArray * _Nonnull errors) { + + BVQuestionSubmission *question = + [[BVQuestionSubmission alloc] initWithProductId:@"test1"]; + question.action = BVSubmissionActionPreview; + + NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; + + question.questionSummary = @"Question Summary"; + question.questionDetails = @"Question details..."; + question.userEmail = @"foo@bar.com"; + question.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; + question.userId = [NSString stringWithFormat:@"UserId%@", randomId]; + question.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; + question.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; + + [question submit:^(BVQuestionSubmissionResponse *_Nonnull response) { + [self showSuccess:@"Success Submitting Question!"]; + } + failure:^(NSArray *_Nonnull errors) { [self showError:errors.description]; - }]; - + }]; } - - (IBAction)submitAnswerTapped:(id)sender { - - BVAnswerSubmission* answer = [[BVAnswerSubmission alloc] initWithQuestionId:@"14679" answerText:@"User answer text goes here...."]; - answer.action = BVSubmissionActionPreview; - - NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; - - answer.userEmail = @"foo@bar.com"; - answer.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; - answer.userId = [NSString stringWithFormat:@"UserId%@", randomId]; - answer.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; - answer.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; - - [answer submit:^(BVAnswerSubmissionResponse * _Nonnull response) { - [self showSuccess:@"Success Submitting Answer!"]; - } failure:^(NSArray * _Nonnull errors) { + + BVAnswerSubmission *answer = [[BVAnswerSubmission alloc] + initWithQuestionId:@"14679" + answerText:@"User answer text goes here...."]; + answer.action = BVSubmissionActionPreview; + + NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; + + answer.userEmail = @"foo@bar.com"; + answer.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; + answer.userId = [NSString stringWithFormat:@"UserId%@", randomId]; + answer.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; + answer.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; + + [answer submit:^(BVAnswerSubmissionResponse *_Nonnull response) { + [self showSuccess:@"Success Submitting Answer!"]; + } + failure:^(NSArray *_Nonnull errors) { [self showError:errors.description]; - }]; - + }]; } - (IBAction)submitFeedbackTapped:(id)sender { - - BVFeedbackSubmission *feedback = [[BVFeedbackSubmission alloc] initWithContentId:@"192451" withContentType:BVFeedbackContentTypeReview withFeedbackType:BVFeedbackTypeHelpfulness]; - - feedback.userId = [NSString stringWithFormat:@"userId%d", arc4random()]; // add in a random user id for testing, avoids duplicate errors - feedback.vote = BVFeedbackVotePositive; - - [feedback submit:^(BVFeedbackSubmissionResponse * _Nonnull response) { - // success - [self showSuccess:@"Success Submitting Feedback!"]; - } failure:^(NSArray * _Nonnull errors) { + + BVFeedbackSubmission *feedback = [[BVFeedbackSubmission alloc] + initWithContentId:@"192451" + withContentType:BVFeedbackContentTypeReview + withFeedbackType:BVFeedbackTypeHelpfulness]; + + feedback.userId = [NSString + stringWithFormat:@"userId%d", arc4random()]; // add in a random user id + // for testing, avoids + // duplicate errors + feedback.vote = BVFeedbackVotePositive; + + [feedback submit:^(BVFeedbackSubmissionResponse *_Nonnull response) { + // success + [self showSuccess:@"Success Submitting Feedback!"]; + } + failure:^(NSArray *_Nonnull errors) { // error - [self showError:errors.description]; - }]; - + [self showError:errors.description]; + }]; } - (IBAction)submitReviewCommentTapped:(id)sender { - - NSString *commentText = @"I love comments almost as much as Objective-C! They are just the most! Seriously!"; - NSString *commentTitle = @"Comments Can We Written In Objective-C"; - - BVCommentSubmission *submission = [[BVCommentSubmission alloc] initWithReviewId:@"192550" withCommentText:commentText]; - - NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; - - //commentRequest.fingerPrint = // the iovation fingerprint would go here... - submission.action = BVSubmissionActionPreview; - submission.campaignId = @"BV_COMMENT_CAMPAIGN_ID"; - submission.commentTitle = commentTitle; - submission.locale = @"en_US"; - submission.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; - submission.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; - submission.userId = [NSString stringWithFormat:@"UserId%@", randomId]; - submission.userEmail = @"developer@bazaarvoice.com"; - submission.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; - - // user added a photo to this review - //[submission addPhoto:[UIImage imageNamed:@"puppy"] withPhotoCaption:@"What a cute pupper!"]; - - [submission submit:^(BVCommentSubmissionResponse * _Nonnull response) { - // success - [self showSuccess:@"Success Submitting Feedback!"]; - } failure:^(NSArray * _Nonnull errors) { + + NSString *commentText = @"I love comments almost as much as Objective-C! " + @"They are just the most! Seriously!"; + NSString *commentTitle = @"Comments Can We Written In Objective-C"; + + BVCommentSubmission *submission = + [[BVCommentSubmission alloc] initWithReviewId:@"192550" + withCommentText:commentText]; + + NSString *randomId = [NSString stringWithFormat:@"userId%u", arc4random()]; + + // commentRequest.fingerPrint = // the iovation fingerprint would go here... + submission.action = BVSubmissionActionPreview; + submission.campaignId = @"BV_COMMENT_CAMPAIGN_ID"; + submission.commentTitle = commentTitle; + submission.locale = @"en_US"; + submission.sendEmailAlertWhenPublished = [NSNumber numberWithBool:YES]; + submission.userNickname = [NSString stringWithFormat:@"UserNick%@", randomId]; + submission.userId = [NSString stringWithFormat:@"UserId%@", randomId]; + submission.userEmail = @"developer@bazaarvoice.com"; + submission.agreedToTermsAndConditions = [NSNumber numberWithBool:YES]; + + // user added a photo to this review + //[submission addPhoto:[UIImage imageNamed:@"puppy"] withPhotoCaption:@"What a + // cute pupper!"]; + + [submission submit:^(BVCommentSubmissionResponse *_Nonnull response) { + // success + [self showSuccess:@"Success Submitting Feedback!"]; + } + failure:^(NSArray *_Nonnull errors) { // error [self showError:errors.description]; - }]; - + }]; } - - (void)showSuccess:(NSString *)message { - - UIAlertController * alert = [UIAlertController - alertControllerWithTitle:@"Success" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* okButon = [UIAlertAction - actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:nil]; - [alert addAction:okButon]; - - [self presentViewController:alert animated:YES completion:nil]; - -} + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:@"Success" + message:message + preferredStyle:UIAlertControllerStyleAlert]; -- (void)showError:(NSString *)message { - - UIAlertController * alert = [UIAlertController - alertControllerWithTitle:@"Error" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* okButon = [UIAlertAction - actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:nil]; - [alert addAction:okButon]; - - [self presentViewController:alert animated:YES completion:nil]; - + UIAlertAction *okButon = + [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:nil]; + [alert addAction:okButon]; + + [self presentViewController:alert animated:YES completion:nil]; } +- (void)showError:(NSString *)message { + + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:@"Error" + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *okButon = + [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:nil]; + [alert addAction:okButon]; + + [self presentViewController:alert animated:YES completion:nil]; +} @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/StatisticTableViewCell.h b/Examples/Conversations/Obj-C/ConversationsExample/StatisticTableViewCell.h index ee905566..6e93b310 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/StatisticTableViewCell.h +++ b/Examples/Conversations/Obj-C/ConversationsExample/StatisticTableViewCell.h @@ -9,7 +9,7 @@ @interface StatisticTableViewCell : UITableViewCell -@property (weak, nonatomic) IBOutlet UILabel *statTypeLabel; -@property (weak, nonatomic) IBOutlet UILabel *statValueLabel; +@property(weak, nonatomic) IBOutlet UILabel *statTypeLabel; +@property(weak, nonatomic) IBOutlet UILabel *statValueLabel; @end diff --git a/Examples/Conversations/Obj-C/ConversationsExample/main.m b/Examples/Conversations/Obj-C/ConversationsExample/main.m index ac52c0fe..a40c7753 100644 --- a/Examples/Conversations/Obj-C/ConversationsExample/main.m +++ b/Examples/Conversations/Obj-C/ConversationsExample/main.m @@ -5,11 +5,12 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "AppDelegate.h" +#import -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, + NSStringFromClass([AppDelegate class])); + } } diff --git a/Examples/Curations/Obj-C/CurationsExample/AppDelegate.h b/Examples/Curations/Obj-C/CurationsExample/AppDelegate.h index 6e60889b..1d0b5641 100644 --- a/Examples/Curations/Obj-C/CurationsExample/AppDelegate.h +++ b/Examples/Curations/Obj-C/CurationsExample/AppDelegate.h @@ -9,7 +9,6 @@ @interface AppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; +@property(strong, nonatomic) UIWindow *window; @end - diff --git a/Examples/Curations/Obj-C/CurationsExample/AppDelegate.m b/Examples/Curations/Obj-C/CurationsExample/AppDelegate.m index b14c5aa8..c0edd0f2 100644 --- a/Examples/Curations/Obj-C/CurationsExample/AppDelegate.m +++ b/Examples/Curations/Obj-C/CurationsExample/AppDelegate.m @@ -14,14 +14,14 @@ @interface AppDelegate () @implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. #warning See bvsdk_config_staging.json and bvsdk_config_product.json in the project for API key and client ID settings. - [BVSDKManager configure:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; - - return YES; + [BVSDKManager configure:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; + + return YES; } @end diff --git a/Examples/Curations/Obj-C/CurationsExample/ViewController.h b/Examples/Curations/Obj-C/CurationsExample/ViewController.h index c995ad84..a4fbf65c 100644 --- a/Examples/Curations/Obj-C/CurationsExample/ViewController.h +++ b/Examples/Curations/Obj-C/CurationsExample/ViewController.h @@ -9,6 +9,4 @@ @interface ViewController : UIViewController - @end - diff --git a/Examples/Curations/Obj-C/CurationsExample/ViewController.m b/Examples/Curations/Obj-C/CurationsExample/ViewController.m index 1bd21da4..71368c04 100644 --- a/Examples/Curations/Obj-C/CurationsExample/ViewController.m +++ b/Examples/Curations/Obj-C/CurationsExample/ViewController.m @@ -5,7 +5,6 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import "ViewController.h" #import "ViewController.h" #import @@ -13,10 +12,12 @@ @interface ViewController () -@property (weak, nonatomic) IBOutlet BVCurationsUICollectionView *curationsCollectionView; -@property (weak, nonatomic) IBOutlet UIStepper *stepper; -@property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraintCarousel; -@property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraintGrid; +@property(weak, nonatomic) + IBOutlet BVCurationsUICollectionView *curationsCollectionView; +@property(weak, nonatomic) IBOutlet UIStepper *stepper; +@property(strong, nonatomic) + IBOutlet NSLayoutConstraint *heightConstraintCarousel; +@property(strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstraintGrid; @property(nonatomic, strong) SDWebImageManager *sdMngr; @end @@ -25,139 +26,161 @@ @interface ViewController () @implementation ViewController - (void)viewDidLoad { - [super viewDidLoad]; - - // Set up the Curations UI display properties - self.curationsCollectionView.curationsDelegate = self; - self.curationsCollectionView.groups = @[@"__all__"]; - self.curationsCollectionView.fetchSize = 40; - self.curationsCollectionView.infiniteScrollEnabled = YES; - self.curationsCollectionView.itemsPerRow = kNumRowsStart; - self.curationsCollectionView.backgroundColor = [UIColor lightGrayColor]; - self.curationsCollectionView.bvCurationsUILayout = BVCurationsUILayoutGrid; - [self.curationsCollectionView loadFeed]; - - _stepper.value = kNumRowsStart; - - SDImageCache *cache = [[SDImageCache alloc]initWithNamespace:@"bvcurationsdemo"]; - SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; - downloader.maxConcurrentDownloads = NSIntegerMax; - downloader.executionOrder = SDWebImageDownloaderLIFOExecutionOrder; - _sdMngr = [[SDWebImageManager alloc]initWithCache:cache downloader:downloader]; - - UIBarButtonItem *submitButton = [[UIBarButtonItem alloc] - initWithTitle:@"Add Photo" - style:UIBarButtonItemStylePlain - target:self - action:@selector(didTapAddPhotoButton)]; - - self.navigationItem.rightBarButtonItem = submitButton; + [super viewDidLoad]; + + // Set up the Curations UI display properties + self.curationsCollectionView.curationsDelegate = self; + self.curationsCollectionView.groups = @[ @"__all__" ]; + self.curationsCollectionView.fetchSize = 40; + self.curationsCollectionView.infiniteScrollEnabled = YES; + self.curationsCollectionView.itemsPerRow = kNumRowsStart; + self.curationsCollectionView.backgroundColor = [UIColor lightGrayColor]; + self.curationsCollectionView.bvCurationsUILayout = BVCurationsUILayoutGrid; + [self.curationsCollectionView loadFeed]; + + _stepper.value = kNumRowsStart; + + SDImageCache *cache = + [[SDImageCache alloc] initWithNamespace:@"bvcurationsdemo"]; + SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] + initWithSessionConfiguration:[NSURLSessionConfiguration + defaultSessionConfiguration]]; + downloader.maxConcurrentDownloads = NSIntegerMax; + downloader.executionOrder = SDWebImageDownloaderLIFOExecutionOrder; + _sdMngr = + [[SDWebImageManager alloc] initWithCache:cache downloader:downloader]; + + UIBarButtonItem *submitButton = + [[UIBarButtonItem alloc] initWithTitle:@"Add Photo" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapAddPhotoButton)]; + + self.navigationItem.rightBarButtonItem = submitButton; } - (void)didTapAddPhotoButton { - - // Here we load our request with the groups we want to subit to and additional info. - BVCurationsAddPostRequest *request = [[BVCurationsAddPostRequest alloc] - initWithGroups:@[] - withAuthorAlias:@"authorAlias" - withToken:@"token" - withText:@"Hello"]; - - // We've hard-coded an image here for testing, where you would normally have the user select from a gallery or camera. - request.image = [UIImage imageNamed:@"curations_test_image"]; - - // Now we just post the share view controller with a couple of styling options. - - BVCurationsPostViewController *shareVC = [[BVCurationsPostViewController alloc] - initWithPostRequest:request - logoImage:[UIImage imageNamed:@"happy_icon"] - bavBarColor:[UIColor orangeColor] - navBarTintColor:[UIColor whiteColor]]; - - shareVC.placeholder = @"Say something awesome\nabout your photo!"; - shareVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; - - [shareVC setDidPressCancel:^{ - // completion - [self.navigationController.presentingViewController dismissViewControllerAnimated:true completion:^{ - // completion - NSLog(@"User tapped cancel."); - }]; - }]; - - [shareVC setDidBeginPost:^{ - // Here you could add your own spinner - NSLog(@"Starting photo upload"); - }]; - - [shareVC setDidPressCancel:^{ - // completion - NSLog(@"User tapped cancel."); - [self.navigationController.presentingViewController dismissViewControllerAnimated:true completion:^{ - // completion - }]; - }]; - - [shareVC setDidCompletePost:^(NSError * _Nullable error) { - // completion - if (error){ - NSLog(@"ERROR submitting photo: %@", error.localizedDescription); - } else { - NSLog(@"Success submitting photo."); - } - }]; - - [self.navigationController presentViewController:shareVC animated:YES completion:^{ - // completion - }]; - -} -- (IBAction)stepColumnCount:(UIStepper *)sender { - - if (sender.value > 0){ - if (self.curationsCollectionView.bvCurationsUILayout == BVCurationsUILayoutCarousel){ - self.curationsCollectionView.bvCurationsUILayout = BVCurationsUILayoutGrid; - self.heightConstraintCarousel.active = NO; - _heightConstraintGrid.active = YES; - } - self.curationsCollectionView.itemsPerRow = sender.value; + // Here we load our request with the groups we want to subit to and additional + // info. + BVCurationsAddPostRequest *request = + [[BVCurationsAddPostRequest alloc] initWithGroups:@[] + withAuthorAlias:@"authorAlias" + withToken:@"token" + withText:@"Hello"]; + + // We've hard-coded an image here for testing, where you would normally have + // the user select from a gallery or camera. + request.image = [UIImage imageNamed:@"curations_test_image"]; + + // Now we just post the share view controller with a couple of styling + // options. + + BVCurationsPostViewController *shareVC = + [[BVCurationsPostViewController alloc] + initWithPostRequest:request + logoImage:[UIImage imageNamed:@"happy_icon"] + bavBarColor:[UIColor orangeColor] + navBarTintColor:[UIColor whiteColor]]; + + shareVC.placeholder = @"Say something awesome\nabout your photo!"; + shareVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; + + [shareVC setDidPressCancel:^{ + // completion + [self.navigationController.presentingViewController + dismissViewControllerAnimated:true + completion:^{ + // completion + NSLog(@"User tapped cancel."); + }]; + }]; + + [shareVC setDidBeginPost:^{ + // Here you could add your own spinner + NSLog(@"Starting photo upload"); + }]; + + [shareVC setDidPressCancel:^{ + // completion + NSLog(@"User tapped cancel."); + [self.navigationController.presentingViewController + dismissViewControllerAnimated:true + completion:^{ + // completion + }]; + }]; + + [shareVC setDidCompletePost:^(NSError *_Nullable error) { + // completion + if (error) { + NSLog(@"ERROR submitting photo: %@", error.localizedDescription); } else { - self.curationsCollectionView.bvCurationsUILayout = BVCurationsUILayoutCarousel; - _heightConstraintGrid.active = NO; - self.heightConstraintCarousel.active = YES; + NSLog(@"Success submitting photo."); } - - [self.view layoutIfNeeded]; + }]; + + [self.navigationController presentViewController:shareVC + animated:YES + completion:^{ + // completion + }]; } +- (IBAction)stepColumnCount:(UIStepper *)sender { + + if (sender.value > 0) { + if (self.curationsCollectionView.bvCurationsUILayout == + BVCurationsUILayoutCarousel) { + self.curationsCollectionView.bvCurationsUILayout = + BVCurationsUILayoutGrid; + self.heightConstraintCarousel.active = NO; + _heightConstraintGrid.active = YES; + } + self.curationsCollectionView.itemsPerRow = sender.value; + } else { + self.curationsCollectionView.bvCurationsUILayout = + BVCurationsUILayoutCarousel; + _heightConstraintGrid.active = NO; + self.heightConstraintCarousel.active = YES; + } + + [self.view layoutIfNeeded]; +} // MARK: BVCurationsUICollectionViewDelegate +- (void)curationsLoadImage:(NSString *)imageUrl + completion:(BVCurationsLoadImageCompletion)completion { -- (void)curationsLoadImage:(NSString *)imageUrl completion:(BVCurationsLoadImageCompletion)completion { - - [_sdMngr loadImageWithURL:[NSURL URLWithString:imageUrl] options:kNilOptions progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { + [_sdMngr loadImageWithURL:[NSURL URLWithString:imageUrl] + options:kNilOptions + progress:^(NSInteger receivedSize, NSInteger expectedSize, + NSURL *_Nullable targetURL) { // progress - } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) { + } + completed:^(UIImage *_Nullable image, NSData *_Nullable data, + NSError *_Nullable error, SDImageCacheType cacheType, + BOOL finished, NSURL *_Nullable imageURL) { completion(image, imageUrl); - }]; - + }]; } -- (void)curationsImageIsCached:(NSString *)imageUrl completion:(BVCurationsIsImageCachedCompletion)completion { - [_sdMngr cachedImageExistsForURL:[NSURL URLWithString:imageUrl] completion:^(BOOL isInCache) { - completion(isInCache, imageUrl); - }]; +- (void)curationsImageIsCached:(NSString *)imageUrl + completion:(BVCurationsIsImageCachedCompletion)completion { + [_sdMngr cachedImageExistsForURL:[NSURL URLWithString:imageUrl] + completion:^(BOOL isInCache) { + completion(isInCache, imageUrl); + }]; } - (void)curationsDidSelectFeedItem:(BVCurationsFeedItem *)feedItem { - // Handle your click logic here... - NSLog(@"Selected item: %@", feedItem.description); + // Handle your click logic here... + NSLog(@"Selected item: %@", feedItem.description); } --(void)curationsFailedToLoadFeed:(NSError *)error { - NSLog(@"An error occurred: %@", error); +- (void)curationsFailedToLoadFeed:(NSError *)error { + NSLog(@"An error occurred: %@", error); } @end diff --git a/Examples/Curations/Obj-C/CurationsExample/main.m b/Examples/Curations/Obj-C/CurationsExample/main.m index 89882bbc..c440e1b9 100644 --- a/Examples/Curations/Obj-C/CurationsExample/main.m +++ b/Examples/Curations/Obj-C/CurationsExample/main.m @@ -5,11 +5,12 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "AppDelegate.h" +#import -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, + NSStringFromClass([AppDelegate class])); + } } diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.h b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.h index e0a2bc5a..aaf7cd87 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.h +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.h @@ -9,8 +9,6 @@ @interface AppDelegate : UIResponder -@property (strong, nonatomic) UIWindow *window; - +@property(strong, nonatomic) UIWindow *window; @end - diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.m b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.m index 0dcbf123..8249f049 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.m +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/AppDelegate.m @@ -14,39 +14,53 @@ @interface AppDelegate () @implementation AppDelegate +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - #warning See bvsdk_config_staging.json and bvsdk_config_product.json in the project for API key and client ID settings. - [BVSDKManager configure:BVConfigurationTypeProd]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; - - // Example of hard-coded UAS token to set a user's profile - [[BVSDKManager sharedManager] setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d31323334267573657269643d5265636f6d6d656e646174696f6e7353646b54657374"]; - - return YES; + [BVSDKManager configure:BVConfigurationTypeProd]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; + + // Example of hard-coded UAS token to set a user's profile + [[BVSDKManager sharedManager] + setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d3132333" + @"4267573657269643d5265636f6d6d656e646174696f6e7353" + @"646b54657374"]; + + return YES; } - (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + // Sent when the application is about to move from active to inactive state. + // This can occur for certain types of temporary interruptions (such as an + // incoming phone call or SMS message) or when the user quits the application + // and it begins the transition to the background state. Use this method to + // pause ongoing tasks, disable timers, and throttle down OpenGL ES frame + // rates. Games should use this method to pause the game. } - (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + // Use this method to release shared resources, save user data, invalidate + // timers, and store enough application state information to restore your + // application to its current state in case it is terminated later. If your + // application supports background execution, this method is called instead of + // applicationWillTerminate: when the user quits. } - (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + // Called as part of the transition from the background to the inactive state; + // here you can undo many of the changes made on entering the background. } - (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + // Restart any tasks that were paused (or not yet started) while the + // application was inactive. If the application was previously in the + // background, optionally refresh the user interface. } - (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + // Called when the application is about to terminate. Save data if + // appropriate. See also applicationDidEnterBackground:. } @end diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.h b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.h index e89108c9..c5bf66ed 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.h +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.h @@ -5,7 +5,6 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // - @import BVSDK; #import diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.m b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.m index 13f6bcc2..61131ba3 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.m +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/DemoCell.m @@ -22,17 +22,19 @@ @interface DemoCell () @implementation DemoCell - (void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { - - super.bvRecommendedProduct = bvRecommendedProduct; - - NSURL *url = [NSURL URLWithString:self.bvRecommendedProduct.imageURL]; - [self.productImageView sd_setImageWithURL:url]; - self.productName.text = bvRecommendedProduct.productName; - self.rating.text = [NSString stringWithFormat:@"%.1f", [bvRecommendedProduct.averageRating floatValue]]; - self.starRating.value = [bvRecommendedProduct.averageRating floatValue]; - self.numReview.text = [NSString stringWithFormat:@"(%ld)", [bvRecommendedProduct.numReviews longValue]]; - self.price.text = bvRecommendedProduct.price; - + + super.bvRecommendedProduct = bvRecommendedProduct; + + NSURL *url = [NSURL URLWithString:self.bvRecommendedProduct.imageURL]; + [self.productImageView sd_setImageWithURL:url]; + self.productName.text = bvRecommendedProduct.productName; + self.rating.text = [NSString + stringWithFormat:@"%.1f", + [bvRecommendedProduct.averageRating floatValue]]; + self.starRating.value = [bvRecommendedProduct.averageRating floatValue]; + self.numReview.text = [NSString + stringWithFormat:@"(%ld)", [bvRecommendedProduct.numReviews longValue]]; + self.price.text = bvRecommendedProduct.price; } @end \ No newline at end of file diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.h b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.h index 1fef134d..811e6980 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.h +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.h @@ -8,8 +8,7 @@ #import @import BVSDK; -@interface ViewController : UIViewController - +@interface ViewController + : UIViewController @end - diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.m b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.m index fed11621..bb641fa3 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.m +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/ViewController.m @@ -10,78 +10,87 @@ @interface ViewController () -@property (weak, nonatomic) IBOutlet BVProductRecommendationsCollectionView *recommendationsView; -@property NSArray*products; +@property(weak, nonatomic) + IBOutlet BVProductRecommendationsCollectionView *recommendationsView; +@property NSArray *products; @end @implementation ViewController - (void)viewDidLoad { - [super viewDidLoad]; - - self.products = [NSArray array]; - - [self.recommendationsView registerNib:[UINib nibWithNibName:@"DemoCell" bundle:nil] - forCellWithReuseIdentifier:@"DemoCellIdentifier"]; - - self.recommendationsView.delegate = self; - self.recommendationsView.dataSource = self; - - [self loadRecommendations]; - + [super viewDidLoad]; + + self.products = [NSArray array]; + + [self.recommendationsView + registerNib:[UINib nibWithNibName:@"DemoCell" bundle:nil] + forCellWithReuseIdentifier:@"DemoCellIdentifier"]; + + self.recommendationsView.delegate = self; + self.recommendationsView.dataSource = self; + + [self loadRecommendations]; } -// Fetch the product recommenations from the API method on the BVRecommendationsCollectionView container. +// Fetch the product recommenations from the API method on the +// BVRecommendationsCollectionView container. - (void)loadRecommendations { - - BVRecommendationsRequest *request = [[BVRecommendationsRequest alloc] initWithLimit:20]; - - [self.recommendationsView loadRequest:request completionHandler:^(NSArray * proudcts) { + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:20]; + + [self.recommendationsView loadRequest:request + completionHandler:^(NSArray *proudcts) { self.products = proudcts; [self.recommendationsView reloadData]; - } errorHandler:^(NSError * error) { + } + errorHandler:^(NSError *error) { NSLog(@"ERROR: %@", error.localizedDescription); - }]; - + }]; } // Lays out the recommendationView with horitontal scrolling -- (void)viewWillLayoutSubviews{ - [super viewWillLayoutSubviews]; - - UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.recommendationsView.collectionViewLayout; - - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - layout.itemSize = CGSizeMake(self.recommendationsView.bounds.size.width / 2, self.recommendationsView.bounds.size.width / 2); - - layout.minimumInteritemSpacing = 0; - layout.minimumLineSpacing = 0; -} +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + UICollectionViewFlowLayout *layout = + (UICollectionViewFlowLayout *) + self.recommendationsView.collectionViewLayout; -#pragma mark UICollectionViewDatasource + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.itemSize = CGSizeMake(self.recommendationsView.bounds.size.width / 2, + self.recommendationsView.bounds.size.width / 2); -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ - return self.products.count; + layout.minimumInteritemSpacing = 0; + layout.minimumLineSpacing = 0; } -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ - - DemoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"DemoCellIdentifier" forIndexPath:indexPath]; - - cell.bvRecommendedProduct = [self.products objectAtIndex:indexPath.row]; - - return cell; - +#pragma mark UICollectionViewDatasource + +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return self.products.count; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + DemoCell *cell = [collectionView + dequeueReusableCellWithReuseIdentifier:@"DemoCellIdentifier" + forIndexPath:indexPath]; + + cell.bvRecommendedProduct = [self.products objectAtIndex:indexPath.row]; + + return cell; } #pragma mark UICollectionViewDelegate -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ - - NSLog(@"Selected: %@", [self.products objectAtIndex:indexPath.row]); - +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + + NSLog(@"Selected: %@", [self.products objectAtIndex:indexPath.row]); } @end diff --git a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/main.m b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/main.m index 5c75f893..ab36d448 100644 --- a/Examples/ProductRecommendations/Obj-C/RecommendationsExample/main.m +++ b/Examples/ProductRecommendations/Obj-C/RecommendationsExample/main.m @@ -5,11 +5,12 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "AppDelegate.h" +#import -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, + NSStringFromClass([AppDelegate class])); + } } diff --git a/Pod/Frameworks/Gimbal.framework/Gimbal b/Frameworks/Gimbal.framework/Gimbal similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Gimbal rename to Frameworks/Gimbal.framework/Gimbal diff --git a/Pod/Frameworks/Gimbal.framework/Headers b/Frameworks/Gimbal.framework/Headers similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Headers rename to Frameworks/Gimbal.framework/Headers diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Gimbal b/Frameworks/Gimbal.framework/Versions/2.52.1/Gimbal similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Gimbal rename to Frameworks/Gimbal.framework/Versions/2.52.1/Gimbal diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAction.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAction.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAction.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAction.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLApplicationStatus.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLApplicationStatus.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLApplicationStatus.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLApplicationStatus.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAttributes.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAttributes.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAttributes.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLAttributes.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeacon.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeacon.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeacon.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeacon.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconManager.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconManager.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconManager.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconManager.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconSighting.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconSighting.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconSighting.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLBeaconSighting.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCircle.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCircle.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCircle.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCircle.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunication.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunication.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunication.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunication.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunicationManager.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunicationManager.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunicationManager.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLCommunicationManager.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLDebugger.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLDebugger.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLDebugger.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLDebugger.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocation.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocation.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocation.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocation.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocationManager.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocationManager.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocationManager.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLEstablishedLocationManager.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLExperienceManager.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLExperienceManager.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLExperienceManager.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLExperienceManager.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlace.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlace.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlace.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlace.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlaceManager.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlaceManager.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlaceManager.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLPlaceManager.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLVisit.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLVisit.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLVisit.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/GMBLVisit.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/Gimbal.h b/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/Gimbal.h similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/2.52.1/Headers/Gimbal.h rename to Frameworks/Gimbal.framework/Versions/2.52.1/Headers/Gimbal.h diff --git a/Pod/Frameworks/Gimbal.framework/Versions/Current b/Frameworks/Gimbal.framework/Versions/Current similarity index 100% rename from Pod/Frameworks/Gimbal.framework/Versions/Current rename to Frameworks/Gimbal.framework/Versions/Current diff --git a/Pod/BVAdvertising/Private/BVAdsMessageInterceptor.h b/Pod/BVAdvertising/Private/BVAdsMessageInterceptor.h index 5e1eaeb4..c6ea08f1 100644 --- a/Pod/BVAdvertising/Private/BVAdsMessageInterceptor.h +++ b/Pod/BVAdvertising/Private/BVAdsMessageInterceptor.h @@ -5,9 +5,9 @@ // Copyright 2015 Bazaarvoice Inc. All rights reserved. // -#import #import "BVMessageInterceptor.h" +#import @interface BVAdsMessageInterceptor : BVMessageInterceptor -@end \ No newline at end of file +@end diff --git a/Pod/BVAnalytics/BVAnalyticsManager.h b/Pod/BVAnalytics/BVAnalyticsManager.h index 18973b9b..abb55675 100644 --- a/Pod/BVAnalytics/BVAnalyticsManager.h +++ b/Pod/BVAnalytics/BVAnalyticsManager.h @@ -7,63 +7,61 @@ #import - -/// Singleton class that manages all analytic event queueing and dispatching of events to Bazaarvoice. +/// Singleton class that manages all analytic event queueing and dispatching of +/// events to Bazaarvoice. @interface BVAnalyticsManager : NSObject - -/// Set to true when sending events to the staging server. Default is NO (production) +/// Set to true when sending events to the staging server. Default is NO +/// (production) @property BOOL isStagingServer; -/// Sets the client ID for the API being used. App will assert if client ID is not set! -@property (strong, nonatomic) NSString *clientId; +/// Sets the client ID for the API being used. App will assert if client ID is +/// not set! +@property(strong, nonatomic) NSString *clientId; - -/// Set to YES to only log analytic events and not send to server. Defaults to NO -@property (nonatomic, assign) BOOL isDryRunAnalytics; +/// Set to YES to only log analytic events and not send to server. Defaults to +/// NO +@property(nonatomic, assign) BOOL isDryRunAnalytics; /// Create and get the singleton instance of the analytics manager. + (BVAnalyticsManager *)sharedManager; - /// Add an event to the analytics processing queuek --(void)queueEvent:(NSDictionary*)eventData; - +- (void)queueEvent:(NSDictionary *)eventData; /// Add an event without IDFA to the analytics processing queue --(void)queueAnonymousEvent:(NSDictionary*)eventData; - +- (void)queueAnonymousEvent:(NSDictionary *)eventData; /** - Add a page view event to be queued. Pageview events run on a separate event queue than other impression events. - + Add a page view event to be queued. Pageview events run on a separate event + queue than other impression events. + @param pageViewEvent The page view event to send */ - (void)queuePageViewEventDict:(NSDictionary *)pageViewEvent; - /** Date formatter for BV analytic events. - + @param date An NSDate object - - @return The string representation of the date that can be sent along with a BV analytic event. - */ --(NSString*)formatDate:(NSDate*)date; + @return The string representation of the date that can be sent along with a + BV analytic event. + */ +- (NSString *)formatDate:(NSDate *)date; /// Immediately send all pending analytic events --(void)flushQueue; - +- (void)flushQueue; /** - Get mobile diagnostic event dictionary to add to an event. Provides info such as bundle identifier, app version, ect. - + Get mobile diagnostic event dictionary to add to an event. Provides info + such as bundle identifier, app version, ect. + @return Mutable dictionary with filled out key/value pairs. */ --(NSMutableDictionary*)getMobileDiagnosticParams; +- (NSMutableDictionary *)getMobileDiagnosticParams; /// internal use --(void)setFlushInterval:(NSTimeInterval)newInterval; +- (void)setFlushInterval:(NSTimeInterval)newInterval; @end diff --git a/Pod/BVAnalytics/BVAnalyticsManager.m b/Pod/BVAnalytics/BVAnalyticsManager.m index 2e44a57c..1619928b 100644 --- a/Pod/BVAnalytics/BVAnalyticsManager.m +++ b/Pod/BVAnalytics/BVAnalyticsManager.m @@ -8,11 +8,11 @@ #import #import -#import "BVSDKConstants.h" -#import "BVLogger.h" -#import "BVAnalyticsManager.h" #import "BVAnalyticEventManager.h" +#import "BVAnalyticsManager.h" +#import "BVLogger.h" #import "BVPersonalizationEvent.h" +#import "BVSDKConstants.h" #include #include @@ -22,12 +22,13 @@ @interface BVAnalyticsManager () -@property (strong) NSMutableArray* eventQueue; // Impressions, other non-pageview events -@property (strong) NSMutableArray* pageviewQueue; // Page views -@property NSTimer* queueFlushTimer; +@property(strong) + NSMutableArray *eventQueue; // Impressions, other non-pageview events +@property(strong) NSMutableArray *pageviewQueue; // Page views +@property NSTimer *queueFlushTimer; @property NSTimeInterval queueFlushInterval; -@property (nonatomic, strong) dispatch_queue_t concurrentEventQueue; +@property(nonatomic, strong) dispatch_queue_t concurrentEventQueue; @end @@ -35,330 +36,364 @@ @implementation BVAnalyticsManager static BVAnalyticsManager *analyticsInstance = nil; -+ (BVAnalyticsManager *) sharedManager { - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - analyticsInstance = [[self alloc] init]; - analyticsInstance->_concurrentEventQueue = dispatch_queue_create("com.bazaarvoice.analyticEventQueue", - DISPATCH_QUEUE_CONCURRENT); - }); - - return analyticsInstance; -} ++ (BVAnalyticsManager *)sharedManager { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + analyticsInstance = [[self alloc] init]; + analyticsInstance->_concurrentEventQueue = dispatch_queue_create( + "com.bazaarvoice.analyticEventQueue", DISPATCH_QUEUE_CONCURRENT); + }); -- (id) init { - self = [super init]; - if (self != nil) { - - self.eventQueue = [NSMutableArray array]; - self.pageviewQueue = [NSMutableArray array]; - [self setFlushInterval:10.0]; - [self registerForAppStateChanges]; - - } - return self; + return analyticsInstance; } +- (id)init { + self = [super init]; + if (self != nil) { + self.eventQueue = [NSMutableArray array]; + self.pageviewQueue = [NSMutableArray array]; + [self setFlushInterval:10.0]; + [self registerForAppStateChanges]; + } + return self; +} --(NSMutableDictionary*)getMobileDiagnosticParams { - - // get diagnostic data - NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; - - NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - NSString* osVersion = [[UIDevice currentDevice] systemVersion]; - NSString *majorMinor = [infoDictionary objectForKey:@"CFBundleShortVersionString"]; - NSString *build = [infoDictionary objectForKey:(NSString*)kCFBundleVersionKey]; - NSString* appVersion = [NSString stringWithFormat:@"%@.%@", majorMinor, build]; - - struct utsname systemInfo; - uname(&systemInfo); - NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; - - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:bundleIdentifier forKey:@"mobileAppIdentifier"]; - [params setValue:appVersion forKey:@"mobileAppVersion"]; - [params setValue:osVersion forKey:@"mobileOSVersion"]; - [params setValue:@"ios" forKey:@"mobileOS"]; - [params setValue:platform forKey:@"mobileDeviceName"]; - [params setValue:BV_SDK_VERSION forKey:@"bvSDKVersion"]; - - return params; +- (NSMutableDictionary *)getMobileDiagnosticParams { + // get diagnostic data + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + NSString *osVersion = [[UIDevice currentDevice] systemVersion]; + NSString *majorMinor = + [infoDictionary objectForKey:@"CFBundleShortVersionString"]; + NSString *build = + [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey]; + NSString *appVersion = + [NSString stringWithFormat:@"%@.%@", majorMinor, build]; + + struct utsname systemInfo; + uname(&systemInfo); + NSString *platform = [NSString stringWithCString:systemInfo.machine + encoding:NSUTF8StringEncoding]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:bundleIdentifier forKey:@"mobileAppIdentifier"]; + [params setValue:appVersion forKey:@"mobileAppVersion"]; + [params setValue:osVersion forKey:@"mobileOSVersion"]; + [params setValue:@"ios" forKey:@"mobileOS"]; + [params setValue:platform forKey:@"mobileDeviceName"]; + [params setValue:BV_SDK_VERSION forKey:@"bvSDKVersion"]; + + return params; } #pragma mark - Application state change updates --(void)registerForAppStateChanges { - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidFinishLaunching) - name:UIApplicationDidFinishLaunchingNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive) - name:UIApplicationDidBecomeActiveNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidEnterBackground) - name:UIApplicationDidEnterBackgroundNotification - object:nil]; - }); +- (void)registerForAppStateChanges { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationDidFinishLaunching) + name:UIApplicationDidFinishLaunchingNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationDidBecomeActive) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(applicationDidEnterBackground) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + }); } --(void)applicationDidFinishLaunching { - [self sendAppLaunchedEvent]; +- (void)applicationDidFinishLaunching { + [self sendAppLaunchedEvent]; } - --(void)applicationDidBecomeActive { - - [self sendAppActiveEvent]; +- (void)applicationDidBecomeActive { + [self sendAppActiveEvent]; } --(void)applicationDidEnterBackground { - [self sendAppInBackgroundEvent]; +- (void)applicationDidEnterBackground { + [self sendAppInBackgroundEvent]; } #pragma mark - App lifecycle events --(NSDictionary*)getAppStateEventParams { - return @{ - @"cl": @"Lifecycle", - @"type": @"MobileApp", - @"source": @"mobile-lifecycle" - }; +- (NSDictionary *)getAppStateEventParams { + return @{ + @"cl" : @"Lifecycle", + @"type" : @"MobileApp", + @"source" : @"mobile-lifecycle" + }; } --(void)sendAppLaunchedEvent { - [self sendAppStateEvent:@"launched"]; +- (void)sendAppLaunchedEvent { + [self sendAppStateEvent:@"launched"]; } --(void)sendAppActiveEvent { - [self sendAppStateEvent:@"active"]; +- (void)sendAppActiveEvent { + [self sendAppStateEvent:@"active"]; } --(void)sendAppInBackgroundEvent{ - [self sendAppStateEvent:@"background"]; +- (void)sendAppInBackgroundEvent { + [self sendAppStateEvent:@"background"]; } --(void)sendAppStateEvent:(NSString*)appState { - // build param dictionary +- (void)sendAppStateEvent:(NSString *)appState { + // build param dictionary + + NSMutableDictionary *eventData = [NSMutableDictionary + dictionaryWithDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; + [eventData addEntriesFromDictionary:[self getMobileDiagnosticParams]]; + [eventData addEntriesFromDictionary:[self getAppStateEventParams]]; + [eventData setObject:appState forKey:@"appState"]; - NSMutableDictionary* eventData = [NSMutableDictionary dictionaryWithDictionary:[[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - [eventData addEntriesFromDictionary:[self getMobileDiagnosticParams]]; - [eventData addEntriesFromDictionary:[self getAppStateEventParams]]; - [eventData setObject:appState forKey:@"appState"]; - - // send request - [self queueEvent:eventData]; + // send request + [self queueEvent:eventData]; } #pragma mark - Event Queueing --(void)queueEvent:(NSDictionary*)eventData { - - [[BVLogger sharedLogger] analyticsMessage:[NSString stringWithFormat:@"%@ - %@ - %@", [eventData objectForKey:@"cl"], [eventData objectForKey:@"type"], [eventData objectForKey:@"name"]]]; - [self processEvent:eventData isAnonymous:NO]; - } +- (void)queueEvent:(NSDictionary *)eventData { + [[BVLogger sharedLogger] + analyticsMessage:[NSString + stringWithFormat:@"%@ - %@ - %@", + [eventData objectForKey:@"cl"], + [eventData objectForKey:@"type"], + [eventData objectForKey:@"name"]]]; + [self processEvent:eventData isAnonymous:NO]; +} --(void)queueAnonymousEvent:(NSDictionary*)eventData { - [self processEvent:eventData isAnonymous:YES]; +- (void)queueAnonymousEvent:(NSDictionary *)eventData { + [self processEvent:eventData isAnonymous:YES]; } --(void)processEvent:(NSDictionary*)eventData isAnonymous:(BOOL)anonymous{ - - NSMutableDictionary *eventForQueue = [NSMutableDictionary dictionaryWithDictionary:eventData]; - [eventForQueue addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:anonymous]]; - - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // Update event queue - [self.eventQueue addObject:eventForQueue]; - - }); - - [self scheduleEventQueueFlush]; +- (void)processEvent:(NSDictionary *)eventData isAnonymous:(BOOL)anonymous { + NSMutableDictionary *eventForQueue = + [NSMutableDictionary dictionaryWithDictionary:eventData]; + [eventForQueue + addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:anonymous]]; + + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // Update event queue + [self.eventQueue addObject:eventForQueue]; + + }); + + [self scheduleEventQueueFlush]; } --(void)scheduleEventQueueFlush{ - - // schedule a queue flush, if not already scheduled - // many times, a rush of events come very close to each other, and then things are quiet for a while. - - dispatch_async(dispatch_get_main_queue(), ^{ - - if(self.queueFlushTimer == nil){ - - SEL flushQueueSelector = @selector(flushQueue); - - self.queueFlushTimer = [NSTimer scheduledTimerWithTimeInterval:self.queueFlushInterval - target:self - selector:flushQueueSelector - userInfo:nil - repeats:NO]; - } - }); +- (void)scheduleEventQueueFlush { + // schedule a queue flush, if not already scheduled + // many times, a rush of events come very close to each other, and then + // things are quiet for a while. + + dispatch_async(dispatch_get_main_queue(), ^{ + + if (self.queueFlushTimer == nil) { + SEL flushQueueSelector = @selector(flushQueue); + + self.queueFlushTimer = + [NSTimer scheduledTimerWithTimeInterval:self.queueFlushInterval + target:self + selector:flushQueueSelector + userInfo:nil + repeats:NO]; + } + }); } -- (void)queuePageViewEventDict:(NSDictionary *)pageViewEvent{ - - NSMutableDictionary *eventForQueue = [NSMutableDictionary dictionaryWithDictionary:pageViewEvent]; - [eventForQueue addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // Update PageView queue events - [self.pageviewQueue addObject:eventForQueue]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self flushQueue]; - }); +- (void)queuePageViewEventDict:(NSDictionary *)pageViewEvent { + NSMutableDictionary *eventForQueue = + [NSMutableDictionary dictionaryWithDictionary:pageViewEvent]; + [eventForQueue + addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; + + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // Update PageView queue events + [self.pageviewQueue addObject:eventForQueue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self flushQueue]; }); - - - + }); } // Flush queue for POST batch --(void)flushQueue { - - // clear flush timer - if(self.queueFlushTimer != nil){ - [self.queueFlushTimer invalidate]; - } - self.queueFlushTimer = nil; - - // send events - if([self.eventQueue count] > 0){ - NSDictionary* batch = @{ @"batch": self.eventQueue }; - [self sendBatchedPOSTEvent:batch withCompletionHandler:^(NSError * _Nullable error) { - if (error){ - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: posting magpie impression event%@", error.localizedDescription]]; - } - - // For internal testing - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" object:error]; - }); - +- (void)flushQueue { + // clear flush timer + if (self.queueFlushTimer != nil) { + [self.queueFlushTimer invalidate]; + } + self.queueFlushTimer = nil; + + // send events + if ([self.eventQueue count] > 0) { + NSDictionary *batch = @{@"batch" : self.eventQueue}; + [self + sendBatchedPOSTEvent:batch + withCompletionHandler:^(NSError *__nullable error) { + if (error) { + [[BVLogger sharedLogger] + error:[NSString stringWithFormat: + @"ERROR: posting magpie impression event%@", + error.localizedDescription]]; + } + + // For internal testing + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] + postNotificationName:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:error]; + }); + }]; - - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // purge queue - [self.eventQueue removeAllObjects]; - }); - - } - - // send page view events - if ([self.pageviewQueue count] > 0){ - NSDictionary* batch = @{ @"batch": self.pageviewQueue }; - [self sendBatchedPOSTEvent:batch withCompletionHandler:^(NSError * _Nullable error) { - if (error){ - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: posting magpie pageview event%@", error.localizedDescription]]; - } - - // For internal testing - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" object:error]; - }); - + + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // purge queue + [self.eventQueue removeAllObjects]; + }); + } + + // send page view events + if ([self.pageviewQueue count] > 0) { + NSDictionary *batch = @{@"batch" : self.pageviewQueue}; + [self + sendBatchedPOSTEvent:batch + withCompletionHandler:^(NSError *__nullable error) { + if (error) { + [[BVLogger sharedLogger] + error:[NSString stringWithFormat: + @"ERROR: posting magpie pageview event%@", + error.localizedDescription]]; + } + + // For internal testing + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] + postNotificationName:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" + object:error]; + }); + }]; - - dispatch_barrier_sync(self.concurrentEventQueue, ^{ - // purge pageview queue - [self.pageviewQueue removeAllObjects]; - }); - - } + + dispatch_barrier_sync(self.concurrentEventQueue, ^{ + // purge pageview queue + [self.pageviewQueue removeAllObjects]; + }); + } } --(void)sendBatchedPOSTEvent:(NSDictionary*)eventData withCompletionHandler:(void (^)(NSError * __nullable error))completionHandler{ - - NSURL *url = [NSURL URLWithString:[self baseUrl]]; - [[BVLogger sharedLogger] analyticsMessage:[NSString stringWithFormat:@"POST Event: %@\nWith Data:%@", url, eventData]]; - - if (_isDryRunAnalytics) { - [[BVLogger sharedLogger]info:@"Analytic events are not being sent to server"]; - return; - } - - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - request.HTTPMethod = @"POST"; - [request setValue:@"application/json" forHTTPHeaderField: @"Content-Type"];// content type - - NSError *error = nil; - NSData *data = [NSJSONSerialization dataWithJSONObject:eventData options:kNilOptions error:&error]; - - if (!error){ - NSURLSessionUploadTask *postTask = [session uploadTaskWithRequest:request - fromData:data completionHandler:^(NSData *data, - NSURLResponse *response, NSError *error) { - - // completion - // one-way communication, client->server: disregard results but log error, if any - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if ((httpResponse && httpResponse.statusCode >= 300) || error != nil){ - - if (data){ - - NSString* errorMsg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - [[BVLogger sharedLogger] error:errorMsg]; - - } else { - - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: Posting analytics event failed with status: %ld and error: %@", (long)httpResponse.statusCode, error]]; +- (void)sendBatchedPOSTEvent:(NSDictionary *)eventData + withCompletionHandler: + (void (^)(NSError *__nullable error))completionHandler { + NSURL *url = [NSURL URLWithString:[self baseUrl]]; + [[BVLogger sharedLogger] + analyticsMessage:[NSString + stringWithFormat:@"POST Event: %@\nWith Data:%@", + url, eventData]]; + + if (_isDryRunAnalytics) { + [[BVLogger sharedLogger] + info:@"Analytic events are not being sent to server"]; + return; + } + + NSURLSessionConfiguration *config = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + request.HTTPMethod = @"POST"; + [request setValue:@"application/json" + forHTTPHeaderField:@"Content-Type"]; // content type + + NSError *error = nil; + NSData *data = [NSJSONSerialization dataWithJSONObject:eventData + options:kNilOptions + error:&error]; + + if (!error) { + NSURLSessionUploadTask *postTask = [session + uploadTaskWithRequest:request + fromData:data + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { + + // completion + // one-way communication, client->server: disregard results + // but log error, if any + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if ((httpResponse && httpResponse.statusCode >= 300) || + error != nil) { + if (data) { + NSString *errorMsg = + [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + [[BVLogger sharedLogger] error:errorMsg]; + + } else { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat:@"ERROR: Posting " + @"analytics event " + @"failed with " + @"status: %ld and " + @"error: %@", + (long)httpResponse.statusCode, + error]]; } - - } else { - - // Successful analyatics event sent - NSString* message = [NSString stringWithFormat:@"Analytics event sent successfully."]; - [[BVLogger sharedLogger] analyticsMessage:message]; - - } - - completionHandler(error); - - }]; - - [postTask resume]; - - }; - -} + } else { + // Successful analyatics event sent + NSString *message = [NSString + stringWithFormat:@"Analytics event sent successfully."]; + [[BVLogger sharedLogger] analyticsMessage:message]; + } -#pragma mark - Helpers + completionHandler(error); + + }]; --(NSString*)formatDate:(NSDate*)date { - - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; - [dateFormatter setLocale:enUSPOSIXLocale]; - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; - - return [dateFormatter stringFromDate:date]; //return ISO-8601 formatted UTC timestamp + [postTask resume]; + }; } --(NSString*)baseUrl { - if(self.isStagingServer){ - [[BVLogger sharedLogger] error:@"WARNING: Using staging server for analytic events. This should only enabled for non-production."]; - return BV_MAGPIE_STAGING_ENDPOINT; - } - else { - return BV_MAGPIE_ENDPOINT; - } +#pragma mark - Helpers + +- (NSString *)formatDate:(NSDate *)date { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = + [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [dateFormatter setLocale:enUSPOSIXLocale]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + + return [dateFormatter + stringFromDate:date]; // return ISO-8601 formatted UTC timestamp } --(void)setFlushInterval:(NSTimeInterval)newInterval { - self.queueFlushInterval = newInterval; +- (NSString *)baseUrl { + if (self.isStagingServer) { + [[BVLogger sharedLogger] error:@"WARNING: Using staging server for " + @"analytic events. This should only " + @"enabled for non-production."]; + return BV_MAGPIE_STAGING_ENDPOINT; + } else { + return BV_MAGPIE_ENDPOINT; + } } +- (void)setFlushInterval:(NSTimeInterval)newInterval { + self.queueFlushInterval = newInterval; +} @end diff --git a/Pod/BVAnalytics/BVLocationEventsHelper.h b/Pod/BVAnalytics/BVLocationEventsHelper.h index a6aeca4c..172531f8 100644 --- a/Pod/BVAnalytics/BVLocationEventsHelper.h +++ b/Pod/BVAnalytics/BVLocationEventsHelper.h @@ -5,102 +5,162 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import -#import // for handling beacon/geofence updates -#import "BVLogger.h" #import "BVAuthenticatedUser.h" -#import "BVGMBLVisit.h" -#import "BVGMBLSighting.h" #import "BVContextualInterests.h" +#import "BVGMBLSighting.h" +#import "BVGMBLVisit.h" +#import "BVLogger.h" +#import // for handling beacon/geofence updates +#import /// Static helper routes to post location-aware events. @interface BVLocationEventsHelper : NSObject - /*! Record the device entering a beacon/geofence region. @param manager The CLLocationManager object used in determining location. @param region The geofence or beacon region entered. - @param tier1 the BV-specified tier 1 interest associated with this region. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this region. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this region. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this region. + See BVContextualInterests.h */ -+(void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLCircularRegion *)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)locationManager:(CLLocationManager *)manager + didEnterRegion:(CLCircularRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! Record the device exiting a beacon/geofence region. @param manager The CLLocationManager object used in determining location. @param region The geofence or beacon region entered. - @param tier1 the BV-specified tier 1 interest associated with this region. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this region. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this region. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this region. + See BVContextualInterests.h */ -+(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLCircularRegion *)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)locationManager:(CLLocationManager *)manager + didExitRegion:(CLCircularRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! Record the device visiting a location. @param manager The CLLocationManager object used in determining location. @param visit The visit object that the device entered. - @param tier1 the BV-specified tier 1 interest associated with this visit. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this visit. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this visit. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this visit. + See BVContextualInterests.h */ -+(void)locationManager:(CLLocationManager*)manager didVisit:(CLVisit *)visit contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)locationManager:(CLLocationManager *)manager + didVisit:(CLVisit *)visit + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the ranging a beacon. This should be called for every beacon ranged in locationDidRangeBeacon:inRegion in CLLocationManagerDelegate. + Record the ranging a beacon. This should be called for every beacon ranged + in locationDidRangeBeacon:inRegion in CLLocationManagerDelegate. @param manager The CLLocationManager object used in determining location. @param beacon The CLBeacon object that was ranged @param region The CLBeaconRegion object that was ranged - @param tier1 the BV-specified tier 1 interest associated with this location. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this location. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this location. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this location. + See BVContextualInterests.h */ -+(void)locationManager:(CLLocationManager*)manager didRangeBeacon:(CLBeacon*)beacon inRegion:(CLBeaconRegion*)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; ++ (void)locationManager:(CLLocationManager *)manager + didRangeBeacon:(CLBeacon *)beacon + inRegion:(CLBeaconRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the user location. This should be called for ever location update in locationDidUpdateLocations: in CLLocationManagerDelegate. + Record the user location. This should be called for ever location update in + locationDidUpdateLocations: in CLLocationManagerDelegate. @param manager The CLLocationManager object used in determining location. @param location The CLLocation object that was updated. - @param tier1 the BV-specified tier 1 interest associated with this location. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this location. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this location. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this location. + See BVContextualInterests.h */ -+(void)locationManager:(CLLocationManager*)manager didUpdateLocation:(CLLocation*)location contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)locationManager:(CLLocationManager *)manager + didUpdateLocation:(CLLocation *)location + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the device receiving a gimbal beacon sighting event. This method has a direct correlation to beaconManager:didReceiveBeaconSighting: + Record the device receiving a gimbal beacon sighting event. This method has + a direct correlation to beaconManager:didReceiveBeaconSighting: @param RSSI The RSSI (signal strength) of the beacon sighting. @param date The NSDate that the sighting occured @param identifier The GMBLBeacon's factory ID. @param name The GMBLBeacon's name, assigned via the Gimbal Manager - @param tier1 the BV-specified tier 1 interest associated with this sighting. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this sighting. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this sighting. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this sighting. + See BVContextualInterests.h */ -+(void)gimbalBeaconSighting:(NSInteger)RSSI date:(NSDate*)date identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)gimbalBeaconSighting:(NSInteger)RSSI + date:(NSDate *)date + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the device beginning a gimbal visit. This directly correlates to GMBLPlaceManagerDelegate's placeManager:didBeginVisit: method. + Record the device beginning a gimbal visit. This directly correlates to + GMBLPlaceManagerDelegate's placeManager:didBeginVisit: method. @param arrivalDate Taken from the GMBLVisit object. @param dwellTime Taken from the GMBLVisit object. @param departureDate Taken from the GMBLVisit object. @param identifier Taken from GMBLPlace object. @param name Taken from GMBLPlace object. - @param tier1 the BV-specified tier 1 interest associated with this visit. See BVContextualInterests.h - @param tier2 the BV-specified tier 2 interest associated with this visit. See BVContextualInterests.h + @param tier1 the BV-specified tier 1 interest associated with this visit. + See BVContextualInterests.h + @param tier2 the BV-specified tier 2 interest associated with this visit. + See BVContextualInterests.h */ -+(void)gimbalPlaceBeginVisit:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)gimbalPlaceBeginVisit:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the device exiting a gimbal visit. This directly correlates to GMBLPlaceManagerDelegate's placeManager:didEndVisit: method. See gimbalPlaceBeginVisit:dwellTime:departureTime:identifier:name:attributes for details on this method. + Record the device exiting a gimbal visit. This directly correlates to + GMBLPlaceManagerDelegate's placeManager:didEndVisit: method. See + gimbalPlaceBeginVisit:dwellTime:departureTime:identifier:name:attributes for + details on this method. */ -+(void)gimbalPlaceEndVisit:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; - ++ (void)gimbalPlaceEndVisit:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; /*! - Record the device receiving a gimbal beacon sighting event. This method has a direct correlation to placeManager:didReceiveBeaconSighting:forVisits:. All parameters (lol, so many) are detailed in the gimbalBeaconSighting: and gimbalPlaceBeginVisit: methods of this SDK, as this event is a combination of those two. + Record the device receiving a gimbal beacon sighting event. This method has + a direct correlation to placeManager:didReceiveBeaconSighting:forVisits:. All + parameters (lol, so many) are detailed in the gimbalBeaconSighting: and + gimbalPlaceBeginVisit: methods of this SDK, as this event is a combination of + those two. */ -+(void)gimbalBeaconSightingForVisits:(NSInteger)RSSI date:(NSDate*)date beaconIdentifier:(NSString*)beaconIdentifier beaconName:(NSString*)beaconName arrivalDate:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate visitIdentifier:(NSString*)visitIdentifier visitName:(NSString*)visitName contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2; ++ (void)gimbalBeaconSightingForVisits:(NSInteger)RSSI + date:(NSDate *)date + beaconIdentifier:(NSString *)beaconIdentifier + beaconName:(NSString *)beaconName + arrivalDate:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + visitIdentifier:(NSString *)visitIdentifier + visitName:(NSString *)visitName + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2; @end diff --git a/Pod/BVAnalytics/BVLocationEventsHelper.m b/Pod/BVAnalytics/BVLocationEventsHelper.m index 9b9493d5..6eccffdb 100644 --- a/Pod/BVAnalytics/BVLocationEventsHelper.m +++ b/Pod/BVAnalytics/BVLocationEventsHelper.m @@ -6,308 +6,341 @@ // #import "BVLocationEventsHelper.h" -#import "BVLocationWrapper.h" #import "BVAnalyticsManager.h" +#import "BVLocationWrapper.h" @implementation BVLocationEventsHelper -+(void)locationManager:(CLLocationManager*)manager didEnterRegion:(CLCircularRegion *)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - ++ (void)locationManager:(CLLocationManager *)manager + didEnterRegion:(CLCircularRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + locationWrapper.location = [manager location]; - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - locationWrapper.location = [manager location]; - - [BVLocationEventsHelper didEnterRegion:region location:locationWrapper]; + [BVLocationEventsHelper didEnterRegion:region location:locationWrapper]; } -+(void)locationManager:(CLLocationManager*)manager didExitRegion:(CLCircularRegion *)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - locationWrapper.location = [manager location]; - - [BVLocationEventsHelper didExitRegion:region location:locationWrapper]; -} ++ (void)locationManager:(CLLocationManager *)manager + didExitRegion:(CLCircularRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + locationWrapper.location = [manager location]; -+(void)locationManager:(CLLocationManager*)manager didVisit:(CLVisit *)visit contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - locationWrapper.location = [manager location]; - - [BVLocationEventsHelper didVisit:visit location:locationWrapper]; + [BVLocationEventsHelper didExitRegion:region location:locationWrapper]; } -+(void)locationManager:(CLLocationManager*)manager didRangeBeacon:(CLBeacon*)beacon inRegion:(CLBeaconRegion*)region contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - locationWrapper.location = [manager location]; - - [BVLocationEventsHelper didRangeBeacon:beacon inRegion:region location:locationWrapper]; -} ++ (void)locationManager:(CLLocationManager *)manager + didVisit:(CLVisit *)visit + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + locationWrapper.location = [manager location]; -+(void)locationManager:(CLLocationManager*)manager didUpdateLocation:(CLLocation*)location contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - locationWrapper.location = [manager location]; - - [BVLocationEventsHelper didUpdateLocation:locationWrapper]; + [BVLocationEventsHelper didVisit:visit location:locationWrapper]; } -+(void)gimbalBeaconSighting:(NSInteger)RSSI date:(NSDate*)date identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVGMBLSighting* sighting = [[BVGMBLSighting alloc] init]; - sighting.RSSI = RSSI; - sighting.date = date; - sighting.identifier = identifier; - sighting.name = name; - - [BVLocationEventsHelper gimbalSighting:sighting]; ++ (void)locationManager:(CLLocationManager *)manager + didRangeBeacon:(CLBeacon *)beacon + inRegion:(CLBeaconRegion *)region + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + locationWrapper.location = [manager location]; + + [BVLocationEventsHelper didRangeBeacon:beacon + inRegion:region + location:locationWrapper]; } -+(void)gimbalPlaceBeginVisit:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVGMBLVisit* visit = [[BVGMBLVisit alloc] init]; - visit.arrivalDate = arrivalDate; - visit.dwellTime = dwellTime; - visit.departureDate = departureDate; - visit.identifier = identifier; - visit.name = name; - - [BVLocationEventsHelper gimbalPlaceBeginVisit:visit]; ++ (void)locationManager:(CLLocationManager *)manager + didUpdateLocation:(CLLocation *)location + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + locationWrapper.location = [manager location]; + + [BVLocationEventsHelper didUpdateLocation:locationWrapper]; } -+(void)gimbalPlaceEndVisit:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate identifier:(NSString*)identifier name:(NSString*)name contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVGMBLVisit* visit = [[BVGMBLVisit alloc] init]; - visit.arrivalDate = arrivalDate; - visit.dwellTime = dwellTime; - visit.departureDate = departureDate; - visit.identifier = identifier; - visit.name = name; ++ (void)gimbalBeaconSighting:(NSInteger)RSSI + date:(NSDate *)date + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVGMBLSighting *sighting = [[BVGMBLSighting alloc] init]; + sighting.RSSI = RSSI; + sighting.date = date; + sighting.identifier = identifier; + sighting.name = name; + + [BVLocationEventsHelper gimbalSighting:sighting]; +} - [BVLocationEventsHelper gimbalPlaceEndVisit:visit]; ++ (void)gimbalPlaceBeginVisit:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVGMBLVisit *visit = [[BVGMBLVisit alloc] init]; + visit.arrivalDate = arrivalDate; + visit.dwellTime = dwellTime; + visit.departureDate = departureDate; + visit.identifier = identifier; + visit.name = name; + + [BVLocationEventsHelper gimbalPlaceBeginVisit:visit]; } -+(void)gimbalBeaconSightingForVisits:(NSInteger)RSSI date:(NSDate*)date beaconIdentifier:(NSString*)beaconIdentifier beaconName:(NSString*)beaconName arrivalDate:(NSDate*)arrivalDate dwellTime:(NSTimeInterval)dwellTime departureDate:(NSDate*)departureDate visitIdentifier:(NSString*)visitIdentifier visitName:(NSString*)visitName contextualTier1:(BVContextualInterest*)tier1 contextualTier2:(BVContextualInterest*)tier2 { - - BVGMBLVisit* visit = [[BVGMBLVisit alloc] init]; - visit.arrivalDate = arrivalDate; - visit.dwellTime = dwellTime; - visit.departureDate = departureDate; - visit.identifier = visitIdentifier; - visit.name = visitName; - - BVGMBLSighting* sighting = [[BVGMBLSighting alloc] init]; - sighting.RSSI = RSSI; - sighting.date = date; - sighting.identifier = beaconIdentifier; - sighting.name = beaconName; - - BVLocationWrapper* locationWrapper = [[BVLocationWrapper alloc] init]; - locationWrapper.contextualTier1 = tier1; - locationWrapper.contextualTier2 = tier2; - - [BVLocationEventsHelper gimbalSighting:sighting forVisit:visit]; ++ (void)gimbalPlaceEndVisit:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + identifier:(NSString *)identifier + name:(NSString *)name + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVGMBLVisit *visit = [[BVGMBLVisit alloc] init]; + visit.arrivalDate = arrivalDate; + visit.dwellTime = dwellTime; + visit.departureDate = departureDate; + visit.identifier = identifier; + visit.name = name; + + [BVLocationEventsHelper gimbalPlaceEndVisit:visit]; } ++ (void)gimbalBeaconSightingForVisits:(NSInteger)RSSI + date:(NSDate *)date + beaconIdentifier:(NSString *)beaconIdentifier + beaconName:(NSString *)beaconName + arrivalDate:(NSDate *)arrivalDate + dwellTime:(NSTimeInterval)dwellTime + departureDate:(NSDate *)departureDate + visitIdentifier:(NSString *)visitIdentifier + visitName:(NSString *)visitName + contextualTier1:(BVContextualInterest *)tier1 + contextualTier2:(BVContextualInterest *)tier2 { + BVGMBLVisit *visit = [[BVGMBLVisit alloc] init]; + visit.arrivalDate = arrivalDate; + visit.dwellTime = dwellTime; + visit.departureDate = departureDate; + visit.identifier = visitIdentifier; + visit.name = visitName; + + BVGMBLSighting *sighting = [[BVGMBLSighting alloc] init]; + sighting.RSSI = RSSI; + sighting.date = date; + sighting.identifier = beaconIdentifier; + sighting.name = beaconName; + + BVLocationWrapper *locationWrapper = [[BVLocationWrapper alloc] init]; + locationWrapper.contextualTier1 = tier1; + locationWrapper.contextualTier2 = tier2; + + [BVLocationEventsHelper gimbalSighting:sighting forVisit:visit]; +} #pragma mark - Gimbal updates -+(NSDictionary*)dictForVisit:(BVGMBLVisit*)visit { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:[[BVAnalyticsManager sharedManager] formatDate:visit.arrivalDate] forKey:@"arrivalDate"]; - [params setValue:[[BVAnalyticsManager sharedManager] formatDate:visit.departureDate] forKey:@"departureDate"]; - [params setValue:@(visit.dwellTime) forKey:@"dwellTime"]; // wrap in NSNumber - [params setValue:visit.identifier forKey:@"identifier"]; - [params setValue:visit.name forKey:@"name"]; - return params; ++ (NSDictionary *)dictForVisit:(BVGMBLVisit *)visit { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params + setValue:[[BVAnalyticsManager sharedManager] formatDate:visit.arrivalDate] + forKey:@"arrivalDate"]; + [params setValue:[[BVAnalyticsManager sharedManager] + formatDate:visit.departureDate] + forKey:@"departureDate"]; + [params setValue:@(visit.dwellTime) forKey:@"dwellTime"]; // wrap in NSNumber + [params setValue:visit.identifier forKey:@"identifier"]; + [params setValue:visit.name forKey:@"name"]; + return params; } -+(NSDictionary*)dictForSighting:(BVGMBLSighting*)sighting { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:@(sighting.RSSI) forKey:@"RSSI"]; // wrap in NSNumber - [params setValue:[[BVAnalyticsManager sharedManager] formatDate:sighting.date] forKey:@"date"]; - [params setValue:sighting.identifier forKey:@"identifier"]; - [params setValue:sighting.name forKey:@"name"]; - return params; ++ (NSDictionary *)dictForSighting:(BVGMBLSighting *)sighting { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:@(sighting.RSSI) forKey:@"RSSI"]; // wrap in NSNumber + [params setValue:[[BVAnalyticsManager sharedManager] formatDate:sighting.date] + forKey:@"date"]; + [params setValue:sighting.identifier forKey:@"identifier"]; + [params setValue:sighting.name forKey:@"name"]; + return params; } -+(void)gimbalSighting:(BVGMBLSighting*)sighting { - - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self dictForSighting:sighting]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"GimbalSighting", - @"source": @"location-mobile" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)gimbalSighting:(BVGMBLSighting *)sighting { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self dictForSighting:sighting]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"GimbalSighting", + @"source" : @"location-mobile" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } - -+(void)gimbalSighting:(BVGMBLSighting*)sighting forVisit:(BVGMBLVisit*)visit { - - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self dictForVisit:visit]]; - [eventData addEntriesFromDictionary:[self dictForSighting:sighting]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"GimbalSighting", - @"source": @"location-mobile" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)gimbalSighting:(BVGMBLSighting *)sighting + forVisit:(BVGMBLVisit *)visit { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self dictForVisit:visit]]; + [eventData addEntriesFromDictionary:[self dictForSighting:sighting]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"GimbalSighting", + @"source" : @"location-mobile" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } -+(void)gimbalPlaceBeginVisit:(BVGMBLVisit*)visit { - [self gimbalPlaceVisit:[self dictForVisit:visit] isBegin:true]; ++ (void)gimbalPlaceBeginVisit:(BVGMBLVisit *)visit { + [self gimbalPlaceVisit:[self dictForVisit:visit] isBegin:true]; } -+(void)gimbalPlaceEndVisit:(BVGMBLVisit*)visit { - [self gimbalPlaceVisit:[self dictForVisit:visit] isBegin:false]; ++ (void)gimbalPlaceEndVisit:(BVGMBLVisit *)visit { + [self gimbalPlaceVisit:[self dictForVisit:visit] isBegin:false]; } -+(void)gimbalPlaceVisit:(NSDictionary*)visitParams isBegin:(bool)begin { - - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:visitParams]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"GimbalVisit", - @"source": @"location-mobile", - @"begin": [NSNumber numberWithBool:begin], - @"end": [NSNumber numberWithBool:!begin] - }]; - - // send request - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)gimbalPlaceVisit:(NSDictionary *)visitParams isBegin:(bool)begin { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:visitParams]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"GimbalVisit", + @"source" : @"location-mobile", + @"begin" : [NSNumber numberWithBool:begin], + @"end" : [NSNumber numberWithBool:!begin] + }]; + + // send request + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } - #pragma mark - Location updates -+(NSDictionary*)getLocationParams:(BVLocationWrapper*)location { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:@(location.location.coordinate.latitude) forKey:@"lat"]; - [params setValue:@(location.location.coordinate.longitude) forKey:@"long"]; - [params setValue:@(location.location.horizontalAccuracy) forKey:@"accuracy"]; - [params setValue:location.contextualTier1 forKey:@"tier1"]; - [params setValue:location.contextualTier2 forKey:@"tier2"]; - - return params; -} ++ (NSDictionary *)getLocationParams:(BVLocationWrapper *)location { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:@(location.location.coordinate.latitude) forKey:@"lat"]; + [params setValue:@(location.location.coordinate.longitude) forKey:@"long"]; + [params setValue:@(location.location.horizontalAccuracy) forKey:@"accuracy"]; + [params setValue:location.contextualTier1 forKey:@"tier1"]; + [params setValue:location.contextualTier2 forKey:@"tier2"]; -+(NSDictionary*)getGeofenceParams:(CLCircularRegion*)region { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:@(region.center.latitude) forKey:@"centerLat"]; - [params setValue:@(region.center.longitude) forKey:@"centerLong"]; - [params setValue:@(region.radius) forKey:@"radius"]; - - return params; + return params; } -+(NSDictionary*)getVisitParams:(CLVisit*)visit { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:@(visit.coordinate.latitude) forKey:@"centerLat"]; - [params setValue:@(visit.coordinate.longitude) forKey:@"centerLong"]; - [params setValue:@(visit.horizontalAccuracy) forKey:@"radius"]; - - return params; -} ++ (NSDictionary *)getGeofenceParams:(CLCircularRegion *)region { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:@(region.center.latitude) forKey:@"centerLat"]; + [params setValue:@(region.center.longitude) forKey:@"centerLong"]; + [params setValue:@(region.radius) forKey:@"radius"]; -+(NSDictionary*)getBeaconParams:(CLBeacon*)beacon forRegion:(CLRegion*)region { - - // create with this patern, to allow for nil values - NSMutableDictionary* params = [NSMutableDictionary dictionary]; - [params setValue:beacon.proximityUUID.UUIDString forKey:@"beaconUUID"]; - [params setValue:beacon.major forKey:@"beaconMajor"]; - [params setValue:beacon.minor forKey:@"beaconMinor"]; - [params setValue:@(beacon.rssi) forKey:@"beaconRSSI"]; - [params setValue:region.identifier forKey:@"beaconId"]; - - return params; + return params; } -+(void)didEnterRegion:(CLCircularRegion*)region location:(BVLocationWrapper*)location { - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self getLocationParams:location]]; - [eventData addEntriesFromDictionary:[self getGeofenceParams:region]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"Geofence", - @"source": @"location-mobile", - @"updateType": @"Enter" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (NSDictionary *)getVisitParams:(CLVisit *)visit { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:@(visit.coordinate.latitude) forKey:@"centerLat"]; + [params setValue:@(visit.coordinate.longitude) forKey:@"centerLong"]; + [params setValue:@(visit.horizontalAccuracy) forKey:@"radius"]; + + return params; } -+(void)didExitRegion:(CLCircularRegion*)region location:(BVLocationWrapper*)location { - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self getLocationParams:location]]; - [eventData addEntriesFromDictionary:[self getGeofenceParams:region]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"Geofence", - @"source": @"location-mobile", - @"updateType": @"Exit" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (NSDictionary *)getBeaconParams:(CLBeacon *)beacon + forRegion:(CLRegion *)region { + // create with this patern, to allow for nil values + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setValue:beacon.proximityUUID.UUIDString forKey:@"beaconUUID"]; + [params setValue:beacon.major forKey:@"beaconMajor"]; + [params setValue:beacon.minor forKey:@"beaconMinor"]; + [params setValue:@(beacon.rssi) forKey:@"beaconRSSI"]; + [params setValue:region.identifier forKey:@"beaconId"]; + + return params; } -+(void)didVisit:(CLVisit*)visit location:(BVLocationWrapper*)location { - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self getLocationParams:location]]; - [eventData addEntriesFromDictionary:[self getVisitParams:visit]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"Geofence", - @"source": @"location-mobile", - @"updateType": @"Enter" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)didEnterRegion:(CLCircularRegion *)region + location:(BVLocationWrapper *)location { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self getLocationParams:location]]; + [eventData addEntriesFromDictionary:[self getGeofenceParams:region]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"Geofence", + @"source" : @"location-mobile", + @"updateType" : @"Enter" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } -+(void)didRangeBeacon:(CLBeacon*)beacon inRegion:(CLBeaconRegion*)region location:(BVLocationWrapper*)location { - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self getLocationParams:location]]; - [eventData addEntriesFromDictionary:[self getBeaconParams:beacon forRegion:region]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"Beacon", - @"source": @"location-mobile" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)didExitRegion:(CLCircularRegion *)region + location:(BVLocationWrapper *)location { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self getLocationParams:location]]; + [eventData addEntriesFromDictionary:[self getGeofenceParams:region]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"Geofence", + @"source" : @"location-mobile", + @"updateType" : @"Exit" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } -+(void)didUpdateLocation:(BVLocationWrapper*)location { - NSMutableDictionary* eventData = [NSMutableDictionary dictionary]; - [eventData addEntriesFromDictionary:[self getLocationParams:location]]; - [eventData addEntriesFromDictionary:@{ - @"cl": @"Location", - @"type": @"Geo", - @"source": @"location-mobile" - }]; - [[BVAnalyticsManager sharedManager] queueEvent:eventData]; ++ (void)didVisit:(CLVisit *)visit location:(BVLocationWrapper *)location { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self getLocationParams:location]]; + [eventData addEntriesFromDictionary:[self getVisitParams:visit]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"Geofence", + @"source" : @"location-mobile", + @"updateType" : @"Enter" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; } ++ (void)didRangeBeacon:(CLBeacon *)beacon + inRegion:(CLBeaconRegion *)region + location:(BVLocationWrapper *)location { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self getLocationParams:location]]; + [eventData + addEntriesFromDictionary:[self getBeaconParams:beacon forRegion:region]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"Beacon", + @"source" : @"location-mobile" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; +} ++ (void)didUpdateLocation:(BVLocationWrapper *)location { + NSMutableDictionary *eventData = [NSMutableDictionary dictionary]; + [eventData addEntriesFromDictionary:[self getLocationParams:location]]; + [eventData addEntriesFromDictionary:@{ + @"cl" : @"Location", + @"type" : @"Geo", + @"source" : @"location-mobile" + }]; + [[BVAnalyticsManager sharedManager] queueEvent:eventData]; +} @end diff --git a/Pod/BVAnalytics/BVPixel.h b/Pod/BVAnalytics/BVPixel.h index 4a5b30f4..587a9ed4 100644 --- a/Pod/BVAnalytics/BVPixel.h +++ b/Pod/BVAnalytics/BVPixel.h @@ -7,29 +7,27 @@ #import -#import "BVAnalyticEvent.h" // All events implement for of BVAnalyticEvent +#import "BVAnalyticEvent.h" // All events implement for of BVAnalyticEvent // Event types that can be tracked. -#import "BVTransactionEvent.h" #import "BVConversionEvent.h" #import "BVFeatureUsedEvent.h" -#import "BVPageViewEvent.h" #import "BVImpressionEvent.h" #import "BVInViewEvent.h" -#import "BVImpressionEvent.h" +#import "BVPageViewEvent.h" #import "BVPersonalizationEvent.h" +#import "BVTransactionEvent.h" #import "BVViewedCGCEvent.h" /// Static class used to queue Conversion events @interface BVPixel : NSObject - /** Adds the event to be queued for upload. - @param event The type of to be tracked (e.g. BVPageViewEvent, BVImpressionEvent) + @param event The type of to be tracked (e.g. BVPageViewEvent, + BVImpressionEvent) */ -+(void)trackEvent:(_Nonnull id)event; - ++ (void)trackEvent:(nonnull id)event; @end diff --git a/Pod/BVAnalytics/BVPixel.m b/Pod/BVAnalytics/BVPixel.m index 1116e344..ff06232e 100644 --- a/Pod/BVAnalytics/BVPixel.m +++ b/Pod/BVAnalytics/BVPixel.m @@ -7,42 +7,39 @@ #import -#import "BVPixel.h" #import "BVAnalyticsManager.h" +#import "BVPixel.h" @implementation BVPixel -+(void)trackEvent:(_Nonnull id)event{ - - if ([event isKindOfClass:[BVConversionEvent class]]){ - BVConversionEvent *conversion = (BVConversionEvent *)event; - if ([conversion hasPII]){ - [[BVAnalyticsManager sharedManager] queueAnonymousEvent:[conversion toRaw]]; - } - - [[BVAnalyticsManager sharedManager] queueEvent:[conversion toRawNonPII]]; - - } - else if ([event isKindOfClass:[BVTransactionEvent class]]){ - BVTransactionEvent *transaction = (BVTransactionEvent *)event; - if ([transaction hasPII]){ - [[BVAnalyticsManager sharedManager] queueAnonymousEvent:[transaction toRaw]]; - } - - [[BVAnalyticsManager sharedManager] queueEvent:[transaction toRawNonPII]]; - - } - else if ([event isKindOfClass:[BVPersonalizationEvent class]]){ - [[BVAnalyticsManager sharedManager] queueEvent:[event toRaw]]; - [[BVAnalyticsManager sharedManager] flushQueue]; ++ (void)trackEvent:(nonnull id)event { + + if ([event isKindOfClass:[BVConversionEvent class]]) { + BVConversionEvent *conversion = (BVConversionEvent *)event; + if ([conversion hasPII]) { + [[BVAnalyticsManager sharedManager] + queueAnonymousEvent:[conversion toRaw]]; } - else if ([event isKindOfClass:[BVPageViewEvent class]]){ - [[BVAnalyticsManager sharedManager] queuePageViewEventDict:[event toRaw]]; - } else { - [[BVAnalyticsManager sharedManager] queueEvent:[event toRaw]]; + + [[BVAnalyticsManager sharedManager] queueEvent:[conversion toRawNonPII]]; + + } else if ([event isKindOfClass:[BVTransactionEvent class]]) { + BVTransactionEvent *transaction = (BVTransactionEvent *)event; + if ([transaction hasPII]) { + [[BVAnalyticsManager sharedManager] + queueAnonymousEvent:[transaction toRaw]]; } - -} + [[BVAnalyticsManager sharedManager] queueEvent:[transaction toRawNonPII]]; + + } else if ([event isKindOfClass:[BVPersonalizationEvent class]]) { + [[BVAnalyticsManager sharedManager] queueEvent:[event toRaw]]; + [[BVAnalyticsManager sharedManager] flushQueue]; + } else if ([event isKindOfClass:[BVPageViewEvent class]]) { + [[BVAnalyticsManager sharedManager] queuePageViewEventDict:[event toRaw]]; + } else { + [[BVAnalyticsManager sharedManager] queueEvent:[event toRaw]]; + } +} @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEvent.h index 97fc4eef..fb1d52a9 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEvent.h @@ -15,17 +15,17 @@ @required /// Other transaction parameters such as user email. -@property (nonatomic, strong) NSDictionary* _Nullable additionalParams; - +@property(nullable, nonatomic, strong) NSDictionary *additionalParams; /** - Converts the analytic event implementing the BVAnalyticEvent protocol to a dictionary. + Converts the analytic event implementing the BVAnalyticEvent protocol to a + dictionary. - @return The NSDictionary representation of this Bazaarvoice mobile analytic event. + @return The NSDictionary representation of this Bazaarvoice mobile analytic + event. */ -- (NSDictionary * _Nonnull)toRaw; +- (nonnull NSDictionary *)toRaw; @end - #endif /* BVAnalyticEvent_h */ diff --git a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.h b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.h index 6bd8b2f1..eb1826e5 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.h @@ -11,13 +11,13 @@ + (BVAnalyticEventManager *)sharedManager; -/// Sets the client ID to be used for all analytic events. Only set this if your client app does not use the BVSDKManager to initialize the SDK. -@property (strong, nonatomic) NSString *clientId; +/// Sets the client ID to be used for all analytic events. Only set this if your +/// client app does not use the BVSDKManager to initialize the SDK. +@property(strong, nonatomic) NSString *clientId; // Internal use only -@property (strong, nonatomic) NSString *eventSource; +@property(strong, nonatomic) NSString *eventSource; - (NSDictionary *)getCommonAnalyticsDictAnonymous:(BOOL)anonymous; - @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.m b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.m index 34ce9c44..5fabc061 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVAnalyticEventManager.m @@ -15,7 +15,7 @@ @interface BVAnalyticEventManager () -@property (strong, nonatomic) NSString *BVID; +@property(strong, nonatomic) NSString *BVID; @end @@ -23,58 +23,59 @@ @implementation BVAnalyticEventManager static BVAnalyticEventManager *mgrInstance = nil; -+ (BVAnalyticEventManager *) sharedManager { - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - mgrInstance = [[self alloc] init]; - }); - - return mgrInstance; -} ++ (BVAnalyticEventManager *)sharedManager { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mgrInstance = [[self alloc] init]; + }); -- (id) init { - self = [super init]; - if (self != nil) { - _eventSource = @"native-mobile-custom"; - self.BVID = [[NSUserDefaults standardUserDefaults] stringForKey:BVID_STORAGE_KEY]; - if(self.BVID == nil || [self.BVID length] == 0) { - self.BVID = [[NSUUID UUID] UUIDString]; - [[NSUserDefaults standardUserDefaults] setValue:self.BVID forKey:BVID_STORAGE_KEY]; - [[NSUserDefaults standardUserDefaults] synchronize]; - } + return mgrInstance; +} +- (id)init { + self = [super init]; + if (self != nil) { + _eventSource = @"native-mobile-custom"; + self.BVID = + [[NSUserDefaults standardUserDefaults] stringForKey:BVID_STORAGE_KEY]; + if (self.BVID == nil || [self.BVID length] == 0) { + self.BVID = [[NSUUID UUID] UUIDString]; + [[NSUserDefaults standardUserDefaults] setValue:self.BVID + forKey:BVID_STORAGE_KEY]; + [[NSUserDefaults standardUserDefaults] synchronize]; } - return self; + } + return self; } - - (NSDictionary *)getCommonAnalyticsDictAnonymous:(BOOL)anonymous { - - NSMutableDictionary* params = [NSMutableDictionary dictionaryWithDictionary:@{ - @"mobileSource": @"bv-ios-sdk", - @"HashedIP" : @"default", - @"source" : self.eventSource, - @"UA" : self.BVID - }]; - - NSAssert(self.clientId != nil, @"Client ID cannot be nil. The client ID is the Compnay name you use in the Bazaarvoice workbench."); - - [params setValue:self.clientId forKey:@"client"]; - - // idfa - //check it limit ad tracking is enabled - NSString *idfa = @"nontracking"; + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:@{ + @"mobileSource" : @"bv-ios-sdk", + @"HashedIP" : @"default", + @"source" : self.eventSource, + @"UA" : self.BVID + }]; + + NSAssert(self.clientId != nil, @"Client ID cannot be nil. The client ID is " + @"the Compnay name you use in the " + @"Bazaarvoice workbench."); + + [params setValue:self.clientId forKey:@"client"]; + + // idfa + // check it limit ad tracking is enabled + NSString *idfa = @"nontracking"; #ifndef DISABLE_BVSDK_IDFA - if([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled] && !anonymous){ - idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; - } + if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled] && + !anonymous) { + idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] + UUIDString]; + } #endif - - [params setValue:idfa forKey:@"advertisingId"]; - - return params; -} + [params setValue:idfa forKey:@"advertisingId"]; + + return params; +} @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.h index 27dd97df..f1c8ee21 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.h @@ -5,32 +5,33 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAnalyticEvent.h" +#import @interface BVBasePIIEvent : NSObject -- (nonnull instancetype)initWithParams:(NSDictionary * _Nullable)params; +- (nonnull instancetype)initWithParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /** - Creates a raw dictionary event that removes any possibly personally identifiable information. These events are fine to send with IDFA. - + Creates a raw dictionary event that removes any possibly personally + identifiable information. These events are fine to send with IDFA. + @param params The additional parameters added to the event creation - + @return The fully created event that can be sent to Bazaarvoice Analytics. */ --(NSDictionary * _Nonnull)getNonPII:(NSDictionary * _Nullable)params; - +- (nonnull NSDictionary *)getNonPII:(nullable NSDictionary *)params; /** Does this event have PII? - - @return True if there are additional parameters added that are not explicitly white-listed. + + @return True if there are additional parameters added that are not explicitly + white-listed. */ - (BOOL)hasPII; --(NSString * _Nonnull)getLoadId; +- (nonnull NSString *)getLoadId; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.m index 9346c3f4..fc95b720 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVBasePIIEvent.m @@ -7,71 +7,63 @@ #import "BVBasePIIEvent.h" -static NSSet* whitelistParams; +static NSSet *whitelistParams; @implementation BVBasePIIEvent @synthesize additionalParams; --(instancetype)initWithParams:(NSDictionary * _Nullable)params{ - - self = [super init]; - - if (self){ - sranddev(); - //these are considered the only non-PII params - whitelistParams= [[NSSet alloc]initWithObjects: @"orderId", @"affiliation", @"total", - @"tax", @"shipping", @"city", - @"state", @"country", @"currency", - @"items", @"locale", @"type", - @"label", @"value", @"proxy", - @"partnerSource", @"TestCase", @"TestSession", - @"dc",@"ref",nil]; - - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; - +- (instancetype)initWithParams:(nullable NSDictionary *)params { + self = [super init]; + + if (self) { + sranddev(); + // these are considered the only non-PII params + whitelistParams = [[NSSet alloc] + initWithObjects:@"orderId", @"affiliation", @"total", @"tax", + @"shipping", @"city", @"state", @"country", @"currency", + @"items", @"locale", @"type", @"label", @"value", + @"proxy", @"partnerSource", @"TestCase", @"TestSession", + @"dc", @"ref", nil]; + + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } -- (NSDictionary *)toRaw{ - NSAssert(NO, @"Should be implemented only by subclass"); - return nil; +- (NSDictionary *)toRaw { + NSAssert(NO, @"Should be implemented only by subclass"); + return nil; } --(NSDictionary*)getNonPII:(NSDictionary * _Nullable)params { - - NSMutableDictionary *nonPIIParmas = [NSMutableDictionary new]; - - if(params){ - for (NSString *key in params.allKeys){ - if ([whitelistParams containsObject:key]){ - [nonPIIParmas setObject:params[key] forKey:key]; - } - } +- (NSDictionary *)getNonPII:(nullable NSDictionary *)params { + NSMutableDictionary *nonPIIParmas = [NSMutableDictionary new]; + + if (params) { + for (NSString *key in params.allKeys) { + if ([whitelistParams containsObject:key]) { + [nonPIIParmas setObject:params[key] forKey:key]; + } } - return nonPIIParmas; + } + return nonPIIParmas; } - - (BOOL)hasPII { - - NSDictionary *nonPIIParams = [self getNonPII:self.additionalParams]; - return nonPIIParams.count != self.additionalParams.count; + NSDictionary *nonPIIParams = [self getNonPII:self.additionalParams]; + return nonPIIParams.count != self.additionalParams.count; } +- (NSString *)getLoadId { + int charLimit = 20; + NSMutableString *loadId = [NSMutableString new]; --(NSString*)getLoadId { - - int charLimit = 20; - NSMutableString* loadId = [NSMutableString new]; - - while (loadId.length < charLimit) { - [loadId appendFormat:@"%x", rand() % 16]; - } - - return loadId; + while (loadId.length < charLimit) { + [loadId appendFormat:@"%x", rand() % 16]; + } + + return loadId; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.h index a3ca34f3..54f72ffc 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.h @@ -6,56 +6,61 @@ // // -#import #import "BVAnalyticEvent.h" #import "BVBasePIIEvent.h" +#import -#define CONVERSION_SCHEMA @{@"cl": @"Conversion"} -#define CONVERSION_SCHEMA_PII @{@"cl": @"PIIConversion"} +#define CONVERSION_SCHEMA @{@"cl" : @"Conversion"} +#define CONVERSION_SCHEMA_PII @{@"cl" : @"PIIConversion"} /** - Known as a non-eCommerce conversion, this analytic event should be performed when an action such as the following takes place: - + Known as a non-eCommerce conversion, this analytic event should be performed + when an action such as the following takes place: + • Registering a user • Signing up for a subscription (paid or free) - • Downloading trial software, a whitepaper, or something else. This presumably will predispose people to progress in the sales funnel + • Downloading trial software, a whitepaper, or something else. This presumably + will predispose people to progress in the sales funnel • Requesting more information • Using a feature of an application - • Anything else that can be unambiguously counted by a computer and that you want users to do - + • Anything else that can be unambiguously counted by a computer and that you + want users to do + */ - @interface BVConversionEvent : BVBasePIIEvent +@interface BVConversionEvent : BVBasePIIEvent /** @param type Required - The type of conversion that is taking place. @param value Required - The total amount of the order. @param label Optional - A descriptive label to apply to the conversion. -@param params Optional - Used to define any other conversion parameters such as user email. - -@return the event object that can be used to submit to Bazaarvoice via the BVPixel API. +@param params Optional - Used to define any other conversion parameters such as +user email. + +@return the event object that can be used to submit to Bazaarvoice via the +BVPixel API. */ --(id _Nonnull)initWithType:(NSString* _Nonnull)type value:(NSString* _Nonnull)value label:(NSString* _Nullable)label otherParams:(NSDictionary* _Nullable)params; +- (nonnull id)initWithType:(nonnull NSString *)type + value:(nonnull NSString *)value + label:(nullable NSString *)label + otherParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /** - Creates a raw dictionary event that removes any possibly personally identifiable information. These events are fine to send with IDFA. - + Creates a raw dictionary event that removes any possibly personally + identifiable information. These events are fine to send with IDFA. + @return The fully created event that can be sent to Bazaarvoice Analytics. */ -- (NSDictionary * _Nonnull)toRawNonPII; - +- (nonnull NSDictionary *)toRawNonPII; /// The type of conversion that is taking place. -@property (nonatomic, strong, readonly) NSString* _Nonnull type; - +@property(nonnull, nonatomic, strong, readonly) NSString *type; /// The total amount of the order. -@property (nonatomic, strong, readonly) NSString* _Nonnull value; - +@property(nonnull, nonatomic, strong, readonly) NSString *value; /// A descriptive label to apply to the conversion. -@property (nonatomic, strong, readonly) NSString* _Nonnull label; - +@property(nonnull, nonatomic, strong, readonly) NSString *label; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.m index a746b1e6..255f13a0 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVConversionEvent.m @@ -13,78 +13,76 @@ @implementation BVConversionEvent @synthesize additionalParams; --(id _Nonnull)initWithType:(NSString* _Nonnull)type value:(NSString* _Nonnull)value label:(NSString* _Nullable)label otherParams:(NSDictionary* _Nullable)params{ - self = [super initWithParams:params]; - - NSAssert(type && type.length > 0, @"You must provide a type"); - NSAssert(value && value.length > 0, @"You must provide a value"); - - if (self) { - - _type = type; - _value = value; - _label = label; - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; +- (nonnull id)initWithType:(nonnull NSString *)type + value:(nonnull NSString *)value + label:(nullable NSString *)label + otherParams:(nullable NSDictionary *)params { + self = [super initWithParams:params]; + + NSAssert(type && type.length > 0, @"You must provide a type"); + NSAssert(value && value.length > 0, @"You must provide a value"); + + if (self) { + _type = type; + _value = value; + _label = label; + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict; + + if ([self hasPII]) { + eventDict = [self crateBaseEvent:YES]; + [eventDict addEntriesFromDictionary:CONVERSION_SCHEMA_PII]; + [eventDict setObject:@"true" forKey:@"hadPII"]; + } else { + eventDict = [self crateBaseEvent:NO]; + [eventDict addEntriesFromDictionary:CONVERSION_SCHEMA]; + } -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict; - - if ([self hasPII]){ - eventDict = [self crateBaseEvent:YES]; - [eventDict addEntriesFromDictionary:CONVERSION_SCHEMA_PII]; - [eventDict setObject:@"true" forKey:@"hadPII"]; - } else { - eventDict = [self crateBaseEvent:NO]; - [eventDict addEntriesFromDictionary:CONVERSION_SCHEMA]; - } - - [eventDict addEntriesFromDictionary:self.additionalParams]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; + [eventDict addEntriesFromDictionary:self.additionalParams]; + + return [NSDictionary dictionaryWithDictionary:eventDict]; } - (NSDictionary *)toRawNonPII { - - NSMutableDictionary *eventDict = [self crateBaseEvent:NO]; - - if ([self hasPII]){ - [eventDict setObject:@"true" forKey:@"hadPII"]; - } - - NSDictionary *nonPIIParams = [self getNonPII:self.additionalParams]; // strip out any non-whitelisted params that may contain personal identifiers - - [eventDict addEntriesFromDictionary:nonPIIParams]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; - -} + NSMutableDictionary *eventDict = [self crateBaseEvent:NO]; + + if ([self hasPII]) { + [eventDict setObject:@"true" forKey:@"hadPII"]; + } + NSDictionary *nonPIIParams = + [self getNonPII:self.additionalParams]; // strip out any non-whitelisted + // params that may contain + // personal identifiers + + [eventDict addEntriesFromDictionary:nonPIIParams]; + + return [NSDictionary dictionaryWithDictionary:eventDict]; +} - (NSMutableDictionary *)crateBaseEvent:(BOOL)anonymous { - - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.type, @"type", - self.value, @"value", - nil]; - - // Add nullable values - if (self.label) { - [eventDict setObject:self.label forKey:@"label"]; - } - - // Common event values implied for schema... - [eventDict setObject:[self getLoadId] forKey:@"loadId"]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:anonymous]]; - - return eventDict; - + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.type, @"type", self.value, @"value", + nil]; + + // Add nullable values + if (self.label) { + [eventDict setObject:self.label forKey:@"label"]; + } + + // Common event values implied for schema... + [eventDict setObject:[self getLoadId] forKey:@"loadId"]; + [eventDict + addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:anonymous]]; + + return eventDict; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.h index 202bd6e1..ab73ec9d 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.h @@ -5,44 +5,50 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAnalyticEvent.h" #import "BVPixelTypes.h" +#import -#define USED_FEATURE_SCHEMA @{@"cl": @"Feature",@"type": @"Used"} +#define USED_FEATURE_SCHEMA @{@"cl" : @"Feature", @"type" : @"Used"} @interface BVFeatureUsedEvent : NSObject /** Creates a Feature Used event for Bazaarvoice Mobile Analytics - - @param productId Required - The product ID used to request the display content. In the event a product Id is not availble, use the contentId, or "none" if no product id was used to display the data. - @prarm brand Optional - Brand of the product for which the user in interacting with + + @param productId Required - The product ID used to request the display + content. In the event a product Id is not availble, use the contentId, or + "none" if no product id was used to display the data. + @prarm brand Optional - Brand of the product for which the user in + interacting with @param bvProduct Required - The product with API key being used. - @param eventName Required - The actual user interaction that casued this event to be created. - @param params Optional - Additional key/value pairs to be send along the request. Most cases this will be nil. - - @return the event object that can be used to submit to Bazaarvoice via the BVPixel API. - */ --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withBrand:(NSString * _Nullable)brand - withProductType:(BVPixelProductType)bvProduct - withEventName:(BVPixelFeatureUsedEventName)eventName - withAdditionalParams:(NSDictionary * _Nullable)params; + @param eventName Required - The actual user interaction that casued this + event to be created. + @param params Optional - Additional key/value pairs to be send along the + request. Most cases this will be nil. -- (nonnull instancetype) __unavailable init; + @return the event object that can be used to submit to Bazaarvoice via the + BVPixel API. + */ +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withBrand:(nullable NSString *)brand + withProductType:(BVPixelProductType)bvProduct + withEventName:(BVPixelFeatureUsedEventName)eventName + withAdditionalParams:(nullable NSDictionary *)params; +- (nonnull instancetype)__unavailable init; /// The product Id for product presented. -@property (nonatomic, strong, readonly) NSString* _Nonnull productId; +@property(nonnull, nonatomic, strong, readonly) NSString *productId; /// Brand name for the product being viewed. -@property (nonatomic, strong, readonly) NSString * _Nullable brand; +@property(nullable, nonatomic, strong, readonly) NSString *brand; -/// The BV API used to request the product, as defined in the BVPixelProductType. -@property (nonatomic, assign, readonly) BVPixelProductType bvProduct; +/// The BV API used to request the product, as defined in the +/// BVPixelProductType. +@property(nonatomic, assign, readonly) BVPixelProductType bvProduct; /// The name of the user interaction that invoked the event. -@property (nonatomic, assign, readonly) BVPixelFeatureUsedEventName eventName; +@property(nonatomic, assign, readonly) BVPixelFeatureUsedEventName eventName; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.m index 49b3a78e..84fde973 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVFeatureUsedEvent.m @@ -12,51 +12,52 @@ @implementation BVFeatureUsedEvent @synthesize additionalParams; --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withBrand:(NSString * _Nullable)brand - withProductType:(BVPixelProductType)bvProduct - withEventName:(BVPixelFeatureUsedEventName)eventName - withAdditionalParams:(NSDictionary * _Nullable)params{ - - self = [super init]; - - if (self) - { - _productId = productId ? productId: @"unknown"; - _brand = brand; - _bvProduct = bvProduct; - _eventName = eventName; - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withBrand:(nullable NSString *)brand + withProductType:(BVPixelProductType)bvProduct + withEventName:(BVPixelFeatureUsedEventName)eventName + withAdditionalParams:(nullable NSDictionary *)params { + self = [super init]; + + if (self) { + _productId = productId ? productId : @"unknown"; + _brand = brand; + _bvProduct = bvProduct; + _eventName = eventName; + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.productId, @"productId", - [BVPixelProductTypeUtil toString:self.bvProduct], @"bvProduct", - [BVPixelFeatureUsedEventNameUtil toString:self.eventName], @"name", - nil]; - - // Add nullable values - if (self.brand){ - [eventDict addEntriesFromDictionary:@{@"brand":self.brand}]; - } - - if (_eventName == BVPixelFeatureUsedEventNameInView){ - [eventDict addEntriesFromDictionary:@{@"interaction":@"false"}]; - } else { - [eventDict addEntriesFromDictionary:@{@"interaction":@"true"}]; - } - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - [eventDict addEntriesFromDictionary:USED_FEATURE_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.productId, @"productId", + [BVPixelProductTypeUtil + toString:self.bvProduct], + @"bvProduct", + [BVPixelFeatureUsedEventNameUtil + toString:self.eventName], + @"name", nil]; + + // Add nullable values + if (self.brand) { + [eventDict addEntriesFromDictionary:@{@"brand" : self.brand}]; + } + + if (_eventName == BVPixelFeatureUsedEventNameInView) { + [eventDict addEntriesFromDictionary:@{@"interaction" : @"false"}]; + } else { + [eventDict addEntriesFromDictionary:@{@"interaction" : @"true"}]; + } + + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + [eventDict addEntriesFromDictionary:USED_FEATURE_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; + + return [NSDictionary dictionaryWithDictionary:eventDict]; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.h index f1aebca1..575b715e 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.h @@ -5,51 +5,57 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAnalyticEvent.h" #import "BVPixelTypes.h" +#import -#define UGC_IMPRESSION_SCHEMA @{@"cl": @"Impression",@"type": @"UGC"} +#define UGC_IMPRESSION_SCHEMA @{@"cl" : @"Impression", @"type" : @"UGC"} @interface BVImpressionEvent : NSObject /** @param productId Required - Product external ID - @param contentId Required - The identifier for the unique piece of user-generated content + @param contentId Required - The identifier for the unique piece of + user-generated content @param categoryId Optional - Id of the CGC content @param bvProduct Required - The product with API key being used. - @param contentType Required - The type of content being requested by the API call. - @param brand Optional - Brand name of what the content (review, question, etc) is about. - @param params Optional - Additional key/value pairs to be send along the request. Most cases this will be nil. - - @return the event object that can be used to submit to Bazaarvoice via the BVPixel API. + @param contentType Required - The type of content being requested by the API + call. + @param brand Optional - Brand name of what the content (review, question, + etc) is about. + @param params Optional - Additional key/value pairs to be send along the + request. Most cases this will be nil. + + @return the event object that can be used to submit to Bazaarvoice via the + BVPixel API. */ --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withContentId:(NSString * _Nonnull)contentId - withCategoryId:(NSString * _Nullable)categoryId - withProductType:(BVPixelProductType)bvProduct +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withContentId:(nonnull NSString *)contentId + withCategoryId:(nullable NSString *)categoryId + withProductType:(BVPixelProductType)bvProduct withContentType:(BVPixelImpressionContentType)contentType - withBrand:(NSString * _Nullable)brand - withAdditionalParams:(NSDictionary * _Nullable)params; + withBrand:(nullable NSString *)brand + withAdditionalParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /// The product Id for product presented. -@property (nonatomic, strong, readonly) NSString* _Nonnull productId; +@property(nonnull, nonatomic, strong, readonly) NSString *productId; /// The identifier for the unique piece of user-generated content -@property (nonatomic, strong, readonly) NSString* _Nonnull contentId; +@property(nonnull, nonatomic, strong, readonly) NSString *contentId; -/// The BV API used to request the product, as defined in the BVPixelProductType. -@property (nonatomic, assign, readonly) BVPixelProductType bvProduct; +/// The BV API used to request the product, as defined in the +/// BVPixelProductType. +@property(nonatomic, assign, readonly) BVPixelProductType bvProduct; /// The category Id, if known, for the product presented. -@property (nonatomic, strong, readonly) NSString* _Nullable categoryId; +@property(nullable, nonatomic, strong, readonly) NSString *categoryId; /// Brand name for the product being viewed. -@property (nonatomic, strong, readonly) NSString * _Nullable brand; +@property(nullable, nonatomic, strong, readonly) NSString *brand; /// The individual piece of content viewed -@property (nonatomic, assign, readonly) BVPixelImpressionContentType contentType; +@property(nonatomic, assign, readonly) BVPixelImpressionContentType contentType; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.m index 2d8ce84f..f9e6e50d 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVImpressionEvent.m @@ -12,56 +12,54 @@ @implementation BVImpressionEvent @synthesize additionalParams; --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withContentId:(NSString * _Nonnull)contentId - withCategoryId:(NSString * _Nullable)categoryId - withProductType:(BVPixelProductType)bvProduct +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withContentId:(nonnull NSString *)contentId + withCategoryId:(nullable NSString *)categoryId + withProductType:(BVPixelProductType)bvProduct withContentType:(BVPixelImpressionContentType)contentType - withBrand:(NSString * _Nullable)brand - withAdditionalParams:(NSDictionary * _Nullable)params { - - self = [super init]; - - if (self) - { - _productId = productId ? productId: @"unknown"; - _contentId = contentId ? contentId: @"unknown"; - _categoryId = categoryId; - _bvProduct = bvProduct; - _contentType = contentType; - _brand = brand; - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; - + withBrand:(nullable NSString *)brand + withAdditionalParams:(nullable NSDictionary *)params { + self = [super init]; + + if (self) { + _productId = productId ? productId : @"unknown"; + _contentId = contentId ? contentId : @"unknown"; + _categoryId = categoryId; + _bvProduct = bvProduct; + _contentType = contentType; + _brand = brand; + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.productId, @"productId", self.contentId, + @"contentId", + [BVPixelProductTypeUtil + toString:self.bvProduct], + @"bvProduct", + [BVPixelImpressionContentTypeUtil + toString:self.contentType], + @"contentType", nil]; + + // Add nullable values + if (self.categoryId) { + [eventDict addEntriesFromDictionary:@{@"categoryId" : self.categoryId}]; + } + + if (self.brand) { + [eventDict addEntriesFromDictionary:@{@"brand" : self.brand}]; + } + + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + [eventDict addEntriesFromDictionary:UGC_IMPRESSION_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.productId, @"productId", - self.contentId, @"contentId", - [BVPixelProductTypeUtil toString:self.bvProduct], @"bvProduct", - [BVPixelImpressionContentTypeUtil toString:self.contentType], @"contentType", - nil]; - - // Add nullable values - if (self.categoryId){ - [eventDict addEntriesFromDictionary:@{@"categoryId":self.categoryId}]; - } - - if (self.brand){ - [eventDict addEntriesFromDictionary:@{@"brand":self.brand}]; - } - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - [eventDict addEntriesFromDictionary:UGC_IMPRESSION_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; - + return [NSDictionary dictionaryWithDictionary:eventDict]; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.h index 1bb40dd4..42668425 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.h @@ -5,45 +5,48 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAnalyticEvent.h" #import "BVPixelTypes.h" +#import -#define FEATURE_USED_INVIEW_SCHEMA @{@"cl": @"Used",@"type": @"Feature",@"name":@"InView"} +#define FEATURE_USED_INVIEW_SCHEMA \ + @{@"cl" : @"Used", @"type" : @"Feature", @"name" : @"InView"} @interface BVInViewEvent : NSObject - /** Data used to track when a view is visible to the user. @param productId Required - Product external ID @param brand Optional - The brand name of the product. @param bvProduct Required - The product with API key being used. - @param containerId Required - The name of the container on screen, e.g. ReviewsView - @param params Optional - Additional key/value pairs to be send along the request. Most cases this will be nil. + @param containerId Required - The name of the container on screen, e.g. + ReviewsView + @param params Optional - Additional key/value pairs to be send along the + request. Most cases this will be nil. - @return the event object that can be used to submit to Bazaarvoice via the BVPixel API. + @return the event object that can be used to submit to Bazaarvoice via the + BVPixel API. */ --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withBrand:(NSString * _Nullable)brand - withProductType:(BVPixelProductType)bvProduct - withContainerId:(NSString * _Nonnull)containerId - withAdditionalParams:(NSDictionary * _Nullable)params; - +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withBrand:(nullable NSString *)brand + withProductType:(BVPixelProductType)bvProduct + withContainerId:(nonnull NSString *)containerId + withAdditionalParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /// The product Id for product presented. -@property (nonatomic, strong, readonly) NSString* _Nonnull productId; +@property(nonnull, nonatomic, strong, readonly) NSString *productId; /// Brand name for the product being viewed. -@property (nonatomic, strong, readonly) NSString * _Nullable brand; +@property(nullable, nonatomic, strong, readonly) NSString *brand; -/// The BV API used to request the product, as defined in the BVPixelProductType. -@property (nonatomic, assign, readonly) BVPixelProductType bvProduct; +/// The BV API used to request the product, as defined in the +/// BVPixelProductType. +@property(nonatomic, assign, readonly) BVPixelProductType bvProduct; /// The name of the container on screen, e.g. ReviewsView -@property (nonatomic, strong, readonly) NSString * _Nullable containerId; +@property(nullable, nonatomic, strong, readonly) NSString *containerId; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.m index 863f2a77..e0c13586 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVInViewEvent.m @@ -6,54 +6,51 @@ // #import "BVInViewEvent.h" -#import "BVPixelTypes.h" #import "BVAnalyticEventManager.h" +#import "BVPixelTypes.h" @implementation BVInViewEvent @synthesize additionalParams; --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withBrand:(NSString * _Nullable)brand - withProductType:(BVPixelProductType)bvProduct - withContainerId:(NSString * _Nonnull)containerId - withAdditionalParams:(NSDictionary * _Nullable)params{ - - self = [super init]; - - if (self) - { - _productId = productId ? productId: @"unknown"; - _brand = brand; - _bvProduct = bvProduct; - _containerId = containerId; - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; - +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withBrand:(nullable NSString *)brand + withProductType:(BVPixelProductType)bvProduct + withContainerId:(nonnull NSString *)containerId + withAdditionalParams:(nullable NSDictionary *)params { + self = [super init]; + + if (self) { + _productId = productId ? productId : @"unknown"; + _brand = brand; + _bvProduct = bvProduct; + _containerId = containerId; + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.productId, @"productId", + [BVPixelProductTypeUtil + toString:self.bvProduct], + @"bvProduct", self.containerId, @"component", + nil]; + + // Add nullable values + if (self.brand != nil) { + [eventDict addEntriesFromDictionary:@{@"brand" : self.brand}]; + } + + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + [eventDict addEntriesFromDictionary:FEATURE_USED_INVIEW_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.productId, @"productId", - [BVPixelProductTypeUtil toString:self.bvProduct], @"bvProduct", - self.containerId, @"component", - nil]; - - // Add nullable values - if (self.brand != nil){ - [eventDict addEntriesFromDictionary:@{@"brand":self.brand}]; - } - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - [eventDict addEntriesFromDictionary:FEATURE_USED_INVIEW_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; + return [NSDictionary dictionaryWithDictionary:eventDict]; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.h index 211dbcc4..974e2edf 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.h @@ -7,10 +7,10 @@ #import -#import "BVPixelTypes.h" #import "BVAnalyticEvent.h" +#import "BVPixelTypes.h" -#define PRODUCT_PAGEVIEW_SCHEMA @{@"cl": @"PageView",@"type": @"Product"} +#define PRODUCT_PAGEVIEW_SCHEMA @{@"cl" : @"PageView", @"type" : @"Product"} @interface BVPageViewEvent : NSObject @@ -18,34 +18,39 @@ @param productId Required - Product external ID @param bvProduct Required - The product with API key being used. @param brand Optional - Brand name for which the PageView is about. - @param categoryId Optional - The category Id for the product, e.g. 'Electronics_Helmet_Cameras' - @param rootCategoryId Optional - Root cateogry for the product, e.g. 'electronics' - @param params Optional - Additional key/value pairs to be send along the request. Most cases this will be nil. - -@return the event object that can be used to submit to Bazaarvoice via the BVPixel API. + @param categoryId Optional - The category Id for the product, e.g. +'Electronics_Helmet_Cameras' + @param rootCategoryId Optional - Root cateogry for the product, e.g. +'electronics' + @param params Optional - Additional key/value pairs to be send along the +request. Most cases this will be nil. + +@return the event object that can be used to submit to Bazaarvoice via the +BVPixel API. */ --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId +- (nonnull id)initWithProductId:(nonnull NSString *)productId withBVPixelProductType:(BVPixelProductType)bvProduct - withBrand:(NSString * _Nullable)brand - withCategoryId:(NSString * _Nullable)categoryId - withRootCategoryId:(NSString * _Nullable)rootCategoryId - withAdditionalParams:(NSDictionary * _Nullable)params; + withBrand:(nullable NSString *)brand + withCategoryId:(nullable NSString *)categoryId + withRootCategoryId:(nullable NSString *)rootCategoryId + withAdditionalParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /// The product Id for product presented. -@property (nonatomic, strong, readonly) NSString* _Nonnull productId; +@property(nonnull, nonatomic, strong, readonly) NSString *productId; -/// The BV API used to request the product, as defined in the BVPixelProductType. -@property (nonatomic, assign, readonly) BVPixelProductType bvProduct; +/// The BV API used to request the product, as defined in the +/// BVPixelProductType. +@property(nonatomic, assign, readonly) BVPixelProductType bvProduct; /// The category Id, if known, for the product presented. -@property (nonatomic, strong, readonly) NSString* _Nullable categoryId; +@property(nullable, nonatomic, strong, readonly) NSString *categoryId; /// The root category Id, if known, for the product presented. -@property (nonatomic, strong, readonly) NSString* _Nullable rootCategoryId; +@property(nullable, nonatomic, strong, readonly) NSString *rootCategoryId; /// Brand name for the product being viewed. -@property (nonatomic, strong, readonly) NSString * _Nullable brand; +@property(nullable, nonatomic, strong, readonly) NSString *brand; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.m index aab47e9c..595d9071 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVPageViewEvent.m @@ -12,58 +12,57 @@ @implementation BVPageViewEvent @synthesize additionalParams; --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId +- (nonnull id)initWithProductId:(nonnull NSString *)productId withBVPixelProductType:(BVPixelProductType)bvProduct - withBrand:(NSString * _Nullable)brand - withCategoryId:(NSString * _Nullable)categoryId - withRootCategoryId:(NSString * _Nullable)rootCategoryId - withAdditionalParams:(NSDictionary * _Nullable)params; + withBrand:(nullable NSString *)brand + withCategoryId:(nullable NSString *)categoryId + withRootCategoryId:(nullable NSString *)rootCategoryId + withAdditionalParams:(nullable NSDictionary *)params; { - - NSAssert(productId, @"productId cannot be nil"); - - self = [super init]; - - if (self) - { - _productId = productId == nil ? @"unknown" : productId; - _bvProduct = bvProduct; - _brand = brand; - _categoryId = categoryId; - _rootCategoryId = rootCategoryId; - self.additionalParams = params ? params : [NSDictionary dictionary]; - } - - return self; - + NSAssert(productId, @"productId cannot be nil"); + + self = [super init]; + + if (self) { + _productId = productId == nil ? @"unknown" : productId; + _bvProduct = bvProduct; + _brand = brand; + _categoryId = categoryId; + _rootCategoryId = rootCategoryId; + self.additionalParams = params ? params : [NSDictionary dictionary]; + } + + return self; } -- (NSDictionary *)toRaw { +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.productId, @"productId", + [BVPixelProductTypeUtil + toString:self.bvProduct], + @"bvProduct", nil]; + + // Add nullable values + if (self.categoryId) { + [eventDict addEntriesFromDictionary:@{@"categoryId" : self.categoryId}]; + } + + if (self.rootCategoryId) { + [eventDict + addEntriesFromDictionary:@{@"rootCategoryId" : self.rootCategoryId}]; + } + + if (self.brand) { + [eventDict addEntriesFromDictionary:@{@"brand" : self.brand}]; + } + + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + [eventDict addEntriesFromDictionary:PRODUCT_PAGEVIEW_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.productId, @"productId", - [BVPixelProductTypeUtil toString:self.bvProduct], @"bvProduct", - nil]; - - // Add nullable values - if (self.categoryId){ - [eventDict addEntriesFromDictionary:@{@"categoryId":self.categoryId}]; - } - - if (self.rootCategoryId){ - [eventDict addEntriesFromDictionary:@{@"rootCategoryId":self.rootCategoryId}]; - } - - if (self.brand){ - [eventDict addEntriesFromDictionary:@{@"brand":self.brand}]; - } - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - [eventDict addEntriesFromDictionary:PRODUCT_PAGEVIEW_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; + return [NSDictionary dictionaryWithDictionary:eventDict]; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.h index 6109ee7d..f3d6c6c2 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.h @@ -5,22 +5,30 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAnalyticEvent.h" +#import -#define PERSONALIZATION_SCHEMA @{@"cl": @"Personalization",@"type": @"ProfileMobile",@"bvProduct": @"ShopperMarketing"} +#define PERSONALIZATION_SCHEMA \ + @{ \ + @"cl" : @"Personalization", \ + @"type" : @"ProfileMobile", \ + @"bvProduct" : @"ShopperMarketing" \ + } /** - Set user information. Associates a user profile with device for taylored advertising and recommendations. - Use of this method requires that a valid key has been set for apiKeyShopperMarketing. + Set user information. Associates a user profile with device for taylored + advertising and recommendations. + Use of this method requires that a valid key has been set for + apiKeyShopperMarketing. */ @interface BVPersonalizationEvent : NSObject -- (id _Nonnull)initWithUserAuthenticationString:(NSString * _Nonnull)uas; +- (nonnull id)initWithUserAuthenticationString:(nonnull NSString *)uas; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; -/// Generated user authentication string endoded with shared secret. Generated by your server-side implementation. -@property (nonatomic, strong, readonly) NSString* _Nonnull uas; +/// Generated user authentication string endoded with shared secret. Generated +/// by your server-side implementation. +@property(nonnull, nonatomic, strong, readonly) NSString *uas; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.m index eb2b7b68..d5f01190 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVPersonalizationEvent.m @@ -12,28 +12,28 @@ @implementation BVPersonalizationEvent @synthesize additionalParams; -- (id _Nonnull)initWithUserAuthenticationString:(NSString * _Nonnull)uas{ - - self = [super init]; - if (self){ - - _uas = uas; - } - - return self; +- (nonnull id)initWithUserAuthenticationString:(nonnull NSString *)uas { + + self = [super init]; + if (self) { + + _uas = uas; + } + + return self; } -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.uas, @"profileId", - nil]; - - [eventDict addEntriesFromDictionary:PERSONALIZATION_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - [eventDict addEntriesFromDictionary:@{@"source":@"ProfileMobile"}]; - - return eventDict; +- (NSDictionary *)toRaw { + + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.uas, @"profileId", nil]; + + [eventDict addEntriesFromDictionary:PERSONALIZATION_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; + [eventDict addEntriesFromDictionary:@{@"source" : @"ProfileMobile"}]; + + return eventDict; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPixelEvents.h b/Pod/BVAnalytics/BVPixelEvents/BVPixelEvents.h index 37767e6e..0780c44c 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPixelEvents.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVPixelEvents.h @@ -8,7 +8,6 @@ #ifndef BVPixelEvents_h #define BVPixelEvents_h -#import "BVViewedCGCEvent.h" #import "BVAnalyticEvent.h" #import "BVAnalyticEventManager.h" #import "BVConversionEvent.h" @@ -20,5 +19,6 @@ #import "BVPixelTypes.h" #import "BVTransactionEvent.h" #import "BVTransactionItem.h" +#import "BVViewedCGCEvent.h" #endif /* BVPixelEvents_h */ diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.h b/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.h index faf4dd51..3629d0c5 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.h @@ -5,68 +5,64 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // - #import -/** +/** The types of user-interaction events that can be fired. */ typedef NS_ENUM(NSInteger, BVPixelFeatureUsedEventName) { - BVPixelFeatureUsedEventNameWriteReview, - BVPixelFeatureUsedEventNameAskQuestion, - BVPixelFeatureUsedEventNameAnswerQuestion, - BVPixelFeatureUsedEventNameReviewComment, - BVPixelFeatureUsedEventNameFeedback, - BVPixelFeatureUsedEventNameInappropriate, - BVPixelFeatureUsedEventNamePhoto, - BVPixelFeatureUsedEventNameScrolled, - BVPixelFeatureUsedEventContentClick, - BVPixelFeatureUsedEventNameInView, - BVPixelFeatureUsedNameProfile + BVPixelFeatureUsedEventNameWriteReview, + BVPixelFeatureUsedEventNameAskQuestion, + BVPixelFeatureUsedEventNameAnswerQuestion, + BVPixelFeatureUsedEventNameReviewComment, + BVPixelFeatureUsedEventNameFeedback, + BVPixelFeatureUsedEventNameInappropriate, + BVPixelFeatureUsedEventNamePhoto, + BVPixelFeatureUsedEventNameScrolled, + BVPixelFeatureUsedEventContentClick, + BVPixelFeatureUsedEventNameInView, + BVPixelFeatureUsedNameProfile }; @interface BVPixelFeatureUsedEventNameUtil : NSObject -+(NSString*)toString:(BVPixelFeatureUsedEventName)Featuretype; ++ (NSString *)toString:(BVPixelFeatureUsedEventName)Featuretype; @end - /** The UGC content types individual impressions. */ typedef NS_ENUM(NSInteger, BVPixelImpressionContentType) { - BVPixelImpressionContentTypeReview, - BVPixelImpressionContentTypeQuestion, - BVPixelImpressionContentTypeAnswer, - BVPixelImpressionContentTypeComment, - BVPixelImpressionContentProductRecommendation, - BVPixelImpressionContentCurationsFeedItem + BVPixelImpressionContentTypeReview, + BVPixelImpressionContentTypeQuestion, + BVPixelImpressionContentTypeAnswer, + BVPixelImpressionContentTypeComment, + BVPixelImpressionContentProductRecommendation, + BVPixelImpressionContentCurationsFeedItem }; @interface BVPixelImpressionContentTypeUtil : NSObject -+(NSString*)toString:(BVPixelImpressionContentType)contentType; ++ (NSString *)toString:(BVPixelImpressionContentType)contentType; @end - /** Types of Bazaarvoice products supported in this SDK. */ typedef NS_ENUM(NSInteger, BVPixelProductType) { - BVPixelProductTypeConversationsReviews, - BVPixelProductTypeConversationsQuestionAnswer, - BVPixelProductTypeConversationsProfile, - BVPixelProductTypeCurations, - BVPixelProductTypeProductRecommendations, - BVPixelProductTypePIN, - BVPixelProductTypeLocation + BVPixelProductTypeConversationsReviews, + BVPixelProductTypeConversationsQuestionAnswer, + BVPixelProductTypeConversationsProfile, + BVPixelProductTypeCurations, + BVPixelProductTypeProductRecommendations, + BVPixelProductTypePIN, + BVPixelProductTypeLocation }; @interface BVPixelProductTypeUtil : NSObject -+(NSString*)toString:(BVPixelProductType)type; ++ (NSString *)toString:(BVPixelProductType)type; @end - diff --git a/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.m b/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.m index 3a203842..99b934be 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVPixelTypes.m @@ -7,99 +7,90 @@ #import "BVPixelTypes.h" - @implementation BVPixelFeatureUsedEventNameUtil -+(NSString *)toString:(BVPixelFeatureUsedEventName)featuretype{ - - switch (featuretype) { - - case BVPixelFeatureUsedEventNameWriteReview: - return @"Write"; - case BVPixelFeatureUsedEventNameAskQuestion: - return @"Question"; - case BVPixelFeatureUsedEventNameAnswerQuestion: - return @"Answer"; - case BVPixelFeatureUsedEventNameReviewComment: - return @"Comment"; - case BVPixelFeatureUsedEventNameFeedback: - return @"Helpfulness"; - case BVPixelFeatureUsedEventNameInappropriate: - return @"Inappropriate"; - case BVPixelFeatureUsedEventNamePhoto: - return @"Photo"; - case BVPixelFeatureUsedEventNameInView: - return @"InView"; - case BVPixelFeatureUsedEventNameScrolled: - return @"Scrolled"; - case BVPixelFeatureUsedEventContentClick: - return @"Click"; - case BVPixelFeatureUsedNameProfile: - return @"Profile"; - default: - return @"Unknown"; - break; - - } - ++ (NSString *)toString:(BVPixelFeatureUsedEventName)featuretype { + switch (featuretype) { + case BVPixelFeatureUsedEventNameWriteReview: + return @"Write"; + case BVPixelFeatureUsedEventNameAskQuestion: + return @"Question"; + case BVPixelFeatureUsedEventNameAnswerQuestion: + return @"Answer"; + case BVPixelFeatureUsedEventNameReviewComment: + return @"Comment"; + case BVPixelFeatureUsedEventNameFeedback: + return @"Helpfulness"; + case BVPixelFeatureUsedEventNameInappropriate: + return @"Inappropriate"; + case BVPixelFeatureUsedEventNamePhoto: + return @"Photo"; + case BVPixelFeatureUsedEventNameInView: + return @"InView"; + case BVPixelFeatureUsedEventNameScrolled: + return @"Scrolled"; + case BVPixelFeatureUsedEventContentClick: + return @"Click"; + case BVPixelFeatureUsedNameProfile: + return @"Profile"; + default: + return @"Unknown"; + break; + } } @end @implementation BVPixelImpressionContentTypeUtil -+(NSString *)toString:(BVPixelImpressionContentType)contentType{ - - switch (contentType) { - case BVPixelImpressionContentTypeAnswer: - return @"Answer"; - break; - case BVPixelImpressionContentTypeQuestion: - return @"Question"; - break; - case BVPixelImpressionContentTypeReview: - return @"Review"; - break; - case BVPixelImpressionContentTypeComment: - return @"Comment"; - break; - case BVPixelImpressionContentCurationsFeedItem: - return @"SocialPost"; - break; - case BVPixelImpressionContentProductRecommendation: - return @"Recommendation"; - default: - return @"Unknown"; - break; - } - ++ (NSString *)toString:(BVPixelImpressionContentType)contentType { + switch (contentType) { + case BVPixelImpressionContentTypeAnswer: + return @"Answer"; + break; + case BVPixelImpressionContentTypeQuestion: + return @"Question"; + break; + case BVPixelImpressionContentTypeReview: + return @"Review"; + break; + case BVPixelImpressionContentTypeComment: + return @"Comment"; + break; + case BVPixelImpressionContentCurationsFeedItem: + return @"SocialPost"; + break; + case BVPixelImpressionContentProductRecommendation: + return @"Recommendation"; + default: + return @"Unknown"; + break; + } } @end @implementation BVPixelProductTypeUtil -+(NSString*)toString:(BVPixelProductType)productType { - - switch (productType) { - case BVPixelProductTypeConversationsReviews: - return @"RatingsAndReviews"; - case BVPixelProductTypeConversationsQuestionAnswer: - return @"AskAndAnswer"; - case BVPixelProductTypeConversationsProfile: - return @"Profiles"; - case BVPixelProductTypeLocation: - return @"Location"; - case BVPixelProductTypePIN: - return @"PIN"; - case BVPixelProductTypeProductRecommendations: - return @"Recommendations"; - case BVPixelProductTypeCurations: - return @"Curations"; - default: - return @"UnknownProduct"; - } - ++ (NSString *)toString:(BVPixelProductType)productType { + switch (productType) { + case BVPixelProductTypeConversationsReviews: + return @"RatingsAndReviews"; + case BVPixelProductTypeConversationsQuestionAnswer: + return @"AskAndAnswer"; + case BVPixelProductTypeConversationsProfile: + return @"Profiles"; + case BVPixelProductTypeLocation: + return @"Location"; + case BVPixelProductTypePIN: + return @"PIN"; + case BVPixelProductTypeProductRecommendations: + return @"Recommendations"; + case BVPixelProductTypeCurations: + return @"Curations"; + default: + return @"UnknownProduct"; + } } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.h index f83eb882..243e387f 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.h @@ -5,73 +5,75 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import -#import "BVTransactionItem.h" #import "BVAnalyticEvent.h" #import "BVBasePIIEvent.h" +#import "BVTransactionItem.h" +#import -#define TRANSACTION_SCHEMA @{@"cl": @"Conversion",@"type": @"Transaction"} -#define TRANSACTION_SCHEMA_PII @{@"cl": @"PIIConversion",@"type": @"Transaction"} +#define TRANSACTION_SCHEMA @{@"cl" : @"Conversion", @"type" : @"Transaction"} +#define TRANSACTION_SCHEMA_PII \ + @{@"cl" : @"PIIConversion", @"type" : @"Transaction"} /** - The trackConversionTransaction event should execute once the user has completed a purchase. This will often be after a payment has been processed or when loading of a payment confirmation view. + The trackConversionTransaction event should execute once the user has + completed a purchase. This will often be after a payment has been processed or + when loading of a payment confirmation view. */ @interface BVTransactionEvent : BVBasePIIEvent /** @param orderId Required - The unique Id of the order. @param total Required - The total amount of the order. -@param items Required - [BVTransactionItem] -An array of the individual items in the order. -@param params Optional - Used to define any other transaction parameters such as user email. - -@return the event object that can be used to submit to Bazaarvoice via the BVPixel API. +@param items Required - [BVTransactionItem] -An array of the individual items in +the order. +@param params Optional - Used to define any other transaction parameters such as +user email. + +@return the event object that can be used to submit to Bazaarvoice via the +BVPixel API. */ --(id _Nonnull)initWithOrderId:(NSString* _Nonnull)orderId orderTotal:(double)total orderItems:(NSArray* _Nonnull)items andOtherParams:(NSDictionary* _Nullable)params; +- (nonnull id)initWithOrderId:(nonnull NSString *)orderId + orderTotal:(double)total + orderItems:(nonnull NSArray *)items + andOtherParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /** - Creates a raw dictionary event that removes any possibly personally identifiable information. These events are fine to send with IDFA. - + Creates a raw dictionary event that removes any possibly personally + identifiable information. These events are fine to send with IDFA. + @return The fully created event that can be sent to Bazaarvoice Analytics. */ -- (NSDictionary * _Nonnull)toRawNonPII; +- (nonnull NSDictionary *)toRawNonPII; /// The unique Id of the order. -@property (nonatomic, strong, readonly) NSString* _Nonnull orderId; - +@property(nonnull, nonatomic, strong, readonly) NSString *orderId; /// The total amount of the order. -@property (nonatomic, assign, readonly) double total; - +@property(nonatomic, assign, readonly) double total; /// [BVTransactionItem] - An array of the individual items in the order. -@property (nonatomic, strong, readonly) NSArray* _Nonnull items; - +@property(nonnull, nonatomic, strong, readonly) + NSArray *items; /// The tax amount of the order. -@property (nonatomic, assign) double tax; - +@property(nonatomic, assign) double tax; /// The shipping cost of the order. -@property (nonatomic, assign) double shipping; - +@property(nonatomic, assign) double shipping; /// The user's city -@property (nonatomic, strong) NSString* _Nullable city; - +@property(nullable, nonatomic, strong) NSString *city; /// The user's state -@property (nonatomic, strong) NSString* _Nullable state; - +@property(nullable, nonatomic, strong) NSString *state; /// The user's country -@property (nonatomic, strong) NSString* _Nullable country; - +@property(nullable, nonatomic, strong) NSString *country; /// The currency used of the order. /// ISO 4217 alphabetic currency code. -@property (nonatomic, strong) NSString* _Nullable currency; - +@property(nullable, nonatomic, strong) NSString *currency; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.m index a31aff18..e6899cf1 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVTransactionEvent.m @@ -10,110 +10,111 @@ @implementation BVTransactionEvent --(id _Nonnull)initWithOrderId:(NSString* _Nonnull)orderId orderTotal:(double)total orderItems:(NSArray* _Nonnull)items andOtherParams:(NSDictionary* _Nullable)params{ - - NSAssert(orderId,@"orderId cannot be nil"); - NSAssert(items, @"items cannot be nil"); - - self = [super initWithParams:params]; - - if (self) - { - _orderId = orderId; - _total = total; - _items = items; - } - - return self; +- (nonnull id)initWithOrderId:(nonnull NSString *)orderId + orderTotal:(double)total + orderItems:(nonnull NSArray *)items + andOtherParams:(nullable NSDictionary *)params { + NSAssert(orderId, @"orderId cannot be nil"); + NSAssert(items, @"items cannot be nil"); + + self = [super initWithParams:params]; + + if (self) { + _orderId = orderId; + _total = total; + _items = items; + } + + return self; } +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict; + + if ([self hasPII]) { + eventDict = [self crateBaseEvent:YES]; + [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA_PII]; + [eventDict setObject:@"true" forKey:@"hadPII"]; + } else { + eventDict = [self crateBaseEvent:NO]; + [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA]; + } + + [eventDict addEntriesFromDictionary:self.additionalParams]; -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict; - - if ([self hasPII]){ - eventDict = [self crateBaseEvent:YES]; - [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA_PII]; - [eventDict setObject:@"true" forKey:@"hadPII"]; - } else { - eventDict = [self crateBaseEvent:NO]; - [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA]; - } - - [eventDict addEntriesFromDictionary:self.additionalParams]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; + return [NSDictionary dictionaryWithDictionary:eventDict]; } - (NSDictionary *)toRawNonPII { - - NSMutableDictionary *eventDict = [self crateBaseEvent:NO]; - - [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA]; - - if ([self hasPII]){ - [eventDict setObject:@"true" forKey:@"hadPII"]; - } - - NSDictionary *nonPIIParams = [self getNonPII:self.additionalParams]; // strip out any non-whitelisted params that may contain personal identifiers - - [eventDict addEntriesFromDictionary:nonPIIParams]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; - + NSMutableDictionary *eventDict = [self crateBaseEvent:NO]; + + [eventDict addEntriesFromDictionary:TRANSACTION_SCHEMA]; + + if ([self hasPII]) { + [eventDict setObject:@"true" forKey:@"hadPII"]; + } + + NSDictionary *nonPIIParams = + [self getNonPII:self.additionalParams]; // strip out any non-whitelisted + // params that may contain + // personal identifiers + + [eventDict addEntriesFromDictionary:nonPIIParams]; + + return [NSDictionary dictionaryWithDictionary:eventDict]; } // All transaction events are created from a base set of properties. - (NSMutableDictionary *)crateBaseEvent:(BOOL)anonymous { - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.orderId, @"orderId", - nil]; - - - [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.total] forKey:@"total"]; - - // Add nullable values - if (self.tax){ - [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.tax] forKey:@"tax"]; - } - - if (self.shipping){ - [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.shipping] forKey:@"shipping"]; - } - - if (self.city){ - [eventDict setObject:self.city forKey:@"city"]; - } - - if (self.state){ - [eventDict setObject:self.state forKey:@"state"]; - } - - if (self.country){ - [eventDict setObject:self.country forKey:@"country"]; - } - - if (self.currency){ - [eventDict setObject:self.city forKey:@"currency"]; - } - - // convert and add Transaction Items - NSMutableArray *serializedItems = [NSMutableArray new]; - for(BVTransactionItem *item in self.items){ - NSDictionary *itemParams = [item toRaw]; - [serializedItems addObject:itemParams]; - } - - [eventDict setObject:serializedItems forKey:@"items"]; - - // Common event values implied for schema... - [eventDict setObject:[self getLoadId] forKey:@"loadId"]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:anonymous]]; - - return eventDict; - + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.orderId, @"orderId", nil]; + + [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.total] + forKey:@"total"]; + + // Add nullable values + if (self.tax) { + [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.tax] + forKey:@"tax"]; + } + + if (self.shipping) { + [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.shipping] + forKey:@"shipping"]; + } + + if (self.city) { + [eventDict setObject:self.city forKey:@"city"]; + } + + if (self.state) { + [eventDict setObject:self.state forKey:@"state"]; + } + + if (self.country) { + [eventDict setObject:self.country forKey:@"country"]; + } + + if (self.currency) { + [eventDict setObject:self.city forKey:@"currency"]; + } + + // convert and add Transaction Items + NSMutableArray *serializedItems = [NSMutableArray new]; + for (BVTransactionItem *item in self.items) { + NSDictionary *itemParams = [item toRaw]; + [serializedItems addObject:itemParams]; + } + + [eventDict setObject:serializedItems forKey:@"items"]; + + // Common event values implied for schema... + [eventDict setObject:[self getLoadId] forKey:@"loadId"]; + [eventDict + addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:anonymous]]; + + return eventDict; } @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.h b/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.h index 60d20f82..d8c52901 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.h @@ -1,3 +1,5 @@ + + // // BVTransactionItem.h // Bazaarvoice SDK @@ -5,10 +7,11 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import #import "BVAnalyticEvent.h" +#import -/// Container class with convenience initializer for required and recommended parameters for items to be included in a Transactional Conversion. +/// Container class with convenience initializer for required and recommended +/// parameters for items to be included in a Transactional Conversion. @interface BVTransactionItem : NSObject /** @@ -18,35 +21,35 @@ @param price Recommended - Product Price. @param quantity Recommended - Purchase quantity. @param imageUrl Recommended - Link to product image. - -@return the event object that can be used to submit to Bazaarvoice via the BVPixel API. + +@return the event object that can be used to submit to Bazaarvoice via the +BVPixel API. */ --(id _Nonnull)initWithSku:(NSString* _Nonnull)sku name:(NSString* _Nullable)name category:(NSString* _Nullable)category price:(double)price quantity:(int)quantity imageUrl:(NSString* _Nullable)imageUrl; +- (nonnull id)initWithSku:(nonnull NSString *)sku + name:(nullable NSString *)name + category:(nullable NSString *)category + price:(double)price + quantity:(int)quantity + imageUrl:(nullable NSString *)imageUrl; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /// Product External ID. -@property (nonatomic, readonly) NSString* _Nonnull sku; - +@property(nonnull, nonatomic, readonly) NSString *sku; /// Product name. -@property (nonatomic, readonly) NSString* _Nullable name; - +@property(nullable, nonatomic, readonly) NSString *name; /// Product category. -@property (nonatomic, readonly) NSString* _Nullable category; - +@property(nullable, nonatomic, readonly) NSString *category; /// Product Price. -@property (nonatomic, readonly) NSString* _Nullable imageUrl; - +@property(nullable, nonatomic, readonly) NSString *imageUrl; /// Purchase quantity. -@property (nonatomic, assign, readonly) double price; - +@property(nonatomic, assign, readonly) double price; /// Link to product image. -@property (nonatomic, assign, readonly) int quantity; - +@property(nonatomic, assign, readonly) int quantity; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.m b/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.m index 6e6b597d..0eba1472 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVTransactionItem.m @@ -9,57 +9,58 @@ @implementation BVTransactionItem - @synthesize additionalParams; --(id _Nonnull)initWithSku:(NSString* _Nonnull)sku name:(NSString* _Nullable)name category:(NSString* _Nullable)category price:(double)price quantity:(int)quantity imageUrl:(NSString* _Nullable)imageUrl{ - - NSAssert(sku, @"SKU cannot be nil"); - - self = [super init]; - - if (self) { - _sku = sku; - _name = name; - _category = category; - _imageUrl = imageUrl; - _price = price; - _quantity = quantity; - self.additionalParams = [NSDictionary dictionary]; - } - - return self; -} +- (nonnull id)initWithSku:(nonnull NSString *)sku + name:(nullable NSString *)name + category:(nullable NSString *)category + price:(double)price + quantity:(int)quantity + imageUrl:(nullable NSString *)imageUrl { + + NSAssert(sku, @"SKU cannot be nil"); -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.sku, @"sku", - nil]; - - - // Add nullable values - if (self.name) { - [eventDict setObject:self.name forKey:@"name"]; - } - - if (self.imageUrl) { - [eventDict setObject:self.imageUrl forKey:@"imageUrl"]; - } - - if (self.category) { - [eventDict setObject:self.category forKey:@"category"]; - } - - [eventDict setObject:[NSString stringWithFormat:@"%d", self.quantity] forKey:@"quantity"]; - [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.price] forKey:@"price"]; - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - return [NSDictionary dictionaryWithDictionary:eventDict]; - + self = [super init]; + + if (self) { + _sku = sku; + _name = name; + _category = category; + _imageUrl = imageUrl; + _price = price; + _quantity = quantity; + self.additionalParams = [NSDictionary dictionary]; + } + + return self; } +- (NSDictionary *)toRaw { + + NSMutableDictionary *eventDict = + [NSMutableDictionary dictionaryWithObjectsAndKeys:self.sku, @"sku", nil]; + // Add nullable values + if (self.name) { + [eventDict setObject:self.name forKey:@"name"]; + } + + if (self.imageUrl) { + [eventDict setObject:self.imageUrl forKey:@"imageUrl"]; + } + + if (self.category) { + [eventDict setObject:self.category forKey:@"category"]; + } + + [eventDict setObject:[NSString stringWithFormat:@"%d", self.quantity] + forKey:@"quantity"]; + [eventDict setObject:[NSString stringWithFormat:@"%0.2f", self.price] + forKey:@"price"]; + + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + return [NSDictionary dictionaryWithDictionary:eventDict]; +} @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.h b/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.h index 5daf5de6..5151a951 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.h +++ b/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.h @@ -5,53 +5,63 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import "BVPixelTypes.h" #import "BVAnalyticEvent.h" +#import "BVPixelTypes.h" +#import -#define VIEWED_CGC_SCHEMA @{@"cl": @"Feature",@"type": @"UsedViewedUGC"} - +#define VIEWED_CGC_SCHEMA @{@"cl" : @"Feature", @"type" : @"UsedViewedUGC"} /** - BVViewedCGCEvent should be used to indicate that UGC (User Generated Content) is visible on the screen and has been in the view for an designated amount of time. This event should only be fired once per lifetime of a ViewController. This differs from a BVInView event in that a delay should be provided (typically 5 seconds) to know that content is visible and readable on the screen. + BVViewedCGCEvent should be used to indicate that UGC (User Generated Content) + is visible on the screen and has been in the view for an designated amount of + time. This event should only be fired once per lifetime of a ViewController. + This differs from a BVInView event in that a delay should be provided + (typically 5 seconds) to know that content is visible and readable on the + screen. */ @interface BVViewedCGCEvent : NSObject - /** - Used to create an event to indicate that CGC is visible on the screen and has been in the view for an designated amount of time. This event should only be fired once per lifetime of a ViewController. + Used to create an event to indicate that CGC is visible on the screen and has + been in the view for an designated amount of time. This event should only be + fired once per lifetime of a ViewController. @param productId Required - The product Id for which the UGC is assoicated. - @param rootCategoryId Optional - This value should be obtained from the product feed. - @param categoryId Optional - This value should be obtained from the product feed. + @param rootCategoryId Optional - This value should be obtained from the + product feed. + @param categoryId Optional - This value should be obtained from the product + feed. @param bvProduct Reuired - The product with API key being used. @param brand Optional - The brand name of the product. - @param params Optional - Additional key/value pairs to be send along the request. Most cases this will be nil. + @param params Optional - Additional key/value pairs to be send along the + request. Most cases this will be nil. - @return The event object that can be used to submit to Bazaarvoice via the BVPixel API. + @return The event object that can be used to submit to Bazaarvoice via the + BVPixel API. */ --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withRootCategoryID:(NSString * _Nullable)rootCategoryId - withCategoryId:(NSString * _Nullable)categoryId +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withRootCategoryID:(nullable NSString *)rootCategoryId + withCategoryId:(nullable NSString *)categoryId withProductType:(BVPixelProductType)bvProduct - withBrand:(NSString * _Nullable)brand - withAdditionalParams:(NSDictionary * _Nullable)params; + withBrand:(nullable NSString *)brand + withAdditionalParams:(nullable NSDictionary *)params; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /// The product Id for product presented. -@property (nonatomic, strong, readonly) NSString* _Nonnull productId; +@property(nonnull, nonatomic, strong, readonly) NSString *productId; -/// The BV API used to request the product, as defined in the BVPixelProductType. -@property (nonatomic, assign, readonly) BVPixelProductType bvProduct; +/// The BV API used to request the product, as defined in the +/// BVPixelProductType. +@property(nonatomic, assign, readonly) BVPixelProductType bvProduct; /// The category Id, if known, for the product presented. -@property (nonatomic, strong, readonly) NSString* _Nullable categoryId; +@property(nullable, nonatomic, strong, readonly) NSString *categoryId; /// The root category Id, if known, for the product presented. -@property (nonatomic, strong, readonly) NSString* _Nullable rootCategoryId; +@property(nullable, nonatomic, strong, readonly) NSString *rootCategoryId; /// Brand name for the product being viewed. -@property (nonatomic, strong, readonly) NSString * _Nullable brand; +@property(nullable, nonatomic, strong, readonly) NSString *brand; @end diff --git a/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.m b/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.m index 05c936b8..7f8a4d46 100644 --- a/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.m +++ b/Pod/BVAnalytics/BVPixelEvents/BVViewedCGCEvent.m @@ -6,64 +6,61 @@ // #import "BVViewedCGCEvent.h" -#import "BVPixelTypes.h" #import "BVAnalyticEventManager.h" +#import "BVPixelTypes.h" @implementation BVViewedCGCEvent @synthesize additionalParams; --(id _Nonnull)initWithProductId:(NSString * _Nonnull)productId - withRootCategoryID:(NSString * _Nullable)rootCategoryId - withCategoryId:(NSString * _Nullable)categoryId +- (nonnull id)initWithProductId:(nonnull NSString *)productId + withRootCategoryID:(nullable NSString *)rootCategoryId + withCategoryId:(nullable NSString *)categoryId withProductType:(BVPixelProductType)bvProduct - withBrand:(NSString * _Nullable)brand - withAdditionalParams:(NSDictionary * _Nullable)params{ - - self = [super init]; - - if (self) { - _productId = productId; - _brand = brand; - _bvProduct = bvProduct; - _categoryId = categoryId; - _rootCategoryId = rootCategoryId; - self.additionalParams = [NSDictionary dictionary]; - } - - return self; - + withBrand:(nullable NSString *)brand + withAdditionalParams:(nullable NSDictionary *)params { + self = [super init]; + + if (self) { + _productId = productId; + _brand = brand; + _bvProduct = bvProduct; + _categoryId = categoryId; + _rootCategoryId = rootCategoryId; + self.additionalParams = [NSDictionary dictionary]; + } + + return self; } +- (NSDictionary *)toRaw { + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.productId, @"productId", + [BVPixelProductTypeUtil + toString:self.bvProduct], + @"bvProduct", nil]; + + // Add nullable values + if (self.rootCategoryId) { + [eventDict + addEntriesFromDictionary:@{@"rootCategoryId" : self.rootCategoryId}]; + } + + if (self.categoryId) { + [eventDict addEntriesFromDictionary:@{@"categoryId" : self.categoryId}]; + } + + if (self.brand) { + [eventDict addEntriesFromDictionary:@{@"brand" : self.brand}]; + } -- (NSDictionary *)toRaw{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: - self.productId, @"productId", - [BVPixelProductTypeUtil toString:self.bvProduct], @"bvProduct", - nil]; - - // Add nullable values - if (self.rootCategoryId){ - [eventDict addEntriesFromDictionary:@{@"rootCategoryId":self.rootCategoryId}]; - } + // Common event values implied for schema... + [eventDict addEntriesFromDictionary:self.additionalParams]; + [eventDict addEntriesFromDictionary:VIEWED_CGC_SCHEMA]; + [eventDict addEntriesFromDictionary:[[BVAnalyticEventManager sharedManager] + getCommonAnalyticsDictAnonymous:NO]]; - - if (self.categoryId){ - [eventDict addEntriesFromDictionary:@{@"categoryId":self.categoryId}]; - } - - if (self.brand){ - [eventDict addEntriesFromDictionary:@{@"brand":self.brand}]; - } - - // Common event values implied for schema... - [eventDict addEntriesFromDictionary:self.additionalParams]; - [eventDict addEntriesFromDictionary:VIEWED_CGC_SCHEMA]; - [eventDict addEntriesFromDictionary: [[BVAnalyticEventManager sharedManager] getCommonAnalyticsDictAnonymous:NO]]; - - return [NSDictionary dictionaryWithDictionary:eventDict]; - + return [NSDictionary dictionaryWithDictionary:eventDict]; } @end diff --git a/Pod/BVAnalytics/Private/BVContextualInterests.h b/Pod/BVAnalytics/Private/BVContextualInterests.h index 06d9345e..b85c0a4b 100644 --- a/Pod/BVAnalytics/Private/BVContextualInterests.h +++ b/Pod/BVAnalytics/Private/BVContextualInterests.h @@ -4,8 +4,10 @@ // // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -// This file defines all Contextual Interest Tier 1 and Contextual Interest Tier 2 interest strings. -// These values can be fed into BVAdsSDK via various location-powered methods defined in BVAdsSDK.h +// This file defines all Contextual Interest Tier 1 and Contextual Interest Tier +// 2 interest strings. +// These values can be fed into BVAdsSDK via various location-powered methods +// defined in BVAdsSDK.h // Note that tier 2 interests are "children" of tier 1 interests. // Ex: Tier 1: "tierOneSports" // Tier 2: "tierTwoSportsBaseball" @@ -20,517 +22,728 @@ */ typedef NSString BVContextualInterest; - /// Contextual Interests Tier 1: -static BVContextualInterest* tierOneTravel = @"travel"; -static BVContextualInterest* tierOneFamilyAndParenting = @"familyandparenting"; -static BVContextualInterest* tierOneTechnologyAndComputing = @"technologyandcomputing"; -static BVContextualInterest* tierOneApparelAndAccessories = @"apparelandaccessories"; -static BVContextualInterest* tierOnePets = @"pets"; -static BVContextualInterest* tierOneCareers = @"careers"; -static BVContextualInterest* tierOneUncategorized = @"uncategorized"; -static BVContextualInterest* tierOneShopping = @"shopping"; -static BVContextualInterest* tierOneBeauty = @"beauty"; -static BVContextualInterest* tierOneSociety = @"society"; -static BVContextualInterest* tierOneAutomotive = @"automotive"; -static BVContextualInterest* tierOnePersonalFinance = @"personalfinance"; -static BVContextualInterest* tierOneHealthAndFitness = @"healthandfitness"; -static BVContextualInterest* tierOneNews = @"news"; -static BVContextualInterest* tierOneScience = @"science"; -static BVContextualInterest* tierOneArtsAndEntertainment = @"artsandentertainment"; -static BVContextualInterest* tierOneFoodAndDrink = @"foodanddrink"; -static BVContextualInterest* tierOneSports = @"sports"; -static BVContextualInterest* tierOneBusiness = @"business"; -static BVContextualInterest* tierOneReligionandSpirituality = @"religionandspirituality"; -static BVContextualInterest* tierOneHomeAndGarden = @"homeandgarden"; -static BVContextualInterest* tierOneHobbiesAndInterests = @"hobbiesandinterests"; -static BVContextualInterest* tierOneSportingGoods = @"sportinggoods"; -static BVContextualInterest* tierOneEducation = @"education"; -static BVContextualInterest* tierOneConsumerElectronics = @"consumerelectronics"; -static BVContextualInterest* tierOneRealEstate = @"realestate"; -static BVContextualInterest* tierOneStyleAndFashion = @"styleandfashion"; +static BVContextualInterest *tierOneTravel = @"travel"; +static BVContextualInterest *tierOneFamilyAndParenting = @"familyandparenting"; +static BVContextualInterest *tierOneTechnologyAndComputing = + @"technologyandcomputing"; +static BVContextualInterest *tierOneApparelAndAccessories = + @"apparelandaccessories"; +static BVContextualInterest *tierOnePets = @"pets"; +static BVContextualInterest *tierOneCareers = @"careers"; +static BVContextualInterest *tierOneUncategorized = @"uncategorized"; +static BVContextualInterest *tierOneShopping = @"shopping"; +static BVContextualInterest *tierOneBeauty = @"beauty"; +static BVContextualInterest *tierOneSociety = @"society"; +static BVContextualInterest *tierOneAutomotive = @"automotive"; +static BVContextualInterest *tierOnePersonalFinance = @"personalfinance"; +static BVContextualInterest *tierOneHealthAndFitness = @"healthandfitness"; +static BVContextualInterest *tierOneNews = @"news"; +static BVContextualInterest *tierOneScience = @"science"; +static BVContextualInterest *tierOneArtsAndEntertainment = + @"artsandentertainment"; +static BVContextualInterest *tierOneFoodAndDrink = @"foodanddrink"; +static BVContextualInterest *tierOneSports = @"sports"; +static BVContextualInterest *tierOneBusiness = @"business"; +static BVContextualInterest *tierOneReligionandSpirituality = + @"religionandspirituality"; +static BVContextualInterest *tierOneHomeAndGarden = @"homeandgarden"; +static BVContextualInterest *tierOneHobbiesAndInterests = + @"hobbiesandinterests"; +static BVContextualInterest *tierOneSportingGoods = @"sportinggoods"; +static BVContextualInterest *tierOneEducation = @"education"; +static BVContextualInterest *tierOneConsumerElectronics = + @"consumerelectronics"; +static BVContextualInterest *tierOneRealEstate = @"realestate"; +static BVContextualInterest *tierOneStyleAndFashion = @"styleandfashion"; /// Contextual Interests Tier 2: Travel -static BVContextualInterest* tierTwoTravelAdventureTravel = @"adventuretravel"; -static BVContextualInterest* tierTwoTravelAfrica = @"africa"; -static BVContextualInterest* tierTwoTravelAirTravel = @"airtravel"; -static BVContextualInterest* tierTwoTravelAustraliaAndNewZealand = @"australiaandnewzealand"; -static BVContextualInterest* tierTwoTravelBedAndBreakfasts = @"bedandbreakfasts"; -static BVContextualInterest* tierTwoTravelBudgeTravel = @"budgetravel"; -static BVContextualInterest* tierTwoTravelBusinessTravel = @"businesstravel"; -static BVContextualInterest* tierTwoTravelByUSLocale = @"byuslocale"; -static BVContextualInterest* tierTwoTravelCamping = @"camping"; -static BVContextualInterest* tierTwoTravelCanada = @"canada"; -static BVContextualInterest* tierTwoTravelCaribbean = @"caribbean"; -static BVContextualInterest* tierTwoTravelCruises = @"cruises"; -static BVContextualInterest* tierTwoTravelEasternEurope = @"easterneurope"; -static BVContextualInterest* tierTwoTravelEurope = @"europe"; -static BVContextualInterest* tierTwoTravelFrance = @"france"; -static BVContextualInterest* tierTwoTravelGreece = @"greece"; -static BVContextualInterest* tierTwoTravelHoneymoonsGetaways = @"honeymoonsgetaways"; -static BVContextualInterest* tierTwoTravelHotels = @"hotels"; -static BVContextualInterest* tierTwoTravelItaly = @"italy"; -static BVContextualInterest* tierTwoTravelJapan = @"japan"; -static BVContextualInterest* tierTwoTravelMexicoAndCentralAmerica = @"mexicoandcentralamerica"; -static BVContextualInterest* tierTwoTravelNationalParks = @"nationalparks"; -static BVContextualInterest* tierTwoTravelSouthAmerica = @"southamerica"; -static BVContextualInterest* tierTwoTravelSpas = @"spas"; -static BVContextualInterest* tierTwoTravelThemeParks = @"themeparks"; -static BVContextualInterest* tierTwoTravelTravelingwithKids = @"travelingwithkids"; -static BVContextualInterest* tierTwoTravelUnitedKingdom = @"unitedkingdom"; +static BVContextualInterest *tierTwoTravelAdventureTravel = @"adventuretravel"; +static BVContextualInterest *tierTwoTravelAfrica = @"africa"; +static BVContextualInterest *tierTwoTravelAirTravel = @"airtravel"; +static BVContextualInterest *tierTwoTravelAustraliaAndNewZealand = + @"australiaandnewzealand"; +static BVContextualInterest *tierTwoTravelBedAndBreakfasts = + @"bedandbreakfasts"; +static BVContextualInterest *tierTwoTravelBudgeTravel = @"budgetravel"; +static BVContextualInterest *tierTwoTravelBusinessTravel = @"businesstravel"; +static BVContextualInterest *tierTwoTravelByUSLocale = @"byuslocale"; +static BVContextualInterest *tierTwoTravelCamping = @"camping"; +static BVContextualInterest *tierTwoTravelCanada = @"canada"; +static BVContextualInterest *tierTwoTravelCaribbean = @"caribbean"; +static BVContextualInterest *tierTwoTravelCruises = @"cruises"; +static BVContextualInterest *tierTwoTravelEasternEurope = @"easterneurope"; +static BVContextualInterest *tierTwoTravelEurope = @"europe"; +static BVContextualInterest *tierTwoTravelFrance = @"france"; +static BVContextualInterest *tierTwoTravelGreece = @"greece"; +static BVContextualInterest *tierTwoTravelHoneymoonsGetaways = + @"honeymoonsgetaways"; +static BVContextualInterest *tierTwoTravelHotels = @"hotels"; +static BVContextualInterest *tierTwoTravelItaly = @"italy"; +static BVContextualInterest *tierTwoTravelJapan = @"japan"; +static BVContextualInterest *tierTwoTravelMexicoAndCentralAmerica = + @"mexicoandcentralamerica"; +static BVContextualInterest *tierTwoTravelNationalParks = @"nationalparks"; +static BVContextualInterest *tierTwoTravelSouthAmerica = @"southamerica"; +static BVContextualInterest *tierTwoTravelSpas = @"spas"; +static BVContextualInterest *tierTwoTravelThemeParks = @"themeparks"; +static BVContextualInterest *tierTwoTravelTravelingwithKids = + @"travelingwithkids"; +static BVContextualInterest *tierTwoTravelUnitedKingdom = @"unitedkingdom"; /// Contextual Interests Tier 2: Family & Parenting -static BVContextualInterest* tierTwoFamilyAndParentingAdoption = @"adoption"; -static BVContextualInterest* tierTwoFamilyAndParentingBabiesAndToddlers = @"babiesandtoddlers"; -static BVContextualInterest* tierTwoFamilyAndParentingDaycarePreSchool = @"daycarepreschool"; -static BVContextualInterest* tierTwoFamilyAndParentingEldercare = @"eldercare"; -static BVContextualInterest* tierTwoFamilyAndParentingFamilyInternet = @"familyinternet"; -static BVContextualInterest* tierTwoFamilyAndParentingParentingK6Kids = @"parentingk6kids"; -static BVContextualInterest* tierTwoFamilyAndParentingParentingTeens = @"parentingteens"; -static BVContextualInterest* tierTwoFamilyAndParentingPregnancy = @"pregnancy"; -static BVContextualInterest* tierTwoFamilyAndParentingSpecialNeedsKids = @"specialneedskids"; +static BVContextualInterest *tierTwoFamilyAndParentingAdoption = @"adoption"; +static BVContextualInterest *tierTwoFamilyAndParentingBabiesAndToddlers = + @"babiesandtoddlers"; +static BVContextualInterest *tierTwoFamilyAndParentingDaycarePreSchool = + @"daycarepreschool"; +static BVContextualInterest *tierTwoFamilyAndParentingEldercare = @"eldercare"; +static BVContextualInterest *tierTwoFamilyAndParentingFamilyInternet = + @"familyinternet"; +static BVContextualInterest *tierTwoFamilyAndParentingParentingK6Kids = + @"parentingk6kids"; +static BVContextualInterest *tierTwoFamilyAndParentingParentingTeens = + @"parentingteens"; +static BVContextualInterest *tierTwoFamilyAndParentingPregnancy = @"pregnancy"; +static BVContextualInterest *tierTwoFamilyAndParentingSpecialNeedsKids = + @"specialneedskids"; /// Contextual Interests Tier 2: Technology & Computing -static BVContextualInterest* tierTwoTechnologyAndComputing3DGraphics = @"3dgraphics"; -static BVContextualInterest* tierTwoTechnologyAndComputingAnimation = @"animation"; -static BVContextualInterest* tierTwoTechnologyAndComputingAntivirusSoftware = @"antivirussoftware"; -static BVContextualInterest* tierTwoTechnologyAndComputingCC = @"cc"; -static BVContextualInterest* tierTwoTechnologyAndComputingCamerasAndCamcorders = @"camerasandcamcorders"; -static BVContextualInterest* tierTwoTechnologyAndComputingCellPhones = @"cellphones"; -static BVContextualInterest* tierTwoTechnologyAndComputingComputerCertification = @"computercertification"; -static BVContextualInterest* tierTwoTechnologyAndComputingComputerNetworking = @"computernetworking"; -static BVContextualInterest* tierTwoTechnologyAndComputingComputerPeripherals = @"computerperipherals"; -static BVContextualInterest* tierTwoTechnologyAndComputingComputerReviews = @"computerreviews"; -static BVContextualInterest* tierTwoTechnologyAndComputingDataCenters = @"datacenters"; -static BVContextualInterest* tierTwoTechnologyAndComputingDatabases = @"databases"; -static BVContextualInterest* tierTwoTechnologyAndComputingDesktopPublishing = @"desktoppublishing"; -static BVContextualInterest* tierTwoTechnologyAndComputingDesktopVideo = @"desktopvideo"; -static BVContextualInterest* tierTwoTechnologyAndComputingEmail = @"email"; -static BVContextualInterest* tierTwoTechnologyAndComputingGraphicsSoftware = @"graphicssoftware"; -static BVContextualInterest* tierTwoTechnologyAndComputingHomeVideoDVD = @"homevideodvd"; -static BVContextualInterest* tierTwoTechnologyAndComputingInternetTechnology = @"internettechnology"; -static BVContextualInterest* tierTwoTechnologyAndComputingJava = @"java"; -static BVContextualInterest* tierTwoTechnologyAndComputingJavaScript = @"javascript"; -static BVContextualInterest* tierTwoTechnologyAndComputingLinux = @"linux"; -static BVContextualInterest* tierTwoTechnologyAndComputingMacOS = @"macos"; -static BVContextualInterest* tierTwoTechnologyAndComputingMacSupport = @"macsupport"; -static BVContextualInterest* tierTwoTechnologyAndComputingMP3MIDI = @"mp3midi"; -static BVContextualInterest* tierTwoTechnologyAndComputingNetConferencing = @"netconferencing"; -static BVContextualInterest* tierTwoTechnologyAndComputingNetforBeginners = @"netforbeginners"; -static BVContextualInterest* tierTwoTechnologyAndComputingNetworkSecurity = @"networksecurity"; -static BVContextualInterest* tierTwoTechnologyAndComputingPalmtopsPDAs = @"palmtopspdas"; -static BVContextualInterest* tierTwoTechnologyAndComputingPCSupport = @"pcsupport"; -static BVContextualInterest* tierTwoTechnologyAndComputingPortableEntertainment = @"portableentertainment"; -static BVContextualInterest* tierTwoTechnologyAndComputingSharewareFreeware = @"sharewarefreeware"; -static BVContextualInterest* tierTwoTechnologyAndComputingUnix = @"unix"; -static BVContextualInterest* tierTwoTechnologyAndComputingVisualBasic = @"visualbasic"; -static BVContextualInterest* tierTwoTechnologyAndComputingWebClipArt = @"webclipart"; -static BVContextualInterest* tierTwoTechnologyAndComputingWebDesignHTML = @"webdesignhtml"; -static BVContextualInterest* tierTwoTechnologyAndComputingWebSearch = @"websearch"; -static BVContextualInterest* tierTwoTechnologyAndComputingWindows = @"windows"; +static BVContextualInterest *tierTwoTechnologyAndComputing3DGraphics = + @"3dgraphics"; +static BVContextualInterest *tierTwoTechnologyAndComputingAnimation = + @"animation"; +static BVContextualInterest *tierTwoTechnologyAndComputingAntivirusSoftware = + @"antivirussoftware"; +static BVContextualInterest *tierTwoTechnologyAndComputingCC = @"cc"; +static BVContextualInterest *tierTwoTechnologyAndComputingCamerasAndCamcorders = + @"camerasandcamcorders"; +static BVContextualInterest *tierTwoTechnologyAndComputingCellPhones = + @"cellphones"; +static BVContextualInterest + *tierTwoTechnologyAndComputingComputerCertification = + @"computercertification"; +static BVContextualInterest *tierTwoTechnologyAndComputingComputerNetworking = + @"computernetworking"; +static BVContextualInterest *tierTwoTechnologyAndComputingComputerPeripherals = + @"computerperipherals"; +static BVContextualInterest *tierTwoTechnologyAndComputingComputerReviews = + @"computerreviews"; +static BVContextualInterest *tierTwoTechnologyAndComputingDataCenters = + @"datacenters"; +static BVContextualInterest *tierTwoTechnologyAndComputingDatabases = + @"databases"; +static BVContextualInterest *tierTwoTechnologyAndComputingDesktopPublishing = + @"desktoppublishing"; +static BVContextualInterest *tierTwoTechnologyAndComputingDesktopVideo = + @"desktopvideo"; +static BVContextualInterest *tierTwoTechnologyAndComputingEmail = @"email"; +static BVContextualInterest *tierTwoTechnologyAndComputingGraphicsSoftware = + @"graphicssoftware"; +static BVContextualInterest *tierTwoTechnologyAndComputingHomeVideoDVD = + @"homevideodvd"; +static BVContextualInterest *tierTwoTechnologyAndComputingInternetTechnology = + @"internettechnology"; +static BVContextualInterest *tierTwoTechnologyAndComputingJava = @"java"; +static BVContextualInterest *tierTwoTechnologyAndComputingJavaScript = + @"javascript"; +static BVContextualInterest *tierTwoTechnologyAndComputingLinux = @"linux"; +static BVContextualInterest *tierTwoTechnologyAndComputingMacOS = @"macos"; +static BVContextualInterest *tierTwoTechnologyAndComputingMacSupport = + @"macsupport"; +static BVContextualInterest *tierTwoTechnologyAndComputingMP3MIDI = @"mp3midi"; +static BVContextualInterest *tierTwoTechnologyAndComputingNetConferencing = + @"netconferencing"; +static BVContextualInterest *tierTwoTechnologyAndComputingNetforBeginners = + @"netforbeginners"; +static BVContextualInterest *tierTwoTechnologyAndComputingNetworkSecurity = + @"networksecurity"; +static BVContextualInterest *tierTwoTechnologyAndComputingPalmtopsPDAs = + @"palmtopspdas"; +static BVContextualInterest *tierTwoTechnologyAndComputingPCSupport = + @"pcsupport"; +static BVContextualInterest + *tierTwoTechnologyAndComputingPortableEntertainment = + @"portableentertainment"; +static BVContextualInterest *tierTwoTechnologyAndComputingSharewareFreeware = + @"sharewarefreeware"; +static BVContextualInterest *tierTwoTechnologyAndComputingUnix = @"unix"; +static BVContextualInterest *tierTwoTechnologyAndComputingVisualBasic = + @"visualbasic"; +static BVContextualInterest *tierTwoTechnologyAndComputingWebClipArt = + @"webclipart"; +static BVContextualInterest *tierTwoTechnologyAndComputingWebDesignHTML = + @"webdesignhtml"; +static BVContextualInterest *tierTwoTechnologyAndComputingWebSearch = + @"websearch"; +static BVContextualInterest *tierTwoTechnologyAndComputingWindows = @"windows"; /// Contextual Interests Tier 2: Apparel & Accessories -static BVContextualInterest* tierTwoApparelAndAccessoriesEngagementRings = @"engagementrings"; -static BVContextualInterest* tierTwoApparelAndAccessoriesFashionJewelry = @"fashionjewelry"; -static BVContextualInterest* tierTwoApparelAndAccessoriesHairAccessories = @"hairaccessories"; -static BVContextualInterest* tierTwoApparelAndAccessoriesHandbags = @"handbags"; -static BVContextualInterest* tierTwoApparelAndAccessoriesJewelryAndWatches = @"jewelryandwatches"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensApparel = @"mensapparel"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensAthleticApparel = @"mensathleticapparel"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensBusinessAttire = @"mensbusinessattire"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensCasualAttire = @"menscasualattire"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensShoes = @"mensshoes"; -static BVContextualInterest* tierTwoApparelAndAccessoriesMensSwimwear = @"mensswimwear"; -static BVContextualInterest* tierTwoApparelAndAccessoriesSunglasses = @"sunglasses"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWallets = @"wallets"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWatches = @"watches"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensShoes = @"womensshoes"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensApparel = @"womensapparel"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensAthleticApparel = @"womensathleticapparel"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensBusinessAttire = @"womensbusinessattire"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensCoatsAndJackets = @"womenscoatsandjackets"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensDresses = @"womensdresses"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensJeans = @"womensjeans"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensMaternity = @"womensmaternity"; -static BVContextualInterest* tierTwoApparelAndAccessoriesWomensSwimwear = @"womensswimwear"; +static BVContextualInterest *tierTwoApparelAndAccessoriesEngagementRings = + @"engagementrings"; +static BVContextualInterest *tierTwoApparelAndAccessoriesFashionJewelry = + @"fashionjewelry"; +static BVContextualInterest *tierTwoApparelAndAccessoriesHairAccessories = + @"hairaccessories"; +static BVContextualInterest *tierTwoApparelAndAccessoriesHandbags = @"handbags"; +static BVContextualInterest *tierTwoApparelAndAccessoriesJewelryAndWatches = + @"jewelryandwatches"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensApparel = + @"mensapparel"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensAthleticApparel = + @"mensathleticapparel"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensBusinessAttire = + @"mensbusinessattire"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensCasualAttire = + @"menscasualattire"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensShoes = + @"mensshoes"; +static BVContextualInterest *tierTwoApparelAndAccessoriesMensSwimwear = + @"mensswimwear"; +static BVContextualInterest *tierTwoApparelAndAccessoriesSunglasses = + @"sunglasses"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWallets = @"wallets"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWatches = @"watches"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensShoes = + @"womensshoes"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensApparel = + @"womensapparel"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensAthleticApparel = + @"womensathleticapparel"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensBusinessAttire = + @"womensbusinessattire"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensCoatsAndJackets = + @"womenscoatsandjackets"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensDresses = + @"womensdresses"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensJeans = + @"womensjeans"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensMaternity = + @"womensmaternity"; +static BVContextualInterest *tierTwoApparelAndAccessoriesWomensSwimwear = + @"womensswimwear"; /// Contextual Interests Tier 2: Pets -static BVContextualInterest* tierTwoPetsAquariums = @"aquariums"; -static BVContextualInterest* tierTwoPetsBirds = @"birds"; -static BVContextualInterest* tierTwoPetsCats = @"cats"; -static BVContextualInterest* tierTwoPetsDogs = @"dogs"; -static BVContextualInterest* tierTwoPetsLargeAnimals = @"largeanimals"; -static BVContextualInterest* tierTwoPetsReptiles = @"reptiles"; -static BVContextualInterest* tierTwoPetsVeterinaryMedicine = @"veterinarymedicine"; +static BVContextualInterest *tierTwoPetsAquariums = @"aquariums"; +static BVContextualInterest *tierTwoPetsBirds = @"birds"; +static BVContextualInterest *tierTwoPetsCats = @"cats"; +static BVContextualInterest *tierTwoPetsDogs = @"dogs"; +static BVContextualInterest *tierTwoPetsLargeAnimals = @"largeanimals"; +static BVContextualInterest *tierTwoPetsReptiles = @"reptiles"; +static BVContextualInterest *tierTwoPetsVeterinaryMedicine = + @"veterinarymedicine"; /// Contextual Interests Tier 2: Careers -static BVContextualInterest* tierTwoCareersCareerAdvice = @"careeradvice"; -static BVContextualInterest* tierTwoCareersCareerPlanning = @"careerplanning"; -static BVContextualInterest* tierTwoCareersCollege = @"college"; -static BVContextualInterest* tierTwoCareersFinancialAid = @"financialaid"; -static BVContextualInterest* tierTwoCareersJobFairs = @"jobfairs"; -static BVContextualInterest* tierTwoCareersJobSearch = @"jobsearch"; -static BVContextualInterest* tierTwoCareersNursing = @"nursing"; -static BVContextualInterest* tierTwoCareersResumeWritingAdvice = @"resumewritingadvice"; -static BVContextualInterest* tierTwoCareersScholarships = @"scholarships"; -static BVContextualInterest* tierTwoCareersTelecommuting = @"telecommuting"; -static BVContextualInterest* tierTwoCareersUSMilitary = @"usmilitary"; +static BVContextualInterest *tierTwoCareersCareerAdvice = @"careeradvice"; +static BVContextualInterest *tierTwoCareersCareerPlanning = @"careerplanning"; +static BVContextualInterest *tierTwoCareersCollege = @"college"; +static BVContextualInterest *tierTwoCareersFinancialAid = @"financialaid"; +static BVContextualInterest *tierTwoCareersJobFairs = @"jobfairs"; +static BVContextualInterest *tierTwoCareersJobSearch = @"jobsearch"; +static BVContextualInterest *tierTwoCareersNursing = @"nursing"; +static BVContextualInterest *tierTwoCareersResumeWritingAdvice = + @"resumewritingadvice"; +static BVContextualInterest *tierTwoCareersScholarships = @"scholarships"; +static BVContextualInterest *tierTwoCareersTelecommuting = @"telecommuting"; +static BVContextualInterest *tierTwoCareersUSMilitary = @"usmilitary"; /// Contextual Interests Tier 2: Uncategorized -static BVContextualInterest* tierTwoUncategorizedSocialMedia = @"socialmedia"; +static BVContextualInterest *tierTwoUncategorizedSocialMedia = @"socialmedia"; /// Contextual Interests Tier 2: Shopping -static BVContextualInterest* tierTwoShoppingComparison = @"comparison"; -static BVContextualInterest* tierTwoShoppingContestsAndFreebies = @"contestsandfreebies"; -static BVContextualInterest* tierTwoShoppingCouponing = @"couponing"; -static BVContextualInterest* tierTwoShoppingEngines = @"engines"; +static BVContextualInterest *tierTwoShoppingComparison = @"comparison"; +static BVContextualInterest *tierTwoShoppingContestsAndFreebies = + @"contestsandfreebies"; +static BVContextualInterest *tierTwoShoppingCouponing = @"couponing"; +static BVContextualInterest *tierTwoShoppingEngines = @"engines"; /// Contextual Interests Tier 2: Beauty -static BVContextualInterest* tierTwoBeautyFragrance = @"fragrance"; -static BVContextualInterest* tierTwoBeautyHairCare = @"haircare"; -static BVContextualInterest* tierTwoBeautyMakeup = @"makeup"; -static BVContextualInterest* tierTwoBeautyNailCare = @"nailcare"; -static BVContextualInterest* tierTwoBeautySkinCare = @"skincare"; +static BVContextualInterest *tierTwoBeautyFragrance = @"fragrance"; +static BVContextualInterest *tierTwoBeautyHairCare = @"haircare"; +static BVContextualInterest *tierTwoBeautyMakeup = @"makeup"; +static BVContextualInterest *tierTwoBeautyNailCare = @"nailcare"; +static BVContextualInterest *tierTwoBeautySkinCare = @"skincare"; /// Contextual Interests Tier 2: Society -static BVContextualInterest* tierTwoSocietyDating = @"dating"; -static BVContextualInterest* tierTwoSocietyDivorceSupport = @"divorcesupport"; -static BVContextualInterest* tierTwoSocietyEthnicSpecific = @"ethnicspecific"; -static BVContextualInterest* tierTwoSocietyGayLife = @"gaylife"; -static BVContextualInterest* tierTwoSocietyMarriage = @"marriage"; -static BVContextualInterest* tierTwoSocietySeniorLiving = @"seniorliving"; -static BVContextualInterest* tierTwoSocietyTeens = @"teens"; -static BVContextualInterest* tierTwoSocietyWeddings = @"weddings"; +static BVContextualInterest *tierTwoSocietyDating = @"dating"; +static BVContextualInterest *tierTwoSocietyDivorceSupport = @"divorcesupport"; +static BVContextualInterest *tierTwoSocietyEthnicSpecific = @"ethnicspecific"; +static BVContextualInterest *tierTwoSocietyGayLife = @"gaylife"; +static BVContextualInterest *tierTwoSocietyMarriage = @"marriage"; +static BVContextualInterest *tierTwoSocietySeniorLiving = @"seniorliving"; +static BVContextualInterest *tierTwoSocietyTeens = @"teens"; +static BVContextualInterest *tierTwoSocietyWeddings = @"weddings"; /// Contextual Interests Tier 2: Automotive -static BVContextualInterest* tierTwoAutomotiveAutoParts = @"autoparts"; -static BVContextualInterest* tierTwoAutomotiveAutoRepair = @"autorepair"; -static BVContextualInterest* tierTwoAutomotiveBuyingSellingCars = @"buyingsellingcars"; -static BVContextualInterest* tierTwoAutomotiveCarCulture = @"carculture"; -static BVContextualInterest* tierTwoAutomotiveCertifiedPreOwned = @"certifiedpreowned"; -static BVContextualInterest* tierTwoAutomotiveConvertible = @"convertible"; -static BVContextualInterest* tierTwoAutomotiveCoupe = @"coupe"; -static BVContextualInterest* tierTwoAutomotiveCrossover = @"crossover"; -static BVContextualInterest* tierTwoAutomotiveDiesel = @"diesel"; -static BVContextualInterest* tierTwoAutomotiveElectricVehicle = @"electricvehicle"; -static BVContextualInterest* tierTwoAutomotiveHatchback = @"hatchback"; -static BVContextualInterest* tierTwoAutomotiveHybrid = @"hybrid"; -static BVContextualInterest* tierTwoAutomotiveLuxury = @"luxury"; -static BVContextualInterest* tierTwoAutomotiveMinivan = @"minivan"; -static BVContextualInterest* tierTwoAutomotiveMotorcycles = @"motorcycles"; -static BVContextualInterest* tierTwoAutomotiveOffRoadVehicles = @"offroadvehicles"; -static BVContextualInterest* tierTwoAutomotivePerformanceVehicles = @"performancevehicles"; -static BVContextualInterest* tierTwoAutomotivePickup = @"pickup"; -static BVContextualInterest* tierTwoAutomotiveRoadSideAssistance = @"roadsideassistance"; -static BVContextualInterest* tierTwoAutomotiveSedan = @"sedan"; -static BVContextualInterest* tierTwoAutomotiveTrucksAndAccessories = @"trucksandaccessories"; -static BVContextualInterest* tierTwoAutomotiveVintageCars = @"vintagecars"; -static BVContextualInterest* tierTwoAutomotiveWagon = @"wagon"; +static BVContextualInterest *tierTwoAutomotiveAutoParts = @"autoparts"; +static BVContextualInterest *tierTwoAutomotiveAutoRepair = @"autorepair"; +static BVContextualInterest *tierTwoAutomotiveBuyingSellingCars = + @"buyingsellingcars"; +static BVContextualInterest *tierTwoAutomotiveCarCulture = @"carculture"; +static BVContextualInterest *tierTwoAutomotiveCertifiedPreOwned = + @"certifiedpreowned"; +static BVContextualInterest *tierTwoAutomotiveConvertible = @"convertible"; +static BVContextualInterest *tierTwoAutomotiveCoupe = @"coupe"; +static BVContextualInterest *tierTwoAutomotiveCrossover = @"crossover"; +static BVContextualInterest *tierTwoAutomotiveDiesel = @"diesel"; +static BVContextualInterest *tierTwoAutomotiveElectricVehicle = + @"electricvehicle"; +static BVContextualInterest *tierTwoAutomotiveHatchback = @"hatchback"; +static BVContextualInterest *tierTwoAutomotiveHybrid = @"hybrid"; +static BVContextualInterest *tierTwoAutomotiveLuxury = @"luxury"; +static BVContextualInterest *tierTwoAutomotiveMinivan = @"minivan"; +static BVContextualInterest *tierTwoAutomotiveMotorcycles = @"motorcycles"; +static BVContextualInterest *tierTwoAutomotiveOffRoadVehicles = + @"offroadvehicles"; +static BVContextualInterest *tierTwoAutomotivePerformanceVehicles = + @"performancevehicles"; +static BVContextualInterest *tierTwoAutomotivePickup = @"pickup"; +static BVContextualInterest *tierTwoAutomotiveRoadSideAssistance = + @"roadsideassistance"; +static BVContextualInterest *tierTwoAutomotiveSedan = @"sedan"; +static BVContextualInterest *tierTwoAutomotiveTrucksAndAccessories = + @"trucksandaccessories"; +static BVContextualInterest *tierTwoAutomotiveVintageCars = @"vintagecars"; +static BVContextualInterest *tierTwoAutomotiveWagon = @"wagon"; /// Contextual Interests Tier 2: Personal Finance -static BVContextualInterest* tierTwoPersonalFinanceBeginningInvesting = @"beginninginvesting"; -static BVContextualInterest* tierTwoPersonalFinanceCreditDebtAndLoans = @"creditdebtandloans"; -static BVContextualInterest* tierTwoPersonalFinanceFinancialNews = @"financialnews"; -static BVContextualInterest* tierTwoPersonalFinanceFinancialPlanning = @"financialplanning"; -static BVContextualInterest* tierTwoPersonalFinanceHedgeFund = @"hedgefund"; -static BVContextualInterest* tierTwoPersonalFinanceInsurance = @"insurance"; -static BVContextualInterest* tierTwoPersonalFinanceInvesting = @"investing"; -static BVContextualInterest* tierTwoPersonalFinanceMutualFunds = @"mutualfunds"; -static BVContextualInterest* tierTwoPersonalFinanceOptions = @"options"; -static BVContextualInterest* tierTwoPersonalFinanceRetirementPlanning = @"retirementplanning"; -static BVContextualInterest* tierTwoPersonalFinanceStocks = @"stocks"; -static BVContextualInterest* tierTwoPersonalFinanceTaxPlanning = @"taxplanning"; +static BVContextualInterest *tierTwoPersonalFinanceBeginningInvesting = + @"beginninginvesting"; +static BVContextualInterest *tierTwoPersonalFinanceCreditDebtAndLoans = + @"creditdebtandloans"; +static BVContextualInterest *tierTwoPersonalFinanceFinancialNews = + @"financialnews"; +static BVContextualInterest *tierTwoPersonalFinanceFinancialPlanning = + @"financialplanning"; +static BVContextualInterest *tierTwoPersonalFinanceHedgeFund = @"hedgefund"; +static BVContextualInterest *tierTwoPersonalFinanceInsurance = @"insurance"; +static BVContextualInterest *tierTwoPersonalFinanceInvesting = @"investing"; +static BVContextualInterest *tierTwoPersonalFinanceMutualFunds = @"mutualfunds"; +static BVContextualInterest *tierTwoPersonalFinanceOptions = @"options"; +static BVContextualInterest *tierTwoPersonalFinanceRetirementPlanning = + @"retirementplanning"; +static BVContextualInterest *tierTwoPersonalFinanceStocks = @"stocks"; +static BVContextualInterest *tierTwoPersonalFinanceTaxPlanning = @"taxplanning"; /// Contextual Interests Tier 2: Health & Fitness -static BVContextualInterest* tierTwoHealthAndFitnessADD = @"add"; -static BVContextualInterest* tierTwoHealthAndFitnessAIDSHIV = @"aidshiv"; -static BVContextualInterest* tierTwoHealthAndFitnessAllergies = @"allergies"; -static BVContextualInterest* tierTwoHealthAndFitnessAlternativeMedicine = @"alternativemedicine"; -static BVContextualInterest* tierTwoHealthAndFitnessArthritis = @"arthritis"; -static BVContextualInterest* tierTwoHealthAndFitnessAsthma = @"asthma"; -static BVContextualInterest* tierTwoHealthAndFitnessAutismPDD = @"autismpdd"; -static BVContextualInterest* tierTwoHealthAndFitnessBipolarDisorder = @"bipolardisorder"; -static BVContextualInterest* tierTwoHealthAndFitnessBrainTumor = @"braintumor"; -static BVContextualInterest* tierTwoHealthAndFitnessCancer = @"cancer"; -static BVContextualInterest* tierTwoHealthAndFitnessCholesterol = @"cholesterol"; -static BVContextualInterest* tierTwoHealthAndFitnessChronicFatigueSyndrome = @"chronicfatiguesyndrome"; -static BVContextualInterest* tierTwoHealthAndFitnessChronicPain = @"chronicpain"; -static BVContextualInterest* tierTwoHealthAndFitnessColdAndFlu = @"coldandflu"; -static BVContextualInterest* tierTwoHealthAndFitnessDeafness = @"deafness"; -static BVContextualInterest* tierTwoHealthAndFitnessDentalCare = @"dentalcare"; -static BVContextualInterest* tierTwoHealthAndFitnessDepression = @"depression"; -static BVContextualInterest* tierTwoHealthAndFitnessDermatology = @"dermatology"; -static BVContextualInterest* tierTwoHealthAndFitnessDiabetes = @"diabetes"; -static BVContextualInterest* tierTwoHealthAndFitnessEpilepsy = @"epilepsy"; -static BVContextualInterest* tierTwoHealthAndFitnessExercise = @"exercise"; -static BVContextualInterest* tierTwoHealthAndFitnessGERDAcidReflux = @"gerdacidreflux"; -static BVContextualInterest* tierTwoHealthAndFitnessHeadachesMigraines = @"headachesmigraines"; -static BVContextualInterest* tierTwoHealthAndFitnessHeartDisease = @"heartdisease"; -static BVContextualInterest* tierTwoHealthAndFitnessHerbsforHealth = @"herbsforhealth"; -static BVContextualInterest* tierTwoHealthAndFitnessHolisticHealing = @"holistichealing"; -static BVContextualInterest* tierTwoHealthAndFitnessIBSCrohnsDisease = @"ibscrohnsdisease"; -static BVContextualInterest* tierTwoHealthAndFitnessIncestAbuseSupport = @"incestabusesupport"; -static BVContextualInterest* tierTwoHealthAndFitnessIncontinence = @"incontinence"; -static BVContextualInterest* tierTwoHealthAndFitnessInfertility = @"infertility"; -static BVContextualInterest* tierTwoHealthAndFitnessMensHealth = @"menshealth"; -static BVContextualInterest* tierTwoHealthAndFitnessNutrition = @"nutrition"; -static BVContextualInterest* tierTwoHealthAndFitnessOrthopedics = @"orthopedics"; -static BVContextualInterest* tierTwoHealthAndFitnessPanicAnxietyDisorders = @"panicanxietydisorders"; -static BVContextualInterest* tierTwoHealthAndFitnessPediatrics = @"pediatrics"; -static BVContextualInterest* tierTwoHealthAndFitnessPhysicalTherapy = @"physicaltherapy"; -static BVContextualInterest* tierTwoHealthAndFitnessPsychologyPsychiatry = @"psychologypsychiatry"; -static BVContextualInterest* tierTwoHealthAndFitnessSeniorHealth = @"seniorhealth"; -static BVContextualInterest* tierTwoHealthAndFitnessSexuality = @"sexuality"; -static BVContextualInterest* tierTwoHealthAndFitnessSleepDisorders = @"sleepdisorders"; -static BVContextualInterest* tierTwoHealthAndFitnessSmokingCessation = @"smokingcessation"; -static BVContextualInterest* tierTwoHealthAndFitnessSubstanceAbuse = @"substanceabuse"; -static BVContextualInterest* tierTwoHealthAndFitnessThyroidDisease = @"thyroiddisease"; -static BVContextualInterest* tierTwoHealthAndFitnessWeightLoss = @"weightloss"; -static BVContextualInterest* tierTwoHealthAndFitnessWomensHealth = @"womenshealth"; +static BVContextualInterest *tierTwoHealthAndFitnessADD = @"add"; +static BVContextualInterest *tierTwoHealthAndFitnessAIDSHIV = @"aidshiv"; +static BVContextualInterest *tierTwoHealthAndFitnessAllergies = @"allergies"; +static BVContextualInterest *tierTwoHealthAndFitnessAlternativeMedicine = + @"alternativemedicine"; +static BVContextualInterest *tierTwoHealthAndFitnessArthritis = @"arthritis"; +static BVContextualInterest *tierTwoHealthAndFitnessAsthma = @"asthma"; +static BVContextualInterest *tierTwoHealthAndFitnessAutismPDD = @"autismpdd"; +static BVContextualInterest *tierTwoHealthAndFitnessBipolarDisorder = + @"bipolardisorder"; +static BVContextualInterest *tierTwoHealthAndFitnessBrainTumor = @"braintumor"; +static BVContextualInterest *tierTwoHealthAndFitnessCancer = @"cancer"; +static BVContextualInterest *tierTwoHealthAndFitnessCholesterol = + @"cholesterol"; +static BVContextualInterest *tierTwoHealthAndFitnessChronicFatigueSyndrome = + @"chronicfatiguesyndrome"; +static BVContextualInterest *tierTwoHealthAndFitnessChronicPain = + @"chronicpain"; +static BVContextualInterest *tierTwoHealthAndFitnessColdAndFlu = @"coldandflu"; +static BVContextualInterest *tierTwoHealthAndFitnessDeafness = @"deafness"; +static BVContextualInterest *tierTwoHealthAndFitnessDentalCare = @"dentalcare"; +static BVContextualInterest *tierTwoHealthAndFitnessDepression = @"depression"; +static BVContextualInterest *tierTwoHealthAndFitnessDermatology = + @"dermatology"; +static BVContextualInterest *tierTwoHealthAndFitnessDiabetes = @"diabetes"; +static BVContextualInterest *tierTwoHealthAndFitnessEpilepsy = @"epilepsy"; +static BVContextualInterest *tierTwoHealthAndFitnessExercise = @"exercise"; +static BVContextualInterest *tierTwoHealthAndFitnessGERDAcidReflux = + @"gerdacidreflux"; +static BVContextualInterest *tierTwoHealthAndFitnessHeadachesMigraines = + @"headachesmigraines"; +static BVContextualInterest *tierTwoHealthAndFitnessHeartDisease = + @"heartdisease"; +static BVContextualInterest *tierTwoHealthAndFitnessHerbsforHealth = + @"herbsforhealth"; +static BVContextualInterest *tierTwoHealthAndFitnessHolisticHealing = + @"holistichealing"; +static BVContextualInterest *tierTwoHealthAndFitnessIBSCrohnsDisease = + @"ibscrohnsdisease"; +static BVContextualInterest *tierTwoHealthAndFitnessIncestAbuseSupport = + @"incestabusesupport"; +static BVContextualInterest *tierTwoHealthAndFitnessIncontinence = + @"incontinence"; +static BVContextualInterest *tierTwoHealthAndFitnessInfertility = + @"infertility"; +static BVContextualInterest *tierTwoHealthAndFitnessMensHealth = @"menshealth"; +static BVContextualInterest *tierTwoHealthAndFitnessNutrition = @"nutrition"; +static BVContextualInterest *tierTwoHealthAndFitnessOrthopedics = + @"orthopedics"; +static BVContextualInterest *tierTwoHealthAndFitnessPanicAnxietyDisorders = + @"panicanxietydisorders"; +static BVContextualInterest *tierTwoHealthAndFitnessPediatrics = @"pediatrics"; +static BVContextualInterest *tierTwoHealthAndFitnessPhysicalTherapy = + @"physicaltherapy"; +static BVContextualInterest *tierTwoHealthAndFitnessPsychologyPsychiatry = + @"psychologypsychiatry"; +static BVContextualInterest *tierTwoHealthAndFitnessSeniorHealth = + @"seniorhealth"; +static BVContextualInterest *tierTwoHealthAndFitnessSexuality = @"sexuality"; +static BVContextualInterest *tierTwoHealthAndFitnessSleepDisorders = + @"sleepdisorders"; +static BVContextualInterest *tierTwoHealthAndFitnessSmokingCessation = + @"smokingcessation"; +static BVContextualInterest *tierTwoHealthAndFitnessSubstanceAbuse = + @"substanceabuse"; +static BVContextualInterest *tierTwoHealthAndFitnessThyroidDisease = + @"thyroiddisease"; +static BVContextualInterest *tierTwoHealthAndFitnessWeightLoss = @"weightloss"; +static BVContextualInterest *tierTwoHealthAndFitnessWomensHealth = + @"womenshealth"; /// Contextual Interests Tier 2: News -static BVContextualInterest* tierTwoNewsInternationalNews = @"internationalnews"; -static BVContextualInterest* tierTwoNewsLocalNews = @"localnews"; -static BVContextualInterest* tierTwoNewsNationalNews = @"nationalnews"; +static BVContextualInterest *tierTwoNewsInternationalNews = + @"internationalnews"; +static BVContextualInterest *tierTwoNewsLocalNews = @"localnews"; +static BVContextualInterest *tierTwoNewsNationalNews = @"nationalnews"; /// Contextual Interests Tier 2: Science -static BVContextualInterest* tierTwoScienceAstrology = @"astrology"; -static BVContextualInterest* tierTwoScienceBiology = @"biology"; -static BVContextualInterest* tierTwoScienceBotany = @"botany"; -static BVContextualInterest* tierTwoScienceChemistry = @"chemistry"; -static BVContextualInterest* tierTwoScienceGeography = @"geography"; -static BVContextualInterest* tierTwoScienceGeology = @"geology"; -static BVContextualInterest* tierTwoScienceParanormalPhenomena = @"paranormalphenomena"; -static BVContextualInterest* tierTwoSciencePhysics = @"physics"; -static BVContextualInterest* tierTwoScienceSpaceAstronomy = @"spaceastronomy"; -static BVContextualInterest* tierTwoScienceWeather = @"weather"; +static BVContextualInterest *tierTwoScienceAstrology = @"astrology"; +static BVContextualInterest *tierTwoScienceBiology = @"biology"; +static BVContextualInterest *tierTwoScienceBotany = @"botany"; +static BVContextualInterest *tierTwoScienceChemistry = @"chemistry"; +static BVContextualInterest *tierTwoScienceGeography = @"geography"; +static BVContextualInterest *tierTwoScienceGeology = @"geology"; +static BVContextualInterest *tierTwoScienceParanormalPhenomena = + @"paranormalphenomena"; +static BVContextualInterest *tierTwoSciencePhysics = @"physics"; +static BVContextualInterest *tierTwoScienceSpaceAstronomy = @"spaceastronomy"; +static BVContextualInterest *tierTwoScienceWeather = @"weather"; /// Contextual Interests Tier 2: Arts & Entertainment -static BVContextualInterest* tierTwoArtsAndEntertainmentBooksAndLiterature = @"booksandliterature"; -static BVContextualInterest* tierTwoArtsAndEntertainmentCelebrityFanGossip = @"celebrityfangossip"; -static BVContextualInterest* tierTwoArtsAndEntertainmentFineArt = @"fineart"; -static BVContextualInterest* tierTwoArtsAndEntertainmentHumor = @"humor"; -static BVContextualInterest* tierTwoArtsAndEntertainmentMovies = @"movies"; -static BVContextualInterest* tierTwoArtsAndEntertainmentMusic = @"music"; -static BVContextualInterest* tierTwoArtsAndEntertainmentTelevision = @"television"; +static BVContextualInterest *tierTwoArtsAndEntertainmentBooksAndLiterature = + @"booksandliterature"; +static BVContextualInterest *tierTwoArtsAndEntertainmentCelebrityFanGossip = + @"celebrityfangossip"; +static BVContextualInterest *tierTwoArtsAndEntertainmentFineArt = @"fineart"; +static BVContextualInterest *tierTwoArtsAndEntertainmentHumor = @"humor"; +static BVContextualInterest *tierTwoArtsAndEntertainmentMovies = @"movies"; +static BVContextualInterest *tierTwoArtsAndEntertainmentMusic = @"music"; +static BVContextualInterest *tierTwoArtsAndEntertainmentTelevision = + @"television"; /// Contextual Interests Tier 2: Food & Drink -static BVContextualInterest* tierTwoFoodAndDrinkAmericanCuisine = @"americancuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkBarbecuesAndGrilling = @"barbecuesandgrilling"; -static BVContextualInterest* tierTwoFoodAndDrinkCajunCreole = @"cajuncreole"; -static BVContextualInterest* tierTwoFoodAndDrinkChineseCuisine = @"chinesecuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkCocktailsBeer = @"cocktailsbeer"; -static BVContextualInterest* tierTwoFoodAndDrinkCoffeeTea = @"coffeetea"; -static BVContextualInterest* tierTwoFoodAndDrinkCuisineSpecific = @"cuisinespecific"; -static BVContextualInterest* tierTwoFoodAndDrinkDessertsAndBaking = @"dessertsandbaking"; -static BVContextualInterest* tierTwoFoodAndDrinkDiningOut = @"diningout"; -static BVContextualInterest* tierTwoFoodAndDrinkFoodAllergies = @"foodallergies"; -static BVContextualInterest* tierTwoFoodAndDrinkFrenchCuisine = @"frenchcuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkHealthLowFatCooking = @"healthlowfatcooking"; -static BVContextualInterest* tierTwoFoodAndDrinkItalianCuisine = @"italiancuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkJapaneseCuisine = @"japanesecuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkMexicanCuisine = @"mexicancuisine"; -static BVContextualInterest* tierTwoFoodAndDrinkVegan = @"vegan"; -static BVContextualInterest* tierTwoFoodAndDrinkVegetarian = @"vegetarian"; -static BVContextualInterest* tierTwoFoodAndDrinkWine = @"wine"; +static BVContextualInterest *tierTwoFoodAndDrinkAmericanCuisine = + @"americancuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkBarbecuesAndGrilling = + @"barbecuesandgrilling"; +static BVContextualInterest *tierTwoFoodAndDrinkCajunCreole = @"cajuncreole"; +static BVContextualInterest *tierTwoFoodAndDrinkChineseCuisine = + @"chinesecuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkCocktailsBeer = + @"cocktailsbeer"; +static BVContextualInterest *tierTwoFoodAndDrinkCoffeeTea = @"coffeetea"; +static BVContextualInterest *tierTwoFoodAndDrinkCuisineSpecific = + @"cuisinespecific"; +static BVContextualInterest *tierTwoFoodAndDrinkDessertsAndBaking = + @"dessertsandbaking"; +static BVContextualInterest *tierTwoFoodAndDrinkDiningOut = @"diningout"; +static BVContextualInterest *tierTwoFoodAndDrinkFoodAllergies = + @"foodallergies"; +static BVContextualInterest *tierTwoFoodAndDrinkFrenchCuisine = + @"frenchcuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkHealthLowFatCooking = + @"healthlowfatcooking"; +static BVContextualInterest *tierTwoFoodAndDrinkItalianCuisine = + @"italiancuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkJapaneseCuisine = + @"japanesecuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkMexicanCuisine = + @"mexicancuisine"; +static BVContextualInterest *tierTwoFoodAndDrinkVegan = @"vegan"; +static BVContextualInterest *tierTwoFoodAndDrinkVegetarian = @"vegetarian"; +static BVContextualInterest *tierTwoFoodAndDrinkWine = @"wine"; /// Contextual Interests Tier 2: Sports -static BVContextualInterest* tierTwoSportsAutoRacing = @"autoracing"; -static BVContextualInterest* tierTwoSportsBaseball = @"baseball"; -static BVContextualInterest* tierTwoSportsBicycling = @"bicycling"; -static BVContextualInterest* tierTwoSportsBodybuilding = @"bodybuilding"; -static BVContextualInterest* tierTwoSportsBoxing = @"boxing"; -static BVContextualInterest* tierTwoSportsCanoeingKayaking = @"canoeingkayaking"; -static BVContextualInterest* tierTwoSportsCheerleading = @"cheerleading"; -static BVContextualInterest* tierTwoSportsClimbing = @"climbing"; -static BVContextualInterest* tierTwoSportsCricket = @"cricket"; -static BVContextualInterest* tierTwoSportsFigureSkating = @"figureskating"; -static BVContextualInterest* tierTwoSportsFlyFishing = @"flyfishing"; -static BVContextualInterest* tierTwoSportsFootball = @"football"; -static BVContextualInterest* tierTwoSportsFreshwaterFishing = @"freshwaterfishing"; -static BVContextualInterest* tierTwoSportsGameAndFish = @"gameandfish"; -static BVContextualInterest* tierTwoSportsGolf = @"golf"; -static BVContextualInterest* tierTwoSportsHorseRacing = @"horseracing"; -static BVContextualInterest* tierTwoSportsHorses = @"horses"; -static BVContextualInterest* tierTwoSportsHuntingShooting = @"huntingshooting"; -static BVContextualInterest* tierTwoSportsInlineSkating = @"inlineskating"; -static BVContextualInterest* tierTwoSportsMartialArts = @"martialarts"; -static BVContextualInterest* tierTwoSportsMountainBiking = @"mountainbiking"; -static BVContextualInterest* tierTwoSportsNASCARRacing = @"nascarracing"; -static BVContextualInterest* tierTwoSportsOlympics = @"olympics"; -static BVContextualInterest* tierTwoSportsPaintball = @"paintball"; -static BVContextualInterest* tierTwoSportsPowerAndMotorcycles = @"powerandmotorcycles"; -static BVContextualInterest* tierTwoSportsProBasketball = @"probasketball"; -static BVContextualInterest* tierTwoSportsProIceHockey = @"proicehockey"; -static BVContextualInterest* tierTwoSportsRodeo = @"rodeo"; -static BVContextualInterest* tierTwoSportsRugby = @"rugby"; -static BVContextualInterest* tierTwoSportsRunningJogging = @"runningjogging"; -static BVContextualInterest* tierTwoSportsSailing = @"sailing"; -static BVContextualInterest* tierTwoSportsSaltwaterFishing = @"saltwaterfishing"; -static BVContextualInterest* tierTwoSportsScubaDiving = @"scubadiving"; -static BVContextualInterest* tierTwoSportsSkateboarding = @"skateboarding"; -static BVContextualInterest* tierTwoSportsSkiing = @"skiing"; -static BVContextualInterest* tierTwoSportsSnowboarding = @"snowboarding"; -static BVContextualInterest* tierTwoSportsSurfingBodyboarding = @"surfingbodyboarding"; -static BVContextualInterest* tierTwoSportsSwimming = @"swimming"; -static BVContextualInterest* tierTwoSportsTableTennisPingPong = @"tabletennispingpong"; -static BVContextualInterest* tierTwoSportsTennis = @"tennis"; -static BVContextualInterest* tierTwoSportsVolleyball = @"volleyball"; -static BVContextualInterest* tierTwoSportsWalking = @"walking"; -static BVContextualInterest* tierTwoSportsWaterskiWakeboard = @"waterskiwakeboard"; -static BVContextualInterest* tierTwoSportsWorldSoccer = @"worldsoccer"; +static BVContextualInterest *tierTwoSportsAutoRacing = @"autoracing"; +static BVContextualInterest *tierTwoSportsBaseball = @"baseball"; +static BVContextualInterest *tierTwoSportsBicycling = @"bicycling"; +static BVContextualInterest *tierTwoSportsBodybuilding = @"bodybuilding"; +static BVContextualInterest *tierTwoSportsBoxing = @"boxing"; +static BVContextualInterest *tierTwoSportsCanoeingKayaking = + @"canoeingkayaking"; +static BVContextualInterest *tierTwoSportsCheerleading = @"cheerleading"; +static BVContextualInterest *tierTwoSportsClimbing = @"climbing"; +static BVContextualInterest *tierTwoSportsCricket = @"cricket"; +static BVContextualInterest *tierTwoSportsFigureSkating = @"figureskating"; +static BVContextualInterest *tierTwoSportsFlyFishing = @"flyfishing"; +static BVContextualInterest *tierTwoSportsFootball = @"football"; +static BVContextualInterest *tierTwoSportsFreshwaterFishing = + @"freshwaterfishing"; +static BVContextualInterest *tierTwoSportsGameAndFish = @"gameandfish"; +static BVContextualInterest *tierTwoSportsGolf = @"golf"; +static BVContextualInterest *tierTwoSportsHorseRacing = @"horseracing"; +static BVContextualInterest *tierTwoSportsHorses = @"horses"; +static BVContextualInterest *tierTwoSportsHuntingShooting = @"huntingshooting"; +static BVContextualInterest *tierTwoSportsInlineSkating = @"inlineskating"; +static BVContextualInterest *tierTwoSportsMartialArts = @"martialarts"; +static BVContextualInterest *tierTwoSportsMountainBiking = @"mountainbiking"; +static BVContextualInterest *tierTwoSportsNASCARRacing = @"nascarracing"; +static BVContextualInterest *tierTwoSportsOlympics = @"olympics"; +static BVContextualInterest *tierTwoSportsPaintball = @"paintball"; +static BVContextualInterest *tierTwoSportsPowerAndMotorcycles = + @"powerandmotorcycles"; +static BVContextualInterest *tierTwoSportsProBasketball = @"probasketball"; +static BVContextualInterest *tierTwoSportsProIceHockey = @"proicehockey"; +static BVContextualInterest *tierTwoSportsRodeo = @"rodeo"; +static BVContextualInterest *tierTwoSportsRugby = @"rugby"; +static BVContextualInterest *tierTwoSportsRunningJogging = @"runningjogging"; +static BVContextualInterest *tierTwoSportsSailing = @"sailing"; +static BVContextualInterest *tierTwoSportsSaltwaterFishing = + @"saltwaterfishing"; +static BVContextualInterest *tierTwoSportsScubaDiving = @"scubadiving"; +static BVContextualInterest *tierTwoSportsSkateboarding = @"skateboarding"; +static BVContextualInterest *tierTwoSportsSkiing = @"skiing"; +static BVContextualInterest *tierTwoSportsSnowboarding = @"snowboarding"; +static BVContextualInterest *tierTwoSportsSurfingBodyboarding = + @"surfingbodyboarding"; +static BVContextualInterest *tierTwoSportsSwimming = @"swimming"; +static BVContextualInterest *tierTwoSportsTableTennisPingPong = + @"tabletennispingpong"; +static BVContextualInterest *tierTwoSportsTennis = @"tennis"; +static BVContextualInterest *tierTwoSportsVolleyball = @"volleyball"; +static BVContextualInterest *tierTwoSportsWalking = @"walking"; +static BVContextualInterest *tierTwoSportsWaterskiWakeboard = + @"waterskiwakeboard"; +static BVContextualInterest *tierTwoSportsWorldSoccer = @"worldsoccer"; /// Contextual Interests Tier 2: Business -static BVContextualInterest* tierTwoBusinessAdvertising = @"advertising"; -static BVContextualInterest* tierTwoBusinessAgriculture = @"agriculture"; -static BVContextualInterest* tierTwoBusinessBiotechBiomedical = @"biotechbiomedical"; -static BVContextualInterest* tierTwoBusinessBusinessSoftware = @"businesssoftware"; -static BVContextualInterest* tierTwoBusinessBusinessSupplies = @"businesssupplies"; -static BVContextualInterest* tierTwoBusinessConstruction = @"construction"; -static BVContextualInterest* tierTwoBusinessForestry = @"forestry"; -static BVContextualInterest* tierTwoBusinessGovernment = @"government"; -static BVContextualInterest* tierTwoBusinessGreenSolutions = @"greensolutions"; -static BVContextualInterest* tierTwoBusinessHumanResources = @"humanresources"; -static BVContextualInterest* tierTwoBusinessLogistics = @"logistics"; -static BVContextualInterest* tierTwoBusinessMarketing = @"marketing"; -static BVContextualInterest* tierTwoBusinessMetals = @"metals"; -static BVContextualInterest* tierTwoBusinessRestaurant = @"restaurant"; +static BVContextualInterest *tierTwoBusinessAdvertising = @"advertising"; +static BVContextualInterest *tierTwoBusinessAgriculture = @"agriculture"; +static BVContextualInterest *tierTwoBusinessBiotechBiomedical = + @"biotechbiomedical"; +static BVContextualInterest *tierTwoBusinessBusinessSoftware = + @"businesssoftware"; +static BVContextualInterest *tierTwoBusinessBusinessSupplies = + @"businesssupplies"; +static BVContextualInterest *tierTwoBusinessConstruction = @"construction"; +static BVContextualInterest *tierTwoBusinessForestry = @"forestry"; +static BVContextualInterest *tierTwoBusinessGovernment = @"government"; +static BVContextualInterest *tierTwoBusinessGreenSolutions = @"greensolutions"; +static BVContextualInterest *tierTwoBusinessHumanResources = @"humanresources"; +static BVContextualInterest *tierTwoBusinessLogistics = @"logistics"; +static BVContextualInterest *tierTwoBusinessMarketing = @"marketing"; +static BVContextualInterest *tierTwoBusinessMetals = @"metals"; +static BVContextualInterest *tierTwoBusinessRestaurant = @"restaurant"; /// Contextual Interests Tier 2: Religion and Spirituality -static BVContextualInterest* tierTwoReligionandSpiritualityAlternativeReligions = @"alternativereligions"; -static BVContextualInterest* tierTwoReligionandSpiritualityAtheismAgnosticism = @"atheismagnosticism"; -static BVContextualInterest* tierTwoReligionandSpiritualityBuddhism = @"buddhism"; -static BVContextualInterest* tierTwoReligionandSpiritualityCatholicism = @"catholicism"; -static BVContextualInterest* tierTwoReligionandSpiritualityChristianity = @"christianity"; -static BVContextualInterest* tierTwoReligionandSpiritualityHinduism = @"hinduism"; -static BVContextualInterest* tierTwoReligionandSpiritualityIslam = @"islam"; -static BVContextualInterest* tierTwoReligionandSpiritualityJudaism = @"judaism"; -static BVContextualInterest* tierTwoReligionandSpiritualityLatterDaySaints = @"latterdaysaints"; -static BVContextualInterest* tierTwoReligionandSpiritualityPaganWiccan = @"paganwiccan"; +static BVContextualInterest + *tierTwoReligionandSpiritualityAlternativeReligions = + @"alternativereligions"; +static BVContextualInterest *tierTwoReligionandSpiritualityAtheismAgnosticism = + @"atheismagnosticism"; +static BVContextualInterest *tierTwoReligionandSpiritualityBuddhism = + @"buddhism"; +static BVContextualInterest *tierTwoReligionandSpiritualityCatholicism = + @"catholicism"; +static BVContextualInterest *tierTwoReligionandSpiritualityChristianity = + @"christianity"; +static BVContextualInterest *tierTwoReligionandSpiritualityHinduism = + @"hinduism"; +static BVContextualInterest *tierTwoReligionandSpiritualityIslam = @"islam"; +static BVContextualInterest *tierTwoReligionandSpiritualityJudaism = @"judaism"; +static BVContextualInterest *tierTwoReligionandSpiritualityLatterDaySaints = + @"latterdaysaints"; +static BVContextualInterest *tierTwoReligionandSpiritualityPaganWiccan = + @"paganwiccan"; /// Contextual Interests Tier 2: Home & Garden -static BVContextualInterest* tierTwoHomeAndGardenAppliances = @"appliances"; -static BVContextualInterest* tierTwoHomeAndGardenBath = @"bath"; -static BVContextualInterest* tierTwoHomeAndGardenBedding = @"bedding"; -static BVContextualInterest* tierTwoHomeAndGardenBuildingAndHardware = @"buildingandhardware"; -static BVContextualInterest* tierTwoHomeAndGardenCoffeeMakers = @"coffeemakers"; -static BVContextualInterest* tierTwoHomeAndGardenElectricalAndSolar = @"electricalandsolar"; -static BVContextualInterest* tierTwoHomeAndGardenEntertaining = @"entertaining"; -static BVContextualInterest* tierTwoHomeAndGardenEnvironmentalSafety = @"environmentalsafety"; -static BVContextualInterest* tierTwoHomeAndGardenFurniture = @"furniture"; -static BVContextualInterest* tierTwoHomeAndGardenGardening = @"gardening"; -static BVContextualInterest* tierTwoHomeAndGardenHeatingCoolingAndAir = @"heatingcoolingandair"; -static BVContextualInterest* tierTwoHomeAndGardenHomeDecor = @"homedecor"; -static BVContextualInterest* tierTwoHomeAndGardenHomeRepair = @"homerepair"; -static BVContextualInterest* tierTwoHomeAndGardenHomeSecurity = @"homesecurity"; -static BVContextualInterest* tierTwoHomeAndGardenHomeTheater = @"hometheater"; -static BVContextualInterest* tierTwoHomeAndGardenHousekeepingAndOrganization = @"housekeepingandorganization"; -static BVContextualInterest* tierTwoHomeAndGardenInteriorDecorating = @"interiordecorating"; -static BVContextualInterest* tierTwoHomeAndGardenKitchenAndDining = @"kitchenanddining"; -static BVContextualInterest* tierTwoHomeAndGardenLampsAndLighting = @"lampsandlighting"; -static BVContextualInterest* tierTwoHomeAndGardenLandscaping = @"landscaping"; -static BVContextualInterest* tierTwoHomeAndGardenMajorAppliances = @"majorappliances"; -static BVContextualInterest* tierTwoHomeAndGardenOutdoor = @"outdoor"; -static BVContextualInterest* tierTwoHomeAndGardenOutdoorLiving = @"outdoorliving"; -static BVContextualInterest* tierTwoHomeAndGardenPlumbingAndFixtures = @"plumbingandfixtures"; -static BVContextualInterest* tierTwoHomeAndGardenRemodelingAndConstruction = @"remodelingandconstruction"; -static BVContextualInterest* tierTwoHomeAndGardenRugsAndCarpets = @"rugsandcarpets"; -static BVContextualInterest* tierTwoHomeAndGardenSmallAppliances = @"smallappliances"; -static BVContextualInterest* tierTwoHomeAndGardenTools = @"tools"; -static BVContextualInterest* tierTwoHomeAndGardenWindowTreatments = @"windowtreatments"; -static BVContextualInterest* tierTwoHomeAndGardenYardAndGarden = @"yardandgarden"; +static BVContextualInterest *tierTwoHomeAndGardenAppliances = @"appliances"; +static BVContextualInterest *tierTwoHomeAndGardenBath = @"bath"; +static BVContextualInterest *tierTwoHomeAndGardenBedding = @"bedding"; +static BVContextualInterest *tierTwoHomeAndGardenBuildingAndHardware = + @"buildingandhardware"; +static BVContextualInterest *tierTwoHomeAndGardenCoffeeMakers = @"coffeemakers"; +static BVContextualInterest *tierTwoHomeAndGardenElectricalAndSolar = + @"electricalandsolar"; +static BVContextualInterest *tierTwoHomeAndGardenEntertaining = @"entertaining"; +static BVContextualInterest *tierTwoHomeAndGardenEnvironmentalSafety = + @"environmentalsafety"; +static BVContextualInterest *tierTwoHomeAndGardenFurniture = @"furniture"; +static BVContextualInterest *tierTwoHomeAndGardenGardening = @"gardening"; +static BVContextualInterest *tierTwoHomeAndGardenHeatingCoolingAndAir = + @"heatingcoolingandair"; +static BVContextualInterest *tierTwoHomeAndGardenHomeDecor = @"homedecor"; +static BVContextualInterest *tierTwoHomeAndGardenHomeRepair = @"homerepair"; +static BVContextualInterest *tierTwoHomeAndGardenHomeSecurity = @"homesecurity"; +static BVContextualInterest *tierTwoHomeAndGardenHomeTheater = @"hometheater"; +static BVContextualInterest *tierTwoHomeAndGardenHousekeepingAndOrganization = + @"housekeepingandorganization"; +static BVContextualInterest *tierTwoHomeAndGardenInteriorDecorating = + @"interiordecorating"; +static BVContextualInterest *tierTwoHomeAndGardenKitchenAndDining = + @"kitchenanddining"; +static BVContextualInterest *tierTwoHomeAndGardenLampsAndLighting = + @"lampsandlighting"; +static BVContextualInterest *tierTwoHomeAndGardenLandscaping = @"landscaping"; +static BVContextualInterest *tierTwoHomeAndGardenMajorAppliances = + @"majorappliances"; +static BVContextualInterest *tierTwoHomeAndGardenOutdoor = @"outdoor"; +static BVContextualInterest *tierTwoHomeAndGardenOutdoorLiving = + @"outdoorliving"; +static BVContextualInterest *tierTwoHomeAndGardenPlumbingAndFixtures = + @"plumbingandfixtures"; +static BVContextualInterest *tierTwoHomeAndGardenRemodelingAndConstruction = + @"remodelingandconstruction"; +static BVContextualInterest *tierTwoHomeAndGardenRugsAndCarpets = + @"rugsandcarpets"; +static BVContextualInterest *tierTwoHomeAndGardenSmallAppliances = + @"smallappliances"; +static BVContextualInterest *tierTwoHomeAndGardenTools = @"tools"; +static BVContextualInterest *tierTwoHomeAndGardenWindowTreatments = + @"windowtreatments"; +static BVContextualInterest *tierTwoHomeAndGardenYardAndGarden = + @"yardandgarden"; /// Contextual Interests Tier 2: Hobbies & Interests -static BVContextualInterest* tierTwoHobbiesAndInterestsArtTechnology = @"arttechnology"; -static BVContextualInterest* tierTwoHobbiesAndInterestsArtsAndCrafts = @"artsandcrafts"; -static BVContextualInterest* tierTwoHobbiesAndInterestsBeadwork = @"beadwork"; -static BVContextualInterest* tierTwoHobbiesAndInterestsBirdwatching = @"birdwatching"; -static BVContextualInterest* tierTwoHobbiesAndInterestsBoardGamesPuzzles = @"boardgamespuzzles"; -static BVContextualInterest* tierTwoHobbiesAndInterestsCandleAndSoapMaking = @"candleandsoapmaking"; -static BVContextualInterest* tierTwoHobbiesAndInterestsCardGames = @"cardgames"; -static BVContextualInterest* tierTwoHobbiesAndInterestsChess = @"chess"; -static BVContextualInterest* tierTwoHobbiesAndInterestsCigars = @"cigars"; -static BVContextualInterest* tierTwoHobbiesAndInterestsCollecting = @"collecting"; -static BVContextualInterest* tierTwoHobbiesAndInterestsComicBooks = @"comicbooks"; -static BVContextualInterest* tierTwoHobbiesAndInterestsDrawingSketching = @"drawingsketching"; -static BVContextualInterest* tierTwoHobbiesAndInterestsFreelanceWriting = @"freelancewriting"; -static BVContextualInterest* tierTwoHobbiesAndInterestsGenealogy = @"genealogy"; -static BVContextualInterest* tierTwoHobbiesAndInterestsGettingPublished = @"gettingpublished"; -static BVContextualInterest* tierTwoHobbiesAndInterestsGuitar = @"guitar"; -static BVContextualInterest* tierTwoHobbiesAndInterestsHomeRecording = @"homerecording"; -static BVContextualInterest* tierTwoHobbiesAndInterestsInvestorsAndPatents = @"investorsandpatents"; -static BVContextualInterest* tierTwoHobbiesAndInterestsJewelryMaking = @"jewelrymaking"; -static BVContextualInterest* tierTwoHobbiesAndInterestsMagicAndIllusion = @"magicandillusion"; -static BVContextualInterest* tierTwoHobbiesAndInterestsNeedlework = @"needlework"; -static BVContextualInterest* tierTwoHobbiesAndInterestsPainting = @"painting"; -static BVContextualInterest* tierTwoHobbiesAndInterestsPhotography = @"photography"; -static BVContextualInterest* tierTwoHobbiesAndInterestsRadio = @"radio"; -static BVContextualInterest* tierTwoHobbiesAndInterestsRoleplayingGames = @"roleplayinggames"; -static BVContextualInterest* tierTwoHobbiesAndInterestsSciFiAndFantasy = @"scifiandfantasy"; -static BVContextualInterest* tierTwoHobbiesAndInterestsScrapbooking = @"scrapbooking"; -static BVContextualInterest* tierTwoHobbiesAndInterestsScreenwriting = @"screenwriting"; -static BVContextualInterest* tierTwoHobbiesAndInterestsStampsAndCoins = @"stampsandcoins"; -static BVContextualInterest* tierTwoHobbiesAndInterestsToys = @"toys"; -static BVContextualInterest* tierTwoHobbiesAndInterestsVideoAndComputerGames = @"videoandcomputergames"; -static BVContextualInterest* tierTwoHobbiesAndInterestsWoodworking = @"woodworking"; +static BVContextualInterest *tierTwoHobbiesAndInterestsArtTechnology = + @"arttechnology"; +static BVContextualInterest *tierTwoHobbiesAndInterestsArtsAndCrafts = + @"artsandcrafts"; +static BVContextualInterest *tierTwoHobbiesAndInterestsBeadwork = @"beadwork"; +static BVContextualInterest *tierTwoHobbiesAndInterestsBirdwatching = + @"birdwatching"; +static BVContextualInterest *tierTwoHobbiesAndInterestsBoardGamesPuzzles = + @"boardgamespuzzles"; +static BVContextualInterest *tierTwoHobbiesAndInterestsCandleAndSoapMaking = + @"candleandsoapmaking"; +static BVContextualInterest *tierTwoHobbiesAndInterestsCardGames = @"cardgames"; +static BVContextualInterest *tierTwoHobbiesAndInterestsChess = @"chess"; +static BVContextualInterest *tierTwoHobbiesAndInterestsCigars = @"cigars"; +static BVContextualInterest *tierTwoHobbiesAndInterestsCollecting = + @"collecting"; +static BVContextualInterest *tierTwoHobbiesAndInterestsComicBooks = + @"comicbooks"; +static BVContextualInterest *tierTwoHobbiesAndInterestsDrawingSketching = + @"drawingsketching"; +static BVContextualInterest *tierTwoHobbiesAndInterestsFreelanceWriting = + @"freelancewriting"; +static BVContextualInterest *tierTwoHobbiesAndInterestsGenealogy = @"genealogy"; +static BVContextualInterest *tierTwoHobbiesAndInterestsGettingPublished = + @"gettingpublished"; +static BVContextualInterest *tierTwoHobbiesAndInterestsGuitar = @"guitar"; +static BVContextualInterest *tierTwoHobbiesAndInterestsHomeRecording = + @"homerecording"; +static BVContextualInterest *tierTwoHobbiesAndInterestsInvestorsAndPatents = + @"investorsandpatents"; +static BVContextualInterest *tierTwoHobbiesAndInterestsJewelryMaking = + @"jewelrymaking"; +static BVContextualInterest *tierTwoHobbiesAndInterestsMagicAndIllusion = + @"magicandillusion"; +static BVContextualInterest *tierTwoHobbiesAndInterestsNeedlework = + @"needlework"; +static BVContextualInterest *tierTwoHobbiesAndInterestsPainting = @"painting"; +static BVContextualInterest *tierTwoHobbiesAndInterestsPhotography = + @"photography"; +static BVContextualInterest *tierTwoHobbiesAndInterestsRadio = @"radio"; +static BVContextualInterest *tierTwoHobbiesAndInterestsRoleplayingGames = + @"roleplayinggames"; +static BVContextualInterest *tierTwoHobbiesAndInterestsSciFiAndFantasy = + @"scifiandfantasy"; +static BVContextualInterest *tierTwoHobbiesAndInterestsScrapbooking = + @"scrapbooking"; +static BVContextualInterest *tierTwoHobbiesAndInterestsScreenwriting = + @"screenwriting"; +static BVContextualInterest *tierTwoHobbiesAndInterestsStampsAndCoins = + @"stampsandcoins"; +static BVContextualInterest *tierTwoHobbiesAndInterestsToys = @"toys"; +static BVContextualInterest *tierTwoHobbiesAndInterestsVideoAndComputerGames = + @"videoandcomputergames"; +static BVContextualInterest *tierTwoHobbiesAndInterestsWoodworking = + @"woodworking"; /// Contextual Interests Tier 2: Sporting Goods -static BVContextualInterest* tierTwoSportingGoodsCampingAndHiking = @"campingandhiking"; -static BVContextualInterest* tierTwoSportingGoodsExerciseAndFitness = @"exerciseandfitness"; -static BVContextualInterest* tierTwoSportingGoodsIndividualSports = @"individualsports"; -static BVContextualInterest* tierTwoSportingGoodsTeamSports = @"teamsports"; +static BVContextualInterest *tierTwoSportingGoodsCampingAndHiking = + @"campingandhiking"; +static BVContextualInterest *tierTwoSportingGoodsExerciseAndFitness = + @"exerciseandfitness"; +static BVContextualInterest *tierTwoSportingGoodsIndividualSports = + @"individualsports"; +static BVContextualInterest *tierTwoSportingGoodsTeamSports = @"teamsports"; /// Contextual Interests Tier 2: Education -static BVContextualInterest* tierTwoEducation712Education = @"712education"; -static BVContextualInterest* tierTwoEducationAdultEducation = @"adulteducation"; -static BVContextualInterest* tierTwoEducationArtHistory = @"arthistory"; -static BVContextualInterest* tierTwoEducationCollegeAdministration = @"collegeadministration"; -static BVContextualInterest* tierTwoEducationCollegeLife = @"collegelife"; -static BVContextualInterest* tierTwoEducationDistanceLearning = @"distancelearning"; -static BVContextualInterest* tierTwoEducationEnglishasa2ndLanguage = @"englishasa2ndlanguage"; -static BVContextualInterest* tierTwoEducationGraduateSchool = @"graduateschool"; -static BVContextualInterest* tierTwoEducationHomeschooling = @"homeschooling"; -static BVContextualInterest* tierTwoEducationHomeworkStudyTips = @"homeworkstudytips"; -static BVContextualInterest* tierTwoEducationK6Educators = @"k6educators"; -static BVContextualInterest* tierTwoEducationLanguageLearning = @"languagelearning"; -static BVContextualInterest* tierTwoEducationPrivateSchool = @"privateschool"; -static BVContextualInterest* tierTwoEducationSpecialEducation = @"specialeducation"; -static BVContextualInterest* tierTwoEducationStudyingBusiness = @"studyingbusiness"; +static BVContextualInterest *tierTwoEducation712Education = @"712education"; +static BVContextualInterest *tierTwoEducationAdultEducation = @"adulteducation"; +static BVContextualInterest *tierTwoEducationArtHistory = @"arthistory"; +static BVContextualInterest *tierTwoEducationCollegeAdministration = + @"collegeadministration"; +static BVContextualInterest *tierTwoEducationCollegeLife = @"collegelife"; +static BVContextualInterest *tierTwoEducationDistanceLearning = + @"distancelearning"; +static BVContextualInterest *tierTwoEducationEnglishasa2ndLanguage = + @"englishasa2ndlanguage"; +static BVContextualInterest *tierTwoEducationGraduateSchool = @"graduateschool"; +static BVContextualInterest *tierTwoEducationHomeschooling = @"homeschooling"; +static BVContextualInterest *tierTwoEducationHomeworkStudyTips = + @"homeworkstudytips"; +static BVContextualInterest *tierTwoEducationK6Educators = @"k6educators"; +static BVContextualInterest *tierTwoEducationLanguageLearning = + @"languagelearning"; +static BVContextualInterest *tierTwoEducationPrivateSchool = @"privateschool"; +static BVContextualInterest *tierTwoEducationSpecialEducation = + @"specialeducation"; +static BVContextualInterest *tierTwoEducationStudyingBusiness = + @"studyingbusiness"; /// Contextual Interests Tier 2: Consumer Electronics -static BVContextualInterest* tierTwoConsumerElectronicsAudio = @"audio"; -static BVContextualInterest* tierTwoConsumerElectronicsBatteriesAndPower = @"batteriesandpower"; -static BVContextualInterest* tierTwoConsumerElectronicsCableBoxes = @"cableboxes"; -static BVContextualInterest* tierTwoConsumerElectronicsCarElectronicsAndGPS = @"carelectronicsandgps"; -static BVContextualInterest* tierTwoConsumerElectronicsCellPhoneCases = @"cellphonecases"; -static BVContextualInterest* tierTwoConsumerElectronicsCellPhoneChargers = @"cellphonechargers"; -static BVContextualInterest* tierTwoConsumerElectronicsCellPhoneHeadsets = @"cellphoneheadsets"; -static BVContextualInterest* tierTwoConsumerElectronicsComputerAccessories = @"computeraccessories"; -static BVContextualInterest* tierTwoConsumerElectronicsDesktopComputers = @"desktopcomputers"; -static BVContextualInterest* tierTwoConsumerElectronicsHeadphones = @"headphones"; -static BVContextualInterest* tierTwoConsumerElectronicsHomeNetworkingConnectivity = @"homenetworkingconnectivity"; -static BVContextualInterest* tierTwoConsumerElectronicsInternetAndMediaStreamers = @"internetandmediastreamers"; -static BVContextualInterest* tierTwoConsumerElectronicsLaptops = @"laptops"; -static BVContextualInterest* tierTwoConsumerElectronicsOtherGadgets = @"othergadgets"; -static BVContextualInterest* tierTwoConsumerElectronicsPrintersAndScanners = @"printersandscanners"; -static BVContextualInterest* tierTwoConsumerElectronicsSoftware = @"software"; -static BVContextualInterest* tierTwoConsumerElectronicsTabletAccessories = @"tabletaccessories"; -static BVContextualInterest* tierTwoConsumerElectronicsTablets = @"tablets"; -static BVContextualInterest* tierTwoConsumerElectronicsTelevisions = @"televisions"; -static BVContextualInterest* tierTwoConsumerElectronicsVideoGameConsoles = @"videogameconsoles"; -static BVContextualInterest* tierTwoConsumerElectronicsWearableTechnology = @"wearabletechnology"; +static BVContextualInterest *tierTwoConsumerElectronicsAudio = @"audio"; +static BVContextualInterest *tierTwoConsumerElectronicsBatteriesAndPower = + @"batteriesandpower"; +static BVContextualInterest *tierTwoConsumerElectronicsCableBoxes = + @"cableboxes"; +static BVContextualInterest *tierTwoConsumerElectronicsCarElectronicsAndGPS = + @"carelectronicsandgps"; +static BVContextualInterest *tierTwoConsumerElectronicsCellPhoneCases = + @"cellphonecases"; +static BVContextualInterest *tierTwoConsumerElectronicsCellPhoneChargers = + @"cellphonechargers"; +static BVContextualInterest *tierTwoConsumerElectronicsCellPhoneHeadsets = + @"cellphoneheadsets"; +static BVContextualInterest *tierTwoConsumerElectronicsComputerAccessories = + @"computeraccessories"; +static BVContextualInterest *tierTwoConsumerElectronicsDesktopComputers = + @"desktopcomputers"; +static BVContextualInterest *tierTwoConsumerElectronicsHeadphones = + @"headphones"; +static BVContextualInterest + *tierTwoConsumerElectronicsHomeNetworkingConnectivity = + @"homenetworkingconnectivity"; +static BVContextualInterest + *tierTwoConsumerElectronicsInternetAndMediaStreamers = + @"internetandmediastreamers"; +static BVContextualInterest *tierTwoConsumerElectronicsLaptops = @"laptops"; +static BVContextualInterest *tierTwoConsumerElectronicsOtherGadgets = + @"othergadgets"; +static BVContextualInterest *tierTwoConsumerElectronicsPrintersAndScanners = + @"printersandscanners"; +static BVContextualInterest *tierTwoConsumerElectronicsSoftware = @"software"; +static BVContextualInterest *tierTwoConsumerElectronicsTabletAccessories = + @"tabletaccessories"; +static BVContextualInterest *tierTwoConsumerElectronicsTablets = @"tablets"; +static BVContextualInterest *tierTwoConsumerElectronicsTelevisions = + @"televisions"; +static BVContextualInterest *tierTwoConsumerElectronicsVideoGameConsoles = + @"videogameconsoles"; +static BVContextualInterest *tierTwoConsumerElectronicsWearableTechnology = + @"wearabletechnology"; /// Contextual Interests Tier 2: Real Estate -static BVContextualInterest* tierTwoRealEstateApartments = @"apartments"; -static BVContextualInterest* tierTwoRealEstateArchitects = @"architects"; -static BVContextualInterest* tierTwoRealEstateBuyingSellingHomes = @"buyingsellinghomes"; -static BVContextualInterest* tierTwoRealEstateCommercial = @"commercial"; +static BVContextualInterest *tierTwoRealEstateApartments = @"apartments"; +static BVContextualInterest *tierTwoRealEstateArchitects = @"architects"; +static BVContextualInterest *tierTwoRealEstateBuyingSellingHomes = + @"buyingsellinghomes"; +static BVContextualInterest *tierTwoRealEstateCommercial = @"commercial"; /// Contextual Interests Tier 2: Style & Fashion -static BVContextualInterest* tierTwoStyleAndFashionAccessories = @"accessories"; -static BVContextualInterest* tierTwoStyleAndFashionBeauty = @"beauty2"; -static BVContextualInterest* tierTwoStyleAndFashionBodyArt = @"bodyart"; -static BVContextualInterest* tierTwoStyleAndFashionClothing = @"clothing"; -static BVContextualInterest* tierTwoStyleAndFashionFashion = @"fashion"; -static BVContextualInterest* tierTwoStyleAndFashionJewelry = @"jewelry"; - +static BVContextualInterest *tierTwoStyleAndFashionAccessories = @"accessories"; +static BVContextualInterest *tierTwoStyleAndFashionBeauty = @"beauty2"; +static BVContextualInterest *tierTwoStyleAndFashionBodyArt = @"bodyart"; +static BVContextualInterest *tierTwoStyleAndFashionClothing = @"clothing"; +static BVContextualInterest *tierTwoStyleAndFashionFashion = @"fashion"; +static BVContextualInterest *tierTwoStyleAndFashionJewelry = @"jewelry"; diff --git a/Pod/BVAnalytics/Private/BVGMBLSighting.h b/Pod/BVAnalytics/Private/BVGMBLSighting.h index 9705b0a7..3ddf4fbf 100644 --- a/Pod/BVAnalytics/Private/BVGMBLSighting.h +++ b/Pod/BVAnalytics/Private/BVGMBLSighting.h @@ -8,7 +8,8 @@ #import /** - * BVGMBLSighting is a holder object that is used to hold and pass around Gimbal-based 'Sighting' events. + * BVGMBLSighting is a holder object that is used to hold and pass around + * Gimbal-based 'Sighting' events. */ @interface BVGMBLSighting : NSObject diff --git a/Pod/BVAnalytics/Private/BVGMBLVisit.h b/Pod/BVAnalytics/Private/BVGMBLVisit.h index b1045226..18bf1f98 100644 --- a/Pod/BVAnalytics/Private/BVGMBLVisit.h +++ b/Pod/BVAnalytics/Private/BVGMBLVisit.h @@ -8,14 +8,15 @@ #import /** - * BVGMBLVisit is a holder object that is used to hold and pass around Gimbal-based 'Visit' events. + * BVGMBLVisit is a holder object that is used to hold and pass around + * Gimbal-based 'Visit' events. */ @interface BVGMBLVisit : NSObject -@property NSDate* arrivalDate; +@property NSDate *arrivalDate; @property NSTimeInterval dwellTime; -@property NSDate* departureDate; -@property NSString* identifier; -@property NSString* name; +@property NSDate *departureDate; +@property NSString *identifier; +@property NSString *name; @end diff --git a/Pod/BVAnalytics/Private/BVLocationWrapper.h b/Pod/BVAnalytics/Private/BVLocationWrapper.h index 23ad6499..dbf6cbb8 100644 --- a/Pod/BVAnalytics/Private/BVLocationWrapper.h +++ b/Pod/BVAnalytics/Private/BVLocationWrapper.h @@ -5,16 +5,15 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import #import +#import - - -// BVLocationWrapper is a holder object that is used to hold and pass around Locaton event data. +// BVLocationWrapper is a holder object that is used to hold and pass around +// Locaton event data. @interface BVLocationWrapper : NSObject -@property NSString* contextualTier1; -@property NSString* contextualTier2; -@property CLLocation* location; +@property NSString *contextualTier1; +@property NSString *contextualTier2; +@property CLLocation *location; @end diff --git a/Pod/BVCommon/BVAuthenticatedUser.h b/Pod/BVCommon/BVAuthenticatedUser.h index 808534e2..7200d421 100644 --- a/Pod/BVCommon/BVAuthenticatedUser.h +++ b/Pod/BVCommon/BVAuthenticatedUser.h @@ -5,10 +5,11 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // +#define BV_INTERNAL_PROFILE_UPDATED_COMPLETED \ + @"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" -#define BV_INTERNAL_PROFILE_UPDATED_COMPLETED @"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" - -/// user information can be included in the userAuthString - sent with: [[BVSDKManager sharedManager] setUserWithAuthString:myAuthString]; +/// user information can be included in the userAuthString - sent with: +/// [[BVSDKManager sharedManager] setUserWithAuthString:myAuthString]; #define BVUSER_ID @"userid" #define BVUSER_AGE @"age"; @@ -17,30 +18,37 @@ #define BVUSER_FACEBOOKID = @"facebookId"; #define BVUSER_TWITTERID = @"twitterId"; - #import /** - This class provides Bazaarvoice to register the current device with an existing user profile. In order to use this profile, you must provide your client application a valid authentication string, calculated server-side. For more information on calculating the Authorization string, please refer to the [Bazaarvoice documentation](http://knowledge.bazaarvoice.com/wp-content/conversations/en_US/KB/Default.htm#Authentication/Site_authentication/Tech_integration_site_auth.htm%23Generate) . The authentication recipe is valid for any customer of the Converstaions, Ads, or Recommendations APIs. + This class provides Bazaarvoice to register the current device with an + existing user profile. In order to use this profile, you must provide your + client application a valid authentication string, calculated server-side. For + more information on calculating the Authorization string, please refer to the + [Bazaarvoice + documentation](http://knowledge.bazaarvoice.com/wp-content/conversations/en_US/KB/Default.htm#Authentication/Site_authentication/Tech_integration_site_auth.htm%23Generate) + . The authentication recipe is valid for any customer of the Converstaions, + Ads, or Recommendations APIs. */ @interface BVAuthenticatedUser : NSObject - /// The server-side calulated Authentication string -@property NSString* userAuthString; - +@property NSString *userAuthString; /** - Update the user profile for the client device given a valid Authentication string. - - @param force Force the profile to be updated, regardless last time of update. + Update the user profile for the client device given a valid Authentication + string. + + @param force Force the profile to be updated, regardless last time of + update. @param passKey The Authentication string @param isStage pass in YES if using staging server, NO for production. */ --(void)updateProfile:(bool)force withAPIKey:(NSString *)passKey isStaging:(BOOL)isStage; - +- (void)updateProfile:(bool)force + withAPIKey:(NSString *)passKey + isStaging:(BOOL)isStage; /// Internal use only. Use [[BVSDKManager sharedManager] getCustomTargeting]. --(NSDictionary*)getTargetingKeywords; +- (NSDictionary *)getTargetingKeywords; @end diff --git a/Pod/BVCommon/BVAuthenticatedUser.m b/Pod/BVCommon/BVAuthenticatedUser.m index d4444afc..f8809688 100644 --- a/Pod/BVCommon/BVAuthenticatedUser.m +++ b/Pod/BVCommon/BVAuthenticatedUser.m @@ -6,147 +6,170 @@ // #import "BVAuthenticatedUser.h" -#import #import "BVCore.h" +#import -@interface BVAuthenticatedUser() +@interface BVAuthenticatedUser () -@property NSDictionary* personalizedPreferences; +@property NSDictionary *personalizedPreferences; @end @implementation BVAuthenticatedUser --(void)updateProfile:(bool)force withAPIKey:(NSString *)passKey isStaging:(BOOL)isStage { - - // don't grab profile if user has opted for limited ad targeting +- (void)updateProfile:(bool)force + withAPIKey:(NSString *)passKey + isStaging:(BOOL)isStage { +// don't grab profile if user has opted for limited ad targeting #ifdef DISABLE_BVSDK_IDFA - return; + return; #else - if(![[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]){ - return; - } - - if(force || [self shouldUpdateProfile]){ - - NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; - NSString* adsPassKey = passKey; - NSString* baseUrl = [BVSDKManager sharedManager].urlRootShopperAdvertising; - - NSString* profileUrl = [NSString stringWithFormat:@"%@/users/magpie_idfa_%@?passkey=%@", baseUrl, idfa, adsPassKey]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", profileUrl]]; - - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; - - NSURLSessionDataTask *profileTask = [session dataTaskWithURL:[NSURL URLWithString:profileUrl] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ - - // Completion - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if ((httpResponse && httpResponse.statusCode < 300) && data != nil){ - - // Success - - NSError *errorJson; - NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&errorJson]; - - if (!errorJson){ - - // JSON response parsing. - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: (%ld): %@", (long)httpResponse.statusCode, responseDict]]; - - if([self.personalizedPreferences isEqualToDictionary:responseDict]){ - return; // No update - } - - NSString* message = [NSString stringWithFormat:@"Profile for current user (may take a few moments to update): %@", responseDict]; - [[BVLogger sharedLogger] info:message]; - - self.personalizedPreferences = responseDict; - - } else { - - // Malformed JSON - NSString *errString = [NSString stringWithFormat:@"ERROR: Authenticated User Profile JSON error: %@", errorJson.localizedDescription]; - [[BVLogger sharedLogger] error:errString]; - - } - - // For internal testing - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:BV_INTERNAL_PROFILE_UPDATED_COMPLETED object:self]; - }); - + if (![[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) { + return; + } + + if (force || [self shouldUpdateProfile]) { + NSString *idfa = [ + [[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; + NSString *adsPassKey = passKey; + NSString *baseUrl = [BVSDKManager sharedManager].urlRootShopperAdvertising; + + NSString *profileUrl = + [NSString stringWithFormat:@"%@/users/magpie_idfa_%@?passkey=%@", + baseUrl, idfa, adsPassKey]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"GET: %@", profileUrl]]; + + NSURLSessionConfiguration *config = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; + + NSURLSessionDataTask *profileTask = [session + dataTaskWithURL:[NSURL URLWithString:profileUrl] + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { + + // Completion + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if ((httpResponse && httpResponse.statusCode < 300) && data != nil) { + // Success + + NSError *errorJson; + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&errorJson]; + + if (!errorJson) { + // JSON response parsing. + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"RESPONSE: (%ld): %@", + (long)httpResponse.statusCode, + responseDict]]; + + if ([self.personalizedPreferences + isEqualToDictionary:responseDict]) { + return; // No update + } + + NSString *message = + [NSString stringWithFormat:@"Profile for current " + @"user (may take a few " + @"moments to update): %@", + responseDict]; + [[BVLogger sharedLogger] info:message]; + + self.personalizedPreferences = responseDict; + } else { - - // Failure - NSString *errString = [NSString stringWithFormat:@"ERROR: Authenticated User Profile response. HTTP Status(%ld) with error: %@", (long)httpResponse.statusCode, error]; - [[BVLogger sharedLogger] error:errString]; - - // For internal testing - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:BV_INTERNAL_PROFILE_UPDATED_COMPLETED object:nil]; - }); + // Malformed JSON + NSString *errString = [NSString + stringWithFormat:@"ERROR: Authenticated User Profile " + @"JSON error: %@", + errorJson.localizedDescription]; + [[BVLogger sharedLogger] error:errString]; } - + + // For internal testing + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] + postNotificationName:BV_INTERNAL_PROFILE_UPDATED_COMPLETED + object:self]; + }); + + } else { + // Failure + NSString *errString = [NSString + stringWithFormat:@"ERROR: Authenticated User Profile " + @"response. HTTP Status(%ld) with " + @"error: %@", + (long)httpResponse.statusCode, error]; + [[BVLogger sharedLogger] error:errString]; + + // For internal testing + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] + postNotificationName:BV_INTERNAL_PROFILE_UPDATED_COMPLETED + object:nil]; + }); + } + }]; - - [profileTask resume]; - } + [profileTask resume]; + } #endif // DISABLE_BVSDK_IDFA - } --(bool)shouldUpdateProfile { - - // Return true if the profile does not have any targeting keywords at all - return [self getTargetingKeywords] == nil || [[self getTargetingKeywords] allKeys].count == 0; +- (bool)shouldUpdateProfile { + // Return true if the profile does not have any targeting keywords at all + return [self getTargetingKeywords] == nil || + [[self getTargetingKeywords] allKeys].count == 0; } --(NSDictionary*)getTargetingKeywords { - +- (NSDictionary *)getTargetingKeywords { #ifdef DISABLE_BVSDK_IDFA - bool trackingEnabled = false; + bool trackingEnabled = false; #else - bool trackingEnabled = [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]; + bool trackingEnabled = + [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]; #endif - - if(self.personalizedPreferences == nil || !trackingEnabled) - return nil; - - NSDictionary* profile = [self.personalizedPreferences objectForKey:@"profile"]; - if(profile == nil){ - return nil; - } - - NSMutableDictionary* targetingInfo = [NSMutableDictionary dictionary]; - - for(NSString* key in profile) { - // ensure we don't include profile id - if([key isEqualToString:@"id"] == false){ - - NSDictionary* value = [profile objectForKey:key]; - if(value != nil && [value count] > 0){ - [targetingInfo setObject:[self generateString:value] forKey:key]; - } - } - } - - return targetingInfo; -} --(NSString*)generateString:(NSDictionary*)dict { - NSMutableArray* keywords = [NSMutableArray array]; - for(NSString* key in [dict allKeys]){ - NSString* val = [dict objectForKey:key]; - [keywords addObject:[NSString stringWithFormat:@"%@_%@", key, val]]; + if (self.personalizedPreferences == nil || !trackingEnabled) + return nil; + + NSDictionary *profile = + [self.personalizedPreferences objectForKey:@"profile"]; + if (profile == nil) { + return nil; + } + + NSMutableDictionary *targetingInfo = [NSMutableDictionary dictionary]; + + for (NSString *key in profile) { + // ensure we don't include profile id + if ([key isEqualToString:@"id"] == false) { + NSDictionary *value = [profile objectForKey:key]; + if (value != nil && [value count] > 0) { + [targetingInfo setObject:[self generateString:value] forKey:key]; + } } - - return [keywords componentsJoinedByString:@" "]; + } + + return targetingInfo; } +- (NSString *)generateString:(NSDictionary *)dict { + NSMutableArray *keywords = [NSMutableArray array]; + for (NSString *key in [dict allKeys]) { + NSString *val = [dict objectForKey:key]; + [keywords addObject:[NSString stringWithFormat:@"%@_%@", key, val]]; + } + + return [keywords componentsJoinedByString:@" "]; +} @end diff --git a/Pod/BVCommon/BVBaseAnalyticsHelper.h b/Pod/BVCommon/BVBaseAnalyticsHelper.h index 151a6e8a..7874b498 100644 --- a/Pod/BVCommon/BVBaseAnalyticsHelper.h +++ b/Pod/BVCommon/BVBaseAnalyticsHelper.h @@ -10,9 +10,9 @@ @interface BVBaseAnalyticsHelper : NSObject -+(NSDictionary*)getImpressionParams; -+(NSDictionary*)getPageViewParams; -+(NSDictionary*)getFeatureUsedParams; -+(NSDictionary*)getFeatureUsedInViewParams; ++ (NSDictionary *)getImpressionParams; ++ (NSDictionary *)getPageViewParams; ++ (NSDictionary *)getFeatureUsedParams; ++ (NSDictionary *)getFeatureUsedInViewParams; @end diff --git a/Pod/BVCommon/BVBaseAnalyticsHelper.m b/Pod/BVCommon/BVBaseAnalyticsHelper.m index 8fa4977d..f5a04d8e 100644 --- a/Pod/BVCommon/BVBaseAnalyticsHelper.m +++ b/Pod/BVCommon/BVBaseAnalyticsHelper.m @@ -10,37 +10,37 @@ @implementation BVBaseAnalyticsHelper -+(NSDictionary*)getImpressionParams { - return @{ - @"cl": @"Impression", - @"type": @"UGC", - @"source": @"native-mobile-sdk" - }; ++ (NSDictionary *)getImpressionParams { + return @{ + @"cl" : @"Impression", + @"type" : @"UGC", + @"source" : @"native-mobile-sdk" + }; } -+(NSDictionary*)getPageViewParams { - return @{ - @"cl": @"PageView", - @"type": @"Product", - @"source": @"native-mobile-sdk" - }; ++ (NSDictionary *)getPageViewParams { + return @{ + @"cl" : @"PageView", + @"type" : @"Product", + @"source" : @"native-mobile-sdk" + }; } -+(NSDictionary*)getFeatureUsedParams { - return @{ - @"cl": @"Feature", - @"type": @"Used", - @"source": @"native-mobile-sdk" - }; ++ (NSDictionary *)getFeatureUsedParams { + return @{ + @"cl" : @"Feature", + @"type" : @"Used", + @"source" : @"native-mobile-sdk" + }; } -+(NSDictionary*)getFeatureUsedInViewParams { - return @{ - @"cl": @"Feature", - @"type": @"Used", - @"name": @"InView", - @"source": @"native-mobile-sdk" - }; ++ (NSDictionary *)getFeatureUsedInViewParams { + return @{ + @"cl" : @"Feature", + @"type" : @"Used", + @"name" : @"InView", + @"source" : @"native-mobile-sdk" + }; } @end diff --git a/Pod/BVCommon/BVCore.h b/Pod/BVCommon/BVCore.h index 6ae1fecc..24e2ba94 100644 --- a/Pod/BVCommon/BVCore.h +++ b/Pod/BVCommon/BVCore.h @@ -8,12 +8,12 @@ #ifndef BVCore_h #define BVCore_h -#include "BVSDKManager.h" -#include "BVLogger.h" -#include "BVPixel.h" #include "BVAuthenticatedUser.h" -#include "BVNullHelper.h" #include "BVErrorCodeConstants.h" +#include "BVLogger.h" +#include "BVNullHelper.h" +#include "BVPixel.h" #include "BVSDKConstants.h" +#include "BVSDKManager.h" #endif diff --git a/Pod/BVCommon/BVDiagnosticHelpers.h b/Pod/BVCommon/BVDiagnosticHelpers.h index 59289b88..4fc85b29 100644 --- a/Pod/BVCommon/BVDiagnosticHelpers.h +++ b/Pod/BVCommon/BVDiagnosticHelpers.h @@ -9,7 +9,7 @@ @interface BVDiagnosticHelpers : NSObject -+(NSString*)releaseVersionNumber; -+(NSString*)buildVersionNumber; ++ (NSString *)releaseVersionNumber; ++ (NSString *)buildVersionNumber; @end diff --git a/Pod/BVCommon/BVDiagnosticHelpers.m b/Pod/BVCommon/BVDiagnosticHelpers.m index 4d8b53aa..addc5536 100644 --- a/Pod/BVCommon/BVDiagnosticHelpers.m +++ b/Pod/BVCommon/BVDiagnosticHelpers.m @@ -9,12 +9,14 @@ @implementation BVDiagnosticHelpers -+(NSString*)releaseVersionNumber { - return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; ++ (NSString *)releaseVersionNumber { + return [[[NSBundle mainBundle] infoDictionary] + objectForKey:@"CFBundleShortVersionString"]; } -+(NSString*)buildVersionNumber { - return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; ++ (NSString *)buildVersionNumber { + return + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; } @end diff --git a/Pod/BVCommon/BVDisplayableProductContent.h b/Pod/BVCommon/BVDisplayableProductContent.h index 8332e02f..940c2d20 100644 --- a/Pod/BVCommon/BVDisplayableProductContent.h +++ b/Pod/BVCommon/BVDisplayableProductContent.h @@ -5,11 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // +#import + @protocol BVDisplayableProductContent -@property (nonatomic, strong, readonly, nonnull) NSString *identifier; -@property (nonatomic, strong, readonly, nullable) NSString *displayName; -@property (nonatomic, strong, readonly, nullable) NSString *displayImageUrl; -@property (nonatomic, strong, readonly, nullable) NSNumber *averageRating; +@property(nonatomic, strong, readonly, nonnull) NSString *identifier; +@property(nonatomic, strong, readonly, nullable) NSString *displayName; +@property(nonatomic, strong, readonly, nullable) NSString *displayImageUrl; +@property(nonatomic, strong, readonly, nullable) NSNumber *averageRating; @end diff --git a/Pod/BVCommon/BVLogger.h b/Pod/BVCommon/BVLogger.h index 6bf41e0a..7ee726ba 100644 --- a/Pod/BVCommon/BVLogger.h +++ b/Pod/BVCommon/BVLogger.h @@ -7,69 +7,60 @@ #import - /// Values to set the log level for the shared BVLogger. typedef NS_ENUM(NSUInteger, BVLogLevel) { - - /// No logging. - BVLogLevelNone = 0, - - /// Log errors only. This is the default setting. - BVLogLevelError = 1, - - /// Logs errors and warnings only. - BVLogLevelWarning = 2, - - /// Logs errors, warnings, and info that may assist in tracing while debugging. - BVLogLevelInfo = 3, - - /// Logs all info, errors, and warnings, including all API invocations and responeses. - BVLogLevelVerbose = 4, - - /// Logs only condensed analytic event information - BVLogLevelAnalyticsOnly = 5 - -}; + /// No logging. + BVLogLevelNone = 0, -/// BVLogger is used for logging debug and informational messages from the SDK. -@interface BVLogger : NSObject + /// Log errors only. This is the default setting. + BVLogLevelError = 1, + /// Logs errors and warnings only. + BVLogLevelWarning = 2, -/// Singleton object. Use [BVLogger sharedLogger] whenever interacting with the logger. -+(nonnull BVLogger*)sharedLogger; + /// Logs errors, warnings, and info that may assist in tracing while + /// debugging. + BVLogLevelInfo = 3, + /// Logs all info, errors, and warnings, including all API invocations and + /// responeses. + BVLogLevelVerbose = 4, -/// Only messages of |logLevel| and below are logged. -@property (nonatomic, assign) BVLogLevel logLevel; + /// Logs only condensed analytic event information + BVLogLevelAnalyticsOnly = 5 +}; -/// Logs message with log level BVLogLevelVerbose. -- (void)verbose:(nonnull NSString*) message; +/// BVLogger is used for logging debug and informational messages from the SDK. +@interface BVLogger : NSObject +/// Singleton object. Use [BVLogger sharedLogger] whenever interacting with the +/// logger. ++ (nonnull BVLogger *)sharedLogger; -/// Logs message with log level BVLogLevelInfo. -- (void)info:(nonnull NSString*)message; +/// Only messages of |logLevel| and below are logged. +@property(nonatomic, assign) BVLogLevel logLevel; +/// Logs message with log level BVLogLevelVerbose. +- (void)verbose:(nonnull NSString *)message; -/// Logs message with log level BVLogLevelWarning. -- (void)warning:(nonnull NSString*)message; +/// Logs message with log level BVLogLevelInfo. +- (void)info:(nonnull NSString *)message; +/// Logs message with log level BVLogLevelWarning. +- (void)warning:(nonnull NSString *)message; /// Logs message with log level BVLogLevelError. -- (void)error:(nonnull NSString*)message; - +- (void)error:(nonnull NSString *)message; /// Logs all errors with log level BVLogLevelError. --(void)printError:(nonnull NSError*)error; - +- (void)printError:(nonnull NSError *)error; /// Logs all errors with log level BVLogLevelError. --(void)printErrors:(nonnull NSArray*)errors; - +- (void)printErrors:(nonnull NSArray *)errors; /// Logs messages specific to analytic events that this SDK fires internally --(void)analyticsMessage:(nonnull NSString*)message; - +- (void)analyticsMessage:(nonnull NSString *)message; @end diff --git a/Pod/BVCommon/BVLogger.m b/Pod/BVCommon/BVLogger.m index 2f8280fc..62e9baa4 100644 --- a/Pod/BVCommon/BVLogger.m +++ b/Pod/BVCommon/BVLogger.m @@ -13,60 +13,59 @@ @implementation BVLogger static BVLogger *sharedLoggerInstance = nil; -+ (BVLogger *) sharedLogger { - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedLoggerInstance = [[self alloc] init]; - }); - - return sharedLoggerInstance; ++ (BVLogger *)sharedLogger { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedLoggerInstance = [[self alloc] init]; + }); + + return sharedLoggerInstance; } --(id)init { - self = [super init]; - if(self){ - self.logLevel = BVLogLevelError; - } - return self; +- (id)init { + self = [super init]; + if (self) { + self.logLevel = BVLogLevelError; + } + return self; } --(void)analyticsMessage:(NSString*)message { - if(self.logLevel == BVLogLevelAnalyticsOnly) { - NSLog(@"%@: %@", BV_LOG_TAG, message); - } +- (void)analyticsMessage:(NSString *)message { + if (self.logLevel == BVLogLevelAnalyticsOnly) { + NSLog(@"%@: %@", BV_LOG_TAG, message); + } } --(void)verbose:(NSString*)message { - [self printMessage:message forLogLevel:BVLogLevelVerbose]; +- (void)verbose:(NSString *)message { + [self printMessage:message forLogLevel:BVLogLevelVerbose]; } --(void)info:(NSString*)message { - [self printMessage:message forLogLevel:BVLogLevelInfo]; +- (void)info:(NSString *)message { + [self printMessage:message forLogLevel:BVLogLevelInfo]; } --(void)warning:(NSString*)message { - [self printMessage:message forLogLevel:BVLogLevelWarning]; +- (void)warning:(NSString *)message { + [self printMessage:message forLogLevel:BVLogLevelWarning]; } --(void)error:(NSString*)message { - [self printMessage:message forLogLevel:BVLogLevelError]; +- (void)error:(NSString *)message { + [self printMessage:message forLogLevel:BVLogLevelError]; } --(void)printError:(nonnull NSError*)error { - [self printMessage:[error localizedDescription] forLogLevel:BVLogLevelError]; +- (void)printError:(nonnull NSError *)error { + [self printMessage:[error localizedDescription] forLogLevel:BVLogLevelError]; } --(void)printErrors:(nonnull NSArray*)errors { - for (NSError* error in errors) { - [self printError:error]; - } +- (void)printErrors:(nonnull NSArray *)errors { + for (NSError *error in errors) { + [self printError:error]; + } } --(void)printMessage:(NSString*)message forLogLevel:(BVLogLevel)logLevel { - if(self.logLevel >= logLevel && self.logLevel != BVLogLevelAnalyticsOnly){ - NSLog(@"%@: %@", BV_LOG_TAG, message); - } +- (void)printMessage:(NSString *)message forLogLevel:(BVLogLevel)logLevel { + if (self.logLevel >= logLevel && self.logLevel != BVLogLevelAnalyticsOnly) { + NSLog(@"%@: %@", BV_LOG_TAG, message); + } } @end diff --git a/Pod/BVCommon/BVMessageInterceptor.m b/Pod/BVCommon/BVMessageInterceptor.m index e30d347c..ae3a87bd 100644 --- a/Pod/BVCommon/BVMessageInterceptor.m +++ b/Pod/BVCommon/BVMessageInterceptor.m @@ -9,24 +9,32 @@ @implementation BVMessageInterceptor --(id)initWithMiddleman:(id)middleMan { - self = [super init]; - if(self) { - self.middleMan = middleMan; - } - return self; +- (id)initWithMiddleman:(id)middleMan { + self = [super init]; + if (self) { + self.middleMan = middleMan; + } + return self; } - (id)forwardingTargetForSelector:(SEL)aSelector { - if ([self.middleMan respondsToSelector:aSelector]) { return self.middleMan; } - if ([self.receiver respondsToSelector:aSelector]) { return self.receiver; } - return [super forwardingTargetForSelector:aSelector]; + if ([self.middleMan respondsToSelector:aSelector]) { + return self.middleMan; + } + if ([self.receiver respondsToSelector:aSelector]) { + return self.receiver; + } + return [super forwardingTargetForSelector:aSelector]; } - (BOOL)respondsToSelector:(SEL)aSelector { - if ([self.middleMan respondsToSelector:aSelector]) { return YES; } - if ([self.receiver respondsToSelector:aSelector]) { return YES; } - return [super respondsToSelector:aSelector]; + if ([self.middleMan respondsToSelector:aSelector]) { + return YES; + } + if ([self.receiver respondsToSelector:aSelector]) { + return YES; + } + return [super respondsToSelector:aSelector]; } -@end \ No newline at end of file +@end diff --git a/Pod/BVCommon/BVNullHelper.h b/Pod/BVCommon/BVNullHelper.h index 153349d4..6b7e87c7 100644 --- a/Pod/BVCommon/BVNullHelper.h +++ b/Pod/BVCommon/BVNullHelper.h @@ -8,25 +8,24 @@ #ifndef BVNullHelper_h #define BVNullHelper_h -#define SET_IF_NOT_NULL(target, value) \ - if(value != [NSNull null]) { \ - target = value; \ - } +#define SET_IF_NOT_NULL(target, value) \ + if (value != [NSNull null]) { \ + target = value; \ + } -#define SET_DEFAULT_IF_NULL(target, value, default) \ - if(value && value != [NSNull null]) { \ - target = value; \ - } else { \ - target = default; \ - } +#define SET_DEFAULT_IF_NULL(target, value, default) \ + if (value && value != [NSNull null]) { \ + target = value; \ + } else { \ + target = default; \ + } -static inline bool isObjectNilOrNull(NSObject *object){ - - if (object == nil || object == [NSNull null]){ - return true; - } else { - return false; - } +static inline bool isObjectNilOrNull(NSObject *object) { + if (object == nil || object == [NSNull null]) { + return true; + } else { + return false; + } } #endif /* BVNullHelper_h */ diff --git a/Pod/BVCommon/BVSDKConfiguration.m b/Pod/BVCommon/BVSDKConfiguration.m index d9b487bf..ad6c65bb 100644 --- a/Pod/BVCommon/BVSDKConfiguration.m +++ b/Pod/BVCommon/BVSDKConfiguration.m @@ -8,19 +8,23 @@ #import "BVSDKConfiguration.h" @implementation BVSDKConfiguration --(instancetype _Nonnull)initWithDictionary:(NSDictionary * _Nonnull)dict configType:(BVConfigurationType)type { - if (self = [super init]) { - - _staging = type == BVConfigurationTypeStaging; - for(NSString *key in dict) { - if ([self respondsToSelector:NSSelectorFromString(key)]) { - [self setValue:dict[key] forKeyPath:key]; - }else { - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"Unrecognized configuration option \"%@\" will be ignored", key]]; - } - } +- (nonnull instancetype)initWithDictionary:(nonnull NSDictionary *)dict + configType:(BVConfigurationType)type { + if (self = [super init]) { + _staging = type == BVConfigurationTypeStaging; + for (NSString *key in dict) { + if ([self respondsToSelector:NSSelectorFromString(key)]) { + [self setValue:dict[key] forKeyPath:key]; + } else { + [[BVLogger sharedLogger] + error:[NSString stringWithFormat:@"Unrecognized " + @"configuration option " + @"\"%@\" will be ignored", + key]]; + } } - return self; + } + return self; } @end diff --git a/Pod/BVCommon/BVSDKConstants.h b/Pod/BVCommon/BVSDKConstants.h index fb4835c1..db1e3cf0 100644 --- a/Pod/BVCommon/BVSDKConstants.h +++ b/Pod/BVCommon/BVSDKConstants.h @@ -19,8 +19,16 @@ /// Error domain for NSError results, when present. #define BVErrDomain @"com.bazaarvoice.bvsdk" -#define SYSTEM_VERSION_IOS_10 ([[[UIDevice currentDevice] systemVersion] compare:@"10.0" options:NSNumericSearch] != NSOrderedAscending) +#define SYSTEM_VERSION_IOS_10 \ + ([[[UIDevice currentDevice] systemVersion] \ + compare:@"10.0" \ + options:NSNumericSearch] != NSOrderedAscending) -#define LOG_DEPRECATED_MESSAGE(message) ([[BVLogger sharedLogger] warning:[NSString stringWithFormat:@"%@#%@ is deprecated and will be removed in a future release", NSStringFromClass([self class]), message]]); +#define LOG_DEPRECATED_MESSAGE(message) \ + ([[BVLogger sharedLogger] \ + warning:[NSString stringWithFormat:@"%@#%@ is deprecated and will be " \ + @"removed in a future release", \ + NSStringFromClass([self class]), \ + message]]); #endif /* BVSDKConstants_h */ diff --git a/Pod/BVCommon/BVSDKManager.h b/Pod/BVCommon/BVSDKManager.h index 7443dab3..2223f462 100644 --- a/Pod/BVCommon/BVSDKManager.h +++ b/Pod/BVCommon/BVSDKManager.h @@ -5,123 +5,168 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import +#import "BVAuthenticatedUser.h" #import "BVCore.h" #import "BVLogger.h" -#import "BVAuthenticatedUser.h" +#import typedef NS_ENUM(NSUInteger, BVConfigurationType) { - BVConfigurationTypeProd, - BVConfigurationTypeStaging + BVConfigurationTypeProd, + BVConfigurationTypeStaging }; -NS_ASSUME_NONNULL_BEGIN - -// For internal use of notifying the BVLocation module when the SDK has been intialized. +// For internal use of notifying the BVLocation module when the SDK has been +// intialized. #define LOCATION_API_KEY_SET_NOTIFICATION @"locationAPIKeyReady" -// For intenal use of notifying with the Conversations Store api key has been initialized. -#define CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION @"conversationsStoreAPIKeyReady" +// For intenal use of notifying with the Conversations Store api key has been +// initialized. +#define CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION \ + @"conversationsStoreAPIKeyReady" -// For intenal use of notifying with the BVPIN (Post Interaction Notification) module has been initialized. +// For intenal use of notifying with the BVPIN (Post Interaction Notification) +// module has been initialized. #define PIN_API_KEY_SET_NOTIFICATION @"pinAPIKeyReady" /*! - The singleton instance for registering your API key and server to use. Any use of the BVRecommendations API must start here! - + The singleton instance for registering your API key and server to use. Any + use of the BVRecommendations API must start here! + ##Code Examples: Initializing the SDK Manager - + ### Objective-C - + @code - // Will search app bundle for file bvsdk_config_staging.json for BVConfigurationTypeStaging or - // bvsdk_config_prod.json for BVConfigurationTypeProd. BVSDK will be configured using this file + // Will search app bundle for file bvsdk_config_staging.json for + BVConfigurationTypeStaging or + // bvsdk_config_prod.json for BVConfigurationTypeProd. BVSDK will be + configured using this file [BVSDKManager configure:BVConfigurationTypeStaging]; @endcode - + ### Swift - + @code - // Will search app bundle for file bvsdk_config_staging.json for BVConfigurationTypeStaging or - // bvsdk_config_prod.json for BVConfigurationTypeProd. BVSDK will be configured using this file + // Will search app bundle for file bvsdk_config_staging.json for + BVConfigurationTypeStaging or + // bvsdk_config_prod.json for BVConfigurationTypeProd. BVSDK will be + configured using this file BVSDKManager.configure(.staging) @endcode - + */ @interface BVSDKManager : NSObject -+(void)configure:(BVConfigurationType)configurationType; ++ (void)configure:(BVConfigurationType)configurationType; -+(void)configureWithConfiguration:(NSDictionary * _Nonnull)configDict configType:(BVConfigurationType)configType; ++ (void)configureWithConfiguration:(nonnull NSDictionary *)configDict + configType:(BVConfigurationType)configType; /// Singleton pattern. Use this `sharedManager` whenever interacting with BVSDK. -+(instancetype)sharedManager; ++ (nonnull instancetype)sharedManager; -- (id) init __attribute__((unavailable("Must use sharedManager for the BVSDKManager"))); +- (nonnull id)init + __attribute__((unavailable("Must use sharedManager for the BVSDKManager"))); -/// Set the log level for getting log and event info. Default is BVLogLevel.kBVLogLevelError --(void)setLogLevel:(BVLogLevel)logLevel; +/// Set the log level for getting log and event info. Default is +/// BVLogLevel.kBVLogLevelError +- (void)setLogLevel:(BVLogLevel)logLevel; -/// Read-only value of urlRoot for Shopper Advertising APIs. Varies depending on value of staging. -@property (nonatomic, readonly) NSString *urlRootShopperAdvertising; +/// Read-only value of urlRoot for Shopper Advertising APIs. Varies depending on +/// value of staging. +@property(nonnull, nonatomic, readonly) NSString *urlRootShopperAdvertising; /// Client ID associated with the API key -@property (nonatomic, strong) NSString *clientId __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *clientId + __attribute__((deprecated( + "Use BVSDKManager#configure:(BVConfigurationType)configurationType " + "instead."))); -/// Boolean indicating whether this request should go to staging (true) or production (false). Default is production (false). -@property (nonatomic, assign) BOOL staging __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +/// Boolean indicating whether this request should go to staging (true) or +/// production (false). Default is production (false). +@property(nonatomic, assign) BOOL staging __attribute__((deprecated( + "Use BVSDKManager#configure:(BVConfigurationType)configurationType " + "instead."))); /// Your private API key for the BVConversations product -@property (nonatomic, strong) NSString *apiKeyConversations __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *apiKeyConversations + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /// Your private API key for the BVConversations, Store Reviews product -@property (nonatomic, strong) NSString *apiKeyConversationsStores __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *apiKeyConversationsStores + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /// Your private API key for Post Interaction Notifications -@property (nonatomic, strong) NSString *apiKeyPIN __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); - -/// The category of the Notification Content Extension you will use for store review notifications -@property (nonatomic, strong) NSString *storeReviewContentExtensionCategory __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *apiKeyPIN + __attribute__((deprecated( + "Use BVSDKManager#configure:(BVConfigurationType)configurationType " + "instead."))); + +/// The category of the Notification Content Extension you will use for store +/// review notifications +@property(nonnull, nonatomic, strong) + NSString *storeReviewContentExtensionCategory + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /// The category of the Notification Content Extension you will use for PIN -@property (nonatomic, strong) NSString *PINContentExtensionCategory __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); - -/// Your private API key for the BVRecommendations and BVAdvertising products (Shopper Advertising) -@property (nonatomic, strong) NSString *apiKeyShopperAdvertising __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *PINContentExtensionCategory + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); + +/// Your private API key for the BVRecommendations and BVAdvertising products +/// (Shopper Advertising) +@property(nonnull, nonatomic, strong) NSString *apiKeyShopperAdvertising + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /// Your private API key for the BVCurations API -@property (nonatomic, strong) NSString *apiKeyCurations __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *apiKeyCurations + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /// Your private API key for the BVLocations API -@property (nonatomic, strong) NSString *apiKeyLocation __attribute__((deprecated("Use BVSDKManager#configure:(BVConfigurationType)configurationType instead."))); +@property(nonnull, nonatomic, strong) NSString *apiKeyLocation + __attribute__((deprecated("Use " + "BVSDKManager#configure:(BVConfigurationType)" + "configurationType instead."))); /** - Set user information. Associates a user profile with device for taylored advertising and recommendations. - Use of this method requires that a valid key has been set for apiKeyShopperMarketing. - - @param userAuthString The UAS that was generated server-side for Bazaarvoice. - */ --(void)setUserWithAuthString:(NSString*)userAuthString; - + Set user information. Associates a user profile with device for taylored + advertising and recommendations. + Use of this method requires that a valid key has been set for + apiKeyShopperMarketing. -/// The authenticed user retrieved after calling setUserWithAuthString. The model may be empty until the BV user profile has been reconcilled. -@property (strong, readonly) BVAuthenticatedUser *bvUser; + @param userAuthString The UAS that was generated server-side for + Bazaarvoice. + */ +- (void)setUserWithAuthString:(nonnull NSString *)userAuthString; +/// The authenticed user retrieved after calling setUserWithAuthString. The +/// model may be empty until the BV user profile has been reconcilled. +@property(nonnull, strong, readonly) BVAuthenticatedUser *bvUser; /** Generate DFP (Doubleclick For Publsher's) compatible custom targeting. - + @code DFPRequest* request = [[DFPRequest request] request]; - request.customerTargeting = [[BVSDKManager sharedManager] getTargetingKeywords]; + request.customerTargeting = [[BVSDKManager sharedManager] + getTargetingKeywords]; @endcode */ --(NSDictionary*)getCustomTargeting; - - -/// Network timeout in seconds. Default is 60 seconds. Note that iOS may set a minimum timeout of 240 seconds for post requests. -@property (nonatomic, assign) float timeout; +- (nonnull NSDictionary *)getCustomTargeting; -NS_ASSUME_NONNULL_END +/// Network timeout in seconds. Default is 60 seconds. Note that iOS may set a +/// minimum timeout of 240 seconds for post requests. +@property(nonatomic, assign) float timeout; @end diff --git a/Pod/BVCommon/BVSDKManager.m b/Pod/BVCommon/BVSDKManager.m index c970ce6e..0f0e0a1a 100644 --- a/Pod/BVCommon/BVSDKManager.m +++ b/Pod/BVCommon/BVSDKManager.m @@ -6,242 +6,289 @@ // #import "BVSDKManager.h" -#import "BVCore.h" -#import "BVAnalyticsManager.h" #import "BVAnalyticEventManager.h" -#import +#import "BVAnalyticsManager.h" +#import "BVCore.h" #import "BVSDKConfiguration.h" +#import @interface BVSDKManager () -@property (nonatomic, strong) BVSDKConfiguration* configuration; +@property(nonatomic, strong) BVSDKConfiguration *configuration; @end @implementation BVSDKManager -static NSString* const BVSDKConfigFileNameProd = @"bvsdk_config_prod"; -static NSString* const BVSDKConfigFileNameStaging = @"bvsdk_config_staging"; -static NSString* const BVSDKConfigFileExt = @"json"; - -+(void)configure:(BVConfigurationType)configurationType { - [[self sharedManager] attemptToLoadConfiguration: configurationType]; -} - -+(void)configureWithConfiguration:(NSDictionary *)configDict configType:(BVConfigurationType)configType { - [[self sharedManager] configureWithDictionary:configDict configType:configType]; -} - -+(instancetype)sharedManager -{ - // structure used to test whether the block has completed or not - static dispatch_once_t p = 0; - - // initialize sharedObject as nil (first call only) - __strong static id _sharedObject = nil; - - // executes a block object once and only once for the lifetime of an application - dispatch_once(&p, ^{ - _sharedObject = [[self alloc] init]; - }); - - // returns the same object each time - return _sharedObject; -} - --(id)init { - self = [super init]; - if(self){ - - _bvUser = [[BVAuthenticatedUser alloc] init]; - - // make sure analytics has been started - [BVAnalyticsManager sharedManager]; - - _timeout = 60; - _staging = NO; - _clientId = nil; - _apiKeyConversations = nil; - _apiKeyShopperAdvertising = nil; - _apiKeyConversationsStores = nil; - _apiKeyLocation = nil; - _configuration = [[BVSDKConfiguration alloc] init]; - } - return self; +static NSString *const BVSDKConfigFileNameProd = @"bvsdk_config_prod"; +static NSString *const BVSDKConfigFileNameStaging = @"bvsdk_config_staging"; +static NSString *const BVSDKConfigFileExt = @"json"; + ++ (void)configure:(BVConfigurationType)configurationType { + [[self sharedManager] attemptToLoadConfiguration:configurationType]; } --(void)attemptToLoadConfiguration:(BVConfigurationType)configType { - - NSString *fileName; - if (configType == BVConfigurationTypeProd) { - fileName = BVSDKConfigFileNameProd; - }else { - fileName = BVSDKConfigFileNameStaging; - } - - NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:BVSDKConfigFileExt]; - NSAssert(filePath, @"File %@.%@ could not be found in bundle. Unable to configure bundle", fileName, BVSDKConfigFileExt); - - if (filePath) { - NSData *data = [NSData dataWithContentsOfFile:filePath]; - NSError *error; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&error]; - [self configureWithDictionary:json configType:configType]; - } - - [self assertConfiguration]; ++ (void)configureWithConfiguration:(NSDictionary *)configDict + configType:(BVConfigurationType)configType { + [[self sharedManager] configureWithDictionary:configDict + configType:configType]; +} + ++ (instancetype)sharedManager { + // structure used to test whether the block has completed or not + static dispatch_once_t p = 0; + + // initialize sharedObject as nil (first call only) + __strong static id _sharedObject = nil; + + // executes a block object once and only once for the lifetime of an + // application + dispatch_once(&p, ^{ + _sharedObject = [[self alloc] init]; + }); + + // returns the same object each time + return _sharedObject; +} + +- (id)init { + self = [super init]; + if (self) { + _bvUser = [[BVAuthenticatedUser alloc] init]; + + // make sure analytics has been started + [BVAnalyticsManager sharedManager]; + + _timeout = 60; + _staging = NO; + _clientId = nil; + _apiKeyConversations = nil; + _apiKeyShopperAdvertising = nil; + _apiKeyConversationsStores = nil; + _apiKeyLocation = nil; + _configuration = [[BVSDKConfiguration alloc] init]; + } + return self; } --(void)configureWithDictionary:(NSDictionary *)dict configType:(BVConfigurationType)configType{ - NSMutableDictionary *config = dict.mutableCopy; - [config setObject:@(configType == BVConfigurationTypeStaging) forKey:@"staging"]; - _configuration = [[BVSDKConfiguration alloc] initWithDictionary:dict configType: configType]; - [BVAnalyticsManager sharedManager].isDryRunAnalytics = _configuration.dryRunAnalytics; - [self copyJson:config toObj:self]; +- (void)attemptToLoadConfiguration:(BVConfigurationType)configType { + NSString *fileName; + if (configType == BVConfigurationTypeProd) { + fileName = BVSDKConfigFileNameProd; + } else { + fileName = BVSDKConfigFileNameStaging; + } + + NSString *filePath = + [[NSBundle mainBundle] pathForResource:fileName + ofType:BVSDKConfigFileExt]; + NSAssert( + filePath, + @"File %@.%@ could not be found in bundle. Unable to configure bundle", + fileName, BVSDKConfigFileExt); + + if (filePath) { + NSData *data = [NSData dataWithContentsOfFile:filePath]; + NSError *error; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&error]; + [self configureWithDictionary:json configType:configType]; + } + + [self assertConfiguration]; } --(void)setConfiguration:(BVSDKConfiguration * _Nonnull)configuration { - _configuration = configuration; +- (void)configureWithDictionary:(NSDictionary *)dict + configType:(BVConfigurationType)configType { + NSMutableDictionary *config = dict.mutableCopy; + [config setObject:@(configType == BVConfigurationTypeStaging) + forKey:@"staging"]; + _configuration = [[BVSDKConfiguration alloc] initWithDictionary:dict + configType:configType]; + [BVAnalyticsManager sharedManager].isDryRunAnalytics = + _configuration.dryRunAnalytics; + [self copyJson:config toObj:self]; } --(void)copyJson:(NSDictionary*)json toObj:(NSObject*)obj { - for(NSString *key in json.allKeys) { - if ([obj respondsToSelector:NSSelectorFromString(key)]) { - [obj setValue:json[key] forKeyPath:key]; - } +- (void)setConfiguration:(nonnull BVSDKConfiguration *)configuration { + _configuration = configuration; +} + +- (void)copyJson:(NSDictionary *)json toObj:(NSObject *)obj { + for (NSString *key in json.allKeys) { + if ([obj respondsToSelector:NSSelectorFromString(key)]) { + [obj setValue:json[key] forKeyPath:key]; } + } } -- (NSString *)description{ - - NSString *returnValue = [NSString stringWithFormat:@"Setting Values:\n conversations API key = %@ \n shopper marketing API key = %@ \n conversations for stores API key = %@ \n BVSDK Version = %@ \n clientId = %@ \n staging = %i \n" , self.configuration.apiKeyConversations, self.configuration.apiKeyShopperAdvertising, self.configuration.apiKeyConversationsStores, BV_SDK_VERSION, self.configuration.clientId, self.configuration.staging]; - - return returnValue; - +- (NSString *)description { + NSString *returnValue = [NSString + stringWithFormat:@"Setting Values:\n conversations API key = %@ \n " + @"shopper marketing API key = %@ \n conversations " + @"for stores API key = %@ \n BVSDK Version = %@ \n " + @"clientId = %@ \n staging = %i \n", + self.configuration.apiKeyConversations, + self.configuration.apiKeyShopperAdvertising, + self.configuration.apiKeyConversationsStores, + BV_SDK_VERSION, self.configuration.clientId, + self.configuration.staging]; + + return returnValue; } -- (NSString *)urlRootShopperAdvertising{ - return self.configuration.staging ? @"https://my.network-stg.bazaarvoice.com" : @"https://my.network.bazaarvoice.com"; +- (NSString *)urlRootShopperAdvertising { + return self.configuration.staging ? @"https://my.network-stg.bazaarvoice.com" + : @"https://my.network.bazaarvoice.com"; } // SDK supports only a single client ID --(void)setClientId:(NSString *)clientId{ - _clientId = clientId; - [BVAnalyticEventManager sharedManager].clientId = clientId; - [BVAnalyticEventManager sharedManager].eventSource = @"native-mobile-sdk"; - [self assertConfiguration]; - [_configuration setValue:clientId forKeyPath:@"clientId"]; +- (void)setClientId:(NSString *)clientId { + _clientId = clientId; + [BVAnalyticEventManager sharedManager].clientId = clientId; + [BVAnalyticEventManager sharedManager].eventSource = @"native-mobile-sdk"; + [self assertConfiguration]; + [_configuration setValue:clientId forKeyPath:@"clientId"]; } --(void)assertConfiguration { - NSAssert(_clientId.length, @"You must supply valid client id in the BVSDKManager before using the Bazaarvoice SDK."); +- (void)assertConfiguration { + NSAssert(_clientId.length, @"You must supply valid client id in the " + @"BVSDKManager before using the Bazaarvoice " + @"SDK."); } // SDK supports only a single setting for production or stage -- (void)setStaging:(BOOL)staging{ - _staging = staging; - [BVAnalyticsManager sharedManager].isStagingServer = staging; - [_configuration setValue:@(staging) forKeyPath:@"staging"]; +- (void)setStaging:(BOOL)staging { + _staging = staging; + [BVAnalyticsManager sharedManager].isStagingServer = staging; + [_configuration setValue:@(staging) forKeyPath:@"staging"]; } --(void)setApiKeyShopperAdvertising:(NSString *)apiKeyShopperAdvertising{ - _apiKeyShopperAdvertising = apiKeyShopperAdvertising; - [_configuration setValue:apiKeyShopperAdvertising forKeyPath:@"apiKeyShopperAdvertising"]; +- (void)setApiKeyShopperAdvertising:(NSString *)apiKeyShopperAdvertising { + _apiKeyShopperAdvertising = apiKeyShopperAdvertising; + [_configuration setValue:apiKeyShopperAdvertising + forKeyPath:@"apiKeyShopperAdvertising"]; } --(void)setApiKeyConversations:(NSString *)apiKeyConversations{ - _apiKeyConversations = apiKeyConversations; - [_configuration setValue:apiKeyConversations forKeyPath:@"apiKeyConversations"]; +- (void)setApiKeyConversations:(NSString *)apiKeyConversations { + _apiKeyConversations = apiKeyConversations; + [_configuration setValue:apiKeyConversations + forKeyPath:@"apiKeyConversations"]; } -- (void)setApiKeyConversationsStores:(NSString *)apiKeyConversationsStores{ - - _apiKeyConversationsStores = apiKeyConversationsStores; - NSDictionary *userInfo = @{CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION:_apiKeyConversationsStores}; - [[NSNotificationCenter defaultCenter] postNotificationName:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION object:nil userInfo:userInfo]; - [_configuration setValue:apiKeyConversationsStores forKeyPath:@"apiKeyConversationsStores"]; +- (void)setApiKeyConversationsStores:(NSString *)apiKeyConversationsStores { + _apiKeyConversationsStores = apiKeyConversationsStores; + NSDictionary *userInfo = @{ + CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION : _apiKeyConversationsStores + }; + [[NSNotificationCenter defaultCenter] + postNotificationName:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION + object:nil + userInfo:userInfo]; + [_configuration setValue:apiKeyConversationsStores + forKeyPath:@"apiKeyConversationsStores"]; } -- (void)setApiKeyLocation:(NSString *)apiKeyLocation{ - - _apiKeyLocation = apiKeyLocation; - NSDictionary *userInfo = @{LOCATION_API_KEY_SET_NOTIFICATION:_apiKeyLocation}; - [[NSNotificationCenter defaultCenter] postNotificationName:LOCATION_API_KEY_SET_NOTIFICATION object:nil userInfo:userInfo]; - [_configuration setValue:apiKeyLocation forKeyPath:@"apiKeyLocation"]; +- (void)setApiKeyLocation:(NSString *)apiKeyLocation { + _apiKeyLocation = apiKeyLocation; + NSDictionary *userInfo = + @{LOCATION_API_KEY_SET_NOTIFICATION : _apiKeyLocation}; + [[NSNotificationCenter defaultCenter] + postNotificationName:LOCATION_API_KEY_SET_NOTIFICATION + object:nil + userInfo:userInfo]; + [_configuration setValue:apiKeyLocation forKeyPath:@"apiKeyLocation"]; } --(void)setApiKeyPIN:(NSString *)apiKeyPIN { - - _apiKeyPIN = apiKeyPIN; - NSDictionary *userInfo = @{PIN_API_KEY_SET_NOTIFICATION:_apiKeyPIN}; - [[NSNotificationCenter defaultCenter] postNotificationName:PIN_API_KEY_SET_NOTIFICATION object:nil userInfo:userInfo]; - [_configuration setValue:apiKeyPIN forKeyPath:@"apiKeyPIN"]; +- (void)setApiKeyPIN:(NSString *)apiKeyPIN { + _apiKeyPIN = apiKeyPIN; + NSDictionary *userInfo = @{PIN_API_KEY_SET_NOTIFICATION : _apiKeyPIN}; + [[NSNotificationCenter defaultCenter] + postNotificationName:PIN_API_KEY_SET_NOTIFICATION + object:nil + userInfo:userInfo]; + [_configuration setValue:apiKeyPIN forKeyPath:@"apiKeyPIN"]; } --(void)setStoreReviewContentExtensionCategory:(NSString *)storeReviewContentExtensionCategory { - _storeReviewContentExtensionCategory = storeReviewContentExtensionCategory; - [_configuration setValue:storeReviewContentExtensionCategory forKeyPath:@"storeReviewContentExtensionCategory"]; +- (void)setStoreReviewContentExtensionCategory: + (NSString *)storeReviewContentExtensionCategory { + _storeReviewContentExtensionCategory = storeReviewContentExtensionCategory; + [_configuration setValue:storeReviewContentExtensionCategory + forKeyPath:@"storeReviewContentExtensionCategory"]; } --(void)setPINContentExtensionCategory:(NSString *)PINContentExtensionCategory { - _PINContentExtensionCategory = PINContentExtensionCategory; - [_configuration setValue:PINContentExtensionCategory forKeyPath:@"PINContentExtensionCategory"]; +- (void)setPINContentExtensionCategory:(NSString *)PINContentExtensionCategory { + _PINContentExtensionCategory = PINContentExtensionCategory; + [_configuration setValue:PINContentExtensionCategory + forKeyPath:@"PINContentExtensionCategory"]; } --(void)setApiKeyCurations:(NSString *)apiKeyCurations { - _apiKeyCurations = apiKeyCurations; - [_configuration setValue:apiKeyCurations forKeyPath:@"apiKeyCurations"]; +- (void)setApiKeyCurations:(NSString *)apiKeyCurations { + _apiKeyCurations = apiKeyCurations; + [_configuration setValue:apiKeyCurations forKeyPath:@"apiKeyCurations"]; } --(void)setLogLevel:(BVLogLevel)logLevel { - [[BVLogger sharedLogger] setLogLevel:logLevel]; +- (void)setLogLevel:(BVLogLevel)logLevel { + [[BVLogger sharedLogger] setLogLevel:logLevel]; } #pragma mark - user --(void)setUserWithAuthString:(NSString*)userAuthString { - - if(!userAuthString.length){ - [[BVLogger sharedLogger] error:@"No userAuthString was supplied for the recommendations manager!"]; - return; - } - - [self setUserId:userAuthString]; +- (void)setUserWithAuthString:(NSString *)userAuthString { + if (!userAuthString.length) { + [[BVLogger sharedLogger] error:@"No userAuthString was supplied for " + @"the recommendations manager!"]; + return; + } + + [self setUserId:userAuthString]; } -// Update the user profile by calling the /users/ endpoint if the targeting params are empty. --(void)updateUserProfileIfEmpty { - [self.bvUser updateProfile:false withAPIKey:self.configuration.apiKeyShopperAdvertising isStaging:self.configuration.staging]; +// Update the user profile by calling the /users/ endpoint if the targeting +// params are empty. +- (void)updateUserProfileIfEmpty { + [self.bvUser updateProfile:false + withAPIKey:self.configuration.apiKeyShopperAdvertising + isStaging:self.configuration.staging]; } -// Udpate the user profile by calling the /users/ endpoint, regardless of the current state of the targeting params --(void)updateUserProfileForce { - [self.bvUser updateProfile:true withAPIKey:self.configuration.apiKeyShopperAdvertising isStaging:self.configuration.staging]; +// Udpate the user profile by calling the /users/ endpoint, regardless of the +// current state of the targeting params +- (void)updateUserProfileForce { + [self.bvUser updateProfile:true + withAPIKey:self.configuration.apiKeyShopperAdvertising + isStaging:self.configuration.staging]; } --(void)setUserId:(NSString*)userAuthString{ - - self.bvUser.userAuthString = userAuthString; - - BVPersonalizationEvent *personEvent = [[BVPersonalizationEvent alloc] initWithUserAuthenticationString:userAuthString]; - [BVPixel trackEvent:personEvent]; - - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - // try to grab the profile as soon as its available - [self updateUserProfileForce]; - [self performSelector:@selector(updateUserProfileIfEmpty) withObject:self afterDelay:5.0]; - [self performSelector:@selector(updateUserProfileIfEmpty) withObject:self afterDelay:12.0]; - [self performSelector:@selector(updateUserProfileIfEmpty) withObject:self afterDelay:24.0]; - +- (void)setUserId:(NSString *)userAuthString { + self.bvUser.userAuthString = userAuthString; + + BVPersonalizationEvent *personEvent = [[BVPersonalizationEvent alloc] + initWithUserAuthenticationString:userAuthString]; + [BVPixel trackEvent:personEvent]; + + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + // try to grab the profile as soon as its available + [self updateUserProfileForce]; + [self performSelector:@selector(updateUserProfileIfEmpty) + withObject:self + afterDelay:5.0]; + [self performSelector:@selector(updateUserProfileIfEmpty) + withObject:self + afterDelay:12.0]; + [self performSelector:@selector(updateUserProfileIfEmpty) + withObject:self + afterDelay:24.0]; } -- (BVAuthenticatedUser *)getBVAuthenticatedUser{ - return self.bvUser; +- (BVAuthenticatedUser *)getBVAuthenticatedUser { + return self.bvUser; } --(NSDictionary*)getCustomTargeting { - NSAssert(_apiKeyShopperAdvertising.length, @"You must supply apiKeyShopperAdvertising in the BVSDKManager before using BVAdvertising."); - - return [self.bvUser getTargetingKeywords]; +- (NSDictionary *)getCustomTargeting { + NSAssert(_apiKeyShopperAdvertising.length, + @"You must supply apiKeyShopperAdvertising in the BVSDKManager " + @"before using BVAdvertising."); + + return [self.bvUser getTargetingKeywords]; } @end diff --git a/Pod/BVCommon/BVStringKeyValuePair.h b/Pod/BVCommon/BVStringKeyValuePair.h index cf9ca4b0..36255d06 100644 --- a/Pod/BVCommon/BVStringKeyValuePair.h +++ b/Pod/BVCommon/BVStringKeyValuePair.h @@ -9,8 +9,9 @@ @interface BVStringKeyValuePair : NSObject -@property NSString* _Nonnull key; -@property NSString* _Nullable value; -+(instancetype _Nonnull)pairWithKey:(NSString* _Nonnull)key value:(NSString* _Nullable)value; +@property(nonnull) NSString *key; +@property(nullable) NSString *value; ++ (nonnull instancetype)pairWithKey:(nonnull NSString *)key + value:(nullable NSString *)value; @end diff --git a/Pod/BVCommon/BVStringKeyValuePair.m b/Pod/BVCommon/BVStringKeyValuePair.m index 72c81146..e3b5163c 100644 --- a/Pod/BVCommon/BVStringKeyValuePair.m +++ b/Pod/BVCommon/BVStringKeyValuePair.m @@ -9,11 +9,12 @@ @implementation BVStringKeyValuePair -+(instancetype _Nonnull)pairWithKey:(NSString* _Nonnull)key value:(NSString* _Nullable)value { - BVStringKeyValuePair* pair = [[BVStringKeyValuePair alloc] init]; - pair.key = key; - pair.value = value; - return pair; ++ (nonnull instancetype)pairWithKey:(nonnull NSString *)key + value:(nullable NSString *)value { + BVStringKeyValuePair *pair = [[BVStringKeyValuePair alloc] init]; + pair.key = key; + pair.value = value; + return pair; } @end diff --git a/Pod/BVCommon/BVViewsHelper.h b/Pod/BVCommon/BVViewsHelper.h index 98544aa3..affd0534 100644 --- a/Pod/BVCommon/BVViewsHelper.h +++ b/Pod/BVCommon/BVViewsHelper.h @@ -10,10 +10,12 @@ @interface BVViewsHelper : NSObject -+ (void)checkButtonsInSubviews:(NSArray*)subviews withTarget:(id)target withSelector:(SEL)targetSelector; ++ (void)checkButtonsInSubviews:(NSArray *)subviews + withTarget:(id)target + withSelector:(SEL)targetSelector; + (void)checkGestureRecognizers:(NSArray *)gestureRecognizers; -+(NSString*)formatIndex:(NSIndexPath*)indexPath; ++ (NSString *)formatIndex:(NSIndexPath *)indexPath; @end diff --git a/Pod/BVCommon/BVViewsHelper.m b/Pod/BVCommon/BVViewsHelper.m index 5215515d..07ea7fa3 100644 --- a/Pod/BVCommon/BVViewsHelper.m +++ b/Pod/BVCommon/BVViewsHelper.m @@ -9,40 +9,40 @@ @implementation BVViewsHelper -+ (void)checkButtonsInSubviews:(NSArray*)subviews withTarget:(id)target withSelector:(SEL)targetSelector { - - for(UIView* subview in subviews) { - if([subview isKindOfClass:[UIButton class]]) { - UIButton* button = (UIButton*)subview; - [button addTarget:self action:@selector(selector) forControlEvents:UIControlEventTouchUpInside]; - - } - [self checkButtonsInSubviews:subview.subviews withTarget:target withSelector:targetSelector]; ++ (void)checkButtonsInSubviews:(NSArray *)subviews + withTarget:(id)target + withSelector:(SEL)targetSelector { + for (UIView *subview in subviews) { + if ([subview isKindOfClass:[UIButton class]]) { + UIButton *button = (UIButton *)subview; + [button addTarget:self + action:@selector(selector) + forControlEvents:UIControlEventTouchUpInside]; } - + [self checkButtonsInSubviews:subview.subviews + withTarget:target + withSelector:targetSelector]; + } } - + (void)checkGestureRecognizers:(NSArray *)gestureRecognizers { - - for(UIGestureRecognizer* recognizer in gestureRecognizers) { - if([recognizer isKindOfClass:[UIGestureRecognizer class]]){ - UIGestureRecognizer* tapRecognizer = (UIGestureRecognizer*)recognizer; - - if([tapRecognizer cancelsTouchesInView]){ - - NSAssert(false, @"UIGestureRecognizer must have `cancelsTouchesInView` set to false for the BVSDK to properly function."); - - } - } + for (UIGestureRecognizer *recognizer in gestureRecognizers) { + if ([recognizer isKindOfClass:[UIGestureRecognizer class]]) { + UIGestureRecognizer *tapRecognizer = (UIGestureRecognizer *)recognizer; + + if ([tapRecognizer cancelsTouchesInView]) { + NSAssert(false, @"UIGestureRecognizer must have " + @"`cancelsTouchesInView` set to false for the " + @"BVSDK to properly function."); + } } - + } } - - -+(NSString*)formatIndex:(NSIndexPath*)indexPath { - return [NSString stringWithFormat:@"%lu:%lu", (unsigned long)indexPath.section, (unsigned long)indexPath.row]; ++ (NSString *)formatIndex:(NSIndexPath *)indexPath { + return + [NSString stringWithFormat:@"%lu:%lu", (unsigned long)indexPath.section, + (unsigned long)indexPath.row]; } @end diff --git a/Pod/BVCommon/Private/BVMessageInterceptor.h b/Pod/BVCommon/Private/BVMessageInterceptor.h index a079c536..d0c06705 100644 --- a/Pod/BVCommon/Private/BVMessageInterceptor.h +++ b/Pod/BVCommon/Private/BVMessageInterceptor.h @@ -9,9 +9,9 @@ @interface BVMessageInterceptor : NSObject -@property (nonatomic, assign) id receiver; -@property (nonatomic, assign) id middleMan; +@property(nonatomic, assign) id receiver; +@property(nonatomic, assign) id middleMan; --(id)initWithMiddleman:(id)middleMan; +- (id)initWithMiddleman:(id)middleMan; @end diff --git a/Pod/BVCommon/Private/BVSDKConfiguration.h b/Pod/BVCommon/Private/BVSDKConfiguration.h index 629931e1..750583d7 100644 --- a/Pod/BVCommon/Private/BVSDKConfiguration.h +++ b/Pod/BVCommon/Private/BVSDKConfiguration.h @@ -5,48 +5,56 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import #import "BVSDKManager.h" +#import @interface BVSDKConfiguration : NSObject --(instancetype _Nonnull)init __attribute__((unavailable("init not available"))); +- (nonnull instancetype)init __attribute__((unavailable("init not available"))); --(instancetype _Nonnull)initWithDictionary:(NSDictionary * _Nonnull)dict configType:(BVConfigurationType)type; +- (nonnull instancetype)initWithDictionary:(nonnull NSDictionary *)dict + configType:(BVConfigurationType)type; /// Client ID associated with the API key -@property (nonatomic, strong, readonly, nonnull) NSString *clientId; +@property(nonatomic, strong, readonly, nonnull) NSString *clientId; /// Your private API key for the BVConversations product -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyConversations; +@property(nonatomic, strong, readonly, nullable) NSString *apiKeyConversations; /// Your private API key for the BVConversations, Store Reviews product -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyConversationsStores; +@property(nonatomic, strong, readonly, nullable) + NSString *apiKeyConversationsStores; /// Your private API key for Post Interaction Notifications -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyPIN; +@property(nonatomic, strong, readonly, nullable) NSString *apiKeyPIN; -/// The category of the Notification Content Extension you will use for store review notifications -@property (nonatomic, strong, readonly, nullable) NSString *storeReviewContentExtensionCategory; +/// The category of the Notification Content Extension you will use for store +/// review notifications +@property(nonatomic, strong, readonly, nullable) + NSString *storeReviewContentExtensionCategory; /// The category of the Notification Content Extension you will use for PIN -@property (nonatomic, strong, readonly, nullable) NSString *PINContentExtensionCategory; +@property(nonatomic, strong, readonly, nullable) + NSString *PINContentExtensionCategory; -/// Your private API key for the BVRecommendations and BVAdvertising products (Shopper Advertising) -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyShopperAdvertising; +/// Your private API key for the BVRecommendations and BVAdvertising products +/// (Shopper Advertising) +@property(nonatomic, strong, readonly, nullable) + NSString *apiKeyShopperAdvertising; /// Your private API key for the BVCurations API -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyCurations; +@property(nonatomic, strong, readonly, nullable) NSString *apiKeyCurations; /// Your private API key for the BVLocations API -@property (nonatomic, strong, readonly, nullable) NSString *apiKeyLocation; +@property(nonatomic, strong, readonly, nullable) NSString *apiKeyLocation; -@property (nonatomic, assign, readonly) BOOL staging; +@property(nonatomic, assign, readonly) BOOL staging; -@property (nonatomic, assign, readonly) BOOL dryRunAnalytics; +@property(nonatomic, assign, readonly) BOOL dryRunAnalytics; @end -//expose config to internal SDK -@interface BVSDKManager(Config) -@property (nonatomic, strong, readonly, nonnull) BVSDKConfiguration* configuration; +// expose config to internal SDK +@interface BVSDKManager (Config) +@property(nonatomic, strong, readonly, nonnull) + BVSDKConfiguration *configuration; @end diff --git a/Pod/BVCommon/Private/UIImage+BundleLocator.h b/Pod/BVCommon/Private/UIImage+BundleLocator.h index 7939b343..563431f1 100644 --- a/Pod/BVCommon/Private/UIImage+BundleLocator.h +++ b/Pod/BVCommon/Private/UIImage+BundleLocator.h @@ -9,7 +9,7 @@ #import @interface UIImage (BundleLocator) -+(UIImage*)bundledImageNamed:(NSString*) imageName; ++ (UIImage *)bundledImageNamed:(NSString *)imageName; @end @interface BundleLocator : NSObject diff --git a/Pod/BVCommon/UIImage+BundleLocator.m b/Pod/BVCommon/UIImage+BundleLocator.m index 6e8579af..35b22518 100644 --- a/Pod/BVCommon/UIImage+BundleLocator.m +++ b/Pod/BVCommon/UIImage+BundleLocator.m @@ -8,12 +8,11 @@ #import "UIImage+BundleLocator.h" @implementation UIImage (BundleLocator) -+(UIImage*)bundledImageNamed:(NSString*) imageName -{ - - return [UIImage imageNamed:imageName - inBundle:[NSBundle bundleForClass:[BundleLocator class]] - compatibleWithTraitCollection:nil]; ++ (UIImage *)bundledImageNamed:(NSString *)imageName { + return [UIImage imageNamed:imageName + inBundle:[NSBundle + bundleForClass:[BundleLocator class]] + compatibleWithTraitCollection:nil]; } @end diff --git a/Pod/BVConversations/Display/BVAuthorContentType.h b/Pod/BVConversations/Display/BVAuthorContentType.h index 770e3b2b..e462ec38 100644 --- a/Pod/BVConversations/Display/BVAuthorContentType.h +++ b/Pod/BVConversations/Display/BVAuthorContentType.h @@ -9,15 +9,14 @@ /// Types of Bazaarvoice content that can be included with a Profile. typedef NS_ENUM(NSInteger, BVAuthorContentType) { - BVAuthorContentTypeReviews, - BVAuthorContentTypeQuestions, - BVAuthorContentTypeAnswers, - BVAuthorContentTypeReviewComments + BVAuthorContentTypeReviews, + BVAuthorContentTypeQuestions, + BVAuthorContentTypeAnswers, + BVAuthorContentTypeReviewComments }; - @interface BVAuthorContentTypeUtil : NSObject -+(NSString*)toString:(BVAuthorContentType)type; ++ (NSString *)toString:(BVAuthorContentType)type; @end diff --git a/Pod/BVConversations/Display/BVAuthorContentType.m b/Pod/BVConversations/Display/BVAuthorContentType.m index 9339ddc2..43adaef0 100644 --- a/Pod/BVConversations/Display/BVAuthorContentType.m +++ b/Pod/BVConversations/Display/BVAuthorContentType.m @@ -9,17 +9,17 @@ @implementation BVAuthorContentTypeUtil -+(NSString*)toString:(BVAuthorContentType)type { - switch (type) { - case BVAuthorContentTypeReviews: - return @"Reviews"; - case BVAuthorContentTypeAnswers: - return @"Answers"; - case BVAuthorContentTypeQuestions: - return @"Questions"; - case BVAuthorContentTypeReviewComments: - return @"Comments"; - } ++ (NSString *)toString:(BVAuthorContentType)type { + switch (type) { + case BVAuthorContentTypeReviews: + return @"Reviews"; + case BVAuthorContentTypeAnswers: + return @"Answers"; + case BVAuthorContentTypeQuestions: + return @"Questions"; + case BVAuthorContentTypeReviewComments: + return @"Comments"; + } } @end diff --git a/Pod/BVConversations/Display/BVAuthorInclude.h b/Pod/BVConversations/Display/BVAuthorInclude.h index cff42bce..4300e80f 100644 --- a/Pod/BVConversations/Display/BVAuthorInclude.h +++ b/Pod/BVConversations/Display/BVAuthorInclude.h @@ -5,16 +5,16 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVAuthorContentType.h" +#import @interface BVAuthorInclude : NSObject @property BVAuthorContentType type; -@property NSNumber* _Nullable limit; - --(id _Nonnull)initWithContentType:(BVAuthorContentType)type limit:(NSNumber* _Nullable)limit; --(NSString* _Nonnull)toParamString; +@property(nullable) NSNumber *limit; +- (nonnull id)initWithContentType:(BVAuthorContentType)type + limit:(nullable NSNumber *)limit; +- (nonnull NSString *)toParamString; @end diff --git a/Pod/BVConversations/Display/BVAuthorInclude.m b/Pod/BVConversations/Display/BVAuthorInclude.m index 0a415938..335839dd 100644 --- a/Pod/BVConversations/Display/BVAuthorInclude.m +++ b/Pod/BVConversations/Display/BVAuthorInclude.m @@ -9,17 +9,18 @@ @implementation BVAuthorInclude --(id _Nonnull)initWithContentType:(BVAuthorContentType)type limit:(NSNumber* _Nullable)limit { - self = [super init]; - if(self){ - self.type = type; - self.limit = limit; - } - return self; +- (nonnull id)initWithContentType:(BVAuthorContentType)type + limit:(nullable NSNumber *)limit { + self = [super init]; + if (self) { + self.type = type; + self.limit = limit; + } + return self; } --(NSString* _Nonnull)toParamString { - return [BVAuthorContentTypeUtil toString:self.type]; +- (nonnull NSString *)toParamString { + return [BVAuthorContentTypeUtil toString:self.type]; } @end diff --git a/Pod/BVConversations/Display/BVCommaUtil.h b/Pod/BVConversations/Display/BVCommaUtil.h index 4ded5d5d..99c772d9 100644 --- a/Pod/BVConversations/Display/BVCommaUtil.h +++ b/Pod/BVConversations/Display/BVCommaUtil.h @@ -10,7 +10,8 @@ /// Internal utility - used only within BVSDK @interface BVCommaUtil : NSObject -+(NSString* _Nonnull)escape:(NSString* _Nonnull)productId; -+(NSArray* _Nonnull)escapeMultiple:(NSArray* _Nonnull)productIds; ++ (nonnull NSString *)escape:(nonnull NSString *)productId; ++ (nonnull NSArray *)escapeMultiple: + (nonnull NSArray *)productIds; @end diff --git a/Pod/BVConversations/Display/BVCommaUtil.m b/Pod/BVConversations/Display/BVCommaUtil.m index d73bdf02..a4cb09c2 100644 --- a/Pod/BVConversations/Display/BVCommaUtil.m +++ b/Pod/BVConversations/Display/BVCommaUtil.m @@ -9,22 +9,22 @@ @implementation BVCommaUtil -+(NSString* _Nonnull)escape:(NSString* _Nonnull)productId { - - return [[[productId stringByReplacingOccurrencesOfString:@"," withString:@"\\,"] - stringByReplacingOccurrencesOfString:@":" withString:@"\\:"] - stringByReplacingOccurrencesOfString:@"&" withString:@"%26"]; - ++ (nonnull NSString *)escape:(nonnull NSString *)productId { + return + [[[productId stringByReplacingOccurrencesOfString:@"," withString:@"\\,"] + stringByReplacingOccurrencesOfString:@":" + withString:@"\\:"] + stringByReplacingOccurrencesOfString:@"&" + withString:@"%26"]; } -+(NSArray* _Nonnull)escapeMultiple:(NSArray* _Nonnull)productIds { - - NSMutableArray* results = [NSMutableArray array]; - for(NSString* productId in productIds) { - [results addObject:[self escape:productId]]; - } - return results; - ++ (nonnull NSArray *)escapeMultiple: + (nonnull NSArray *)productIds { + NSMutableArray *results = [NSMutableArray array]; + for (NSString *productId in productIds) { + [results addObject:[self escape:productId]]; + } + return results; } @end diff --git a/Pod/BVConversations/Display/BVCommentIncludeType.h b/Pod/BVConversations/Display/BVCommentIncludeType.h index 16c94bcd..8ba77b83 100644 --- a/Pod/BVConversations/Display/BVCommentIncludeType.h +++ b/Pod/BVConversations/Display/BVCommentIncludeType.h @@ -9,16 +9,13 @@ /// Types of Bazaarvoice content that can be included with a Profile. typedef NS_ENUM(NSInteger, BVCommentIncludeType) { - BVCommentIncludeTypeReviews, - BVCommentIncludeTypeProducts, - BVCommentIncludeTypeAuthors + BVCommentIncludeTypeReviews, + BVCommentIncludeTypeProducts, + BVCommentIncludeTypeAuthors }; - @interface BVCommentIncludeTypeUtil : NSObject -+(NSString*)toString:(BVCommentIncludeType)type; ++ (NSString *)toString:(BVCommentIncludeType)type; @end - - diff --git a/Pod/BVConversations/Display/BVCommentIncludeType.m b/Pod/BVConversations/Display/BVCommentIncludeType.m index ffdfb23e..06fff04a 100644 --- a/Pod/BVConversations/Display/BVCommentIncludeType.m +++ b/Pod/BVConversations/Display/BVCommentIncludeType.m @@ -1,3 +1,5 @@ + + // // BVCommentIncludeType.m // BVSDK @@ -9,14 +11,15 @@ @implementation BVCommentIncludeTypeUtil -+(NSString*)toString:(BVCommentIncludeType)type{ - - switch (type) { - case BVCommentIncludeTypeProducts: return @"Products"; - case BVCommentIncludeTypeReviews: return @"Reviews"; - case BVCommentIncludeTypeAuthors: return @"Authors"; - } - ++ (NSString *)toString:(BVCommentIncludeType)type { + switch (type) { + case BVCommentIncludeTypeProducts: + return @"Products"; + case BVCommentIncludeTypeReviews: + return @"Reviews"; + case BVCommentIncludeTypeAuthors: + return @"Authors"; + } } @end diff --git a/Pod/BVConversations/Display/BVErrorCode.h b/Pod/BVConversations/Display/BVErrorCode.h index d87e8539..9eb075f2 100644 --- a/Pod/BVConversations/Display/BVErrorCode.h +++ b/Pod/BVConversations/Display/BVErrorCode.h @@ -11,26 +11,26 @@ Type of error code */ typedef NS_ENUM(NSInteger, BVErrorCode) { - // Common - BVErrorCodeBadRequest, - BVErrorCodeUnknown, - BVErrorCodeAccessDenied, - BVErrorCodeParamInvalidApiKey, - BVErrorCodeParamInvalidLocale, - BVErrorCodeRequestLimitReached, - BVErrorCodeUnsupported, - - // Display Request Specific - BVErrorCodeParamInvalidCallback, - BVErrorCodeParamInvalidFilterAttribute, - BVErrorCodeParamInvalidIncluded, - BVErrorCodeParamInvalidLimit, - BVErrorCodeParamInvalidOffset, - BVErrorCodeParamInvalidSearchAttribute, - BVErrorCodeParamInvalidSortAttribute, - - // Submission Request Specific - BVErrorCodeDuplicateSubmission, - BVErrorCodeParamInvalidParameters, - BVErrorCodeParamMissingUserId + // Common + BVErrorCodeBadRequest, + BVErrorCodeUnknown, + BVErrorCodeAccessDenied, + BVErrorCodeParamInvalidApiKey, + BVErrorCodeParamInvalidLocale, + BVErrorCodeRequestLimitReached, + BVErrorCodeUnsupported, + + // Display Request Specific + BVErrorCodeParamInvalidCallback, + BVErrorCodeParamInvalidFilterAttribute, + BVErrorCodeParamInvalidIncluded, + BVErrorCodeParamInvalidLimit, + BVErrorCodeParamInvalidOffset, + BVErrorCodeParamInvalidSearchAttribute, + BVErrorCodeParamInvalidSortAttribute, + + // Submission Request Specific + BVErrorCodeDuplicateSubmission, + BVErrorCodeParamInvalidParameters, + BVErrorCodeParamMissingUserId }; diff --git a/Pod/BVConversations/Display/BVReviewIncludeType.h b/Pod/BVConversations/Display/BVReviewIncludeType.h index d3ee1b29..fb4d7b7c 100644 --- a/Pod/BVConversations/Display/BVReviewIncludeType.h +++ b/Pod/BVConversations/Display/BVReviewIncludeType.h @@ -7,16 +7,14 @@ #import - /// Types of Bazaarvoice content that can be included with a Profile. typedef NS_ENUM(NSInteger, BVReviewIncludeType) { - BVReviewIncludeTypeProducts, - BVReviewIncludeTypeComments + BVReviewIncludeTypeProducts, + BVReviewIncludeTypeComments }; - @interface BVReviewIncludeTypeUtil : NSObject -+(NSString*)toString:(BVReviewIncludeType)type; ++ (NSString *)toString:(BVReviewIncludeType)type; @end diff --git a/Pod/BVConversations/Display/BVReviewIncludeType.m b/Pod/BVConversations/Display/BVReviewIncludeType.m index 77d79b2e..c0b9fd04 100644 --- a/Pod/BVConversations/Display/BVReviewIncludeType.m +++ b/Pod/BVConversations/Display/BVReviewIncludeType.m @@ -9,11 +9,13 @@ @implementation BVReviewIncludeTypeUtil -+(NSString*)toString:(BVReviewIncludeType)type { - switch (type) { - case BVReviewIncludeTypeProducts: return @"Products"; - case BVReviewIncludeTypeComments: return @"Comments"; - } ++ (NSString *)toString:(BVReviewIncludeType)type { + switch (type) { + case BVReviewIncludeTypeProducts: + return @"Products"; + case BVReviewIncludeTypeComments: + return @"Comments"; + } } @end diff --git a/Pod/BVConversations/Display/BVStoreIncludeContentType.h b/Pod/BVConversations/Display/BVStoreIncludeContentType.h index 4aadff6f..5ae8c7d9 100644 --- a/Pod/BVConversations/Display/BVStoreIncludeContentType.h +++ b/Pod/BVConversations/Display/BVStoreIncludeContentType.h @@ -11,11 +11,11 @@ Types of Bazaarvoice content. */ typedef NS_ENUM(NSInteger, BVStoreIncludeContentType) { - BVStoreIncludeContentTypeReviews + BVStoreIncludeContentTypeReviews }; @interface BVStoreIncludeContentTypeUtil : NSObject -+(NSString*)toString:(BVStoreIncludeContentType)type; ++ (NSString *)toString:(BVStoreIncludeContentType)type; @end diff --git a/Pod/BVConversations/Display/BVStoreIncludeContentType.m b/Pod/BVConversations/Display/BVStoreIncludeContentType.m index 6ca4bb79..48d8f1c4 100644 --- a/Pod/BVConversations/Display/BVStoreIncludeContentType.m +++ b/Pod/BVConversations/Display/BVStoreIncludeContentType.m @@ -9,11 +9,11 @@ @implementation BVStoreIncludeContentTypeUtil -+(NSString*)toString:(BVStoreIncludeContentType)type { - switch (type) { - case BVStoreIncludeContentTypeReviews: - return @"Reviews"; - } ++ (NSString *)toString:(BVStoreIncludeContentType)type { + switch (type) { + case BVStoreIncludeContentTypeReviews: + return @"Reviews"; + } } @end diff --git a/Pod/BVConversations/Display/Model/BVAuthorResponse.h b/Pod/BVConversations/Display/Model/BVAuthorResponse.h index 0cfa596c..ff6cc0b9 100644 --- a/Pod/BVConversations/Display/Model/BVAuthorResponse.h +++ b/Pod/BVConversations/Display/Model/BVAuthorResponse.h @@ -5,11 +5,11 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import "BVResponse.h" #import "BVAuthor.h" #import "BVBaseConversationsResponse.h" +#import "BVResponse.h" +#import -@interface BVAuthorResponse : BVBaseConversationsResultsResponse +@interface BVAuthorResponse : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVAuthorResponse.m b/Pod/BVConversations/Display/Model/BVAuthorResponse.m index 976ad9ac..2ac14f9f 100644 --- a/Pod/BVConversations/Display/Model/BVAuthorResponse.m +++ b/Pod/BVConversations/Display/Model/BVAuthorResponse.m @@ -6,11 +6,12 @@ // #import "BVAuthorResponse.h" -#import "BVNullHelper.h" #import "BVConversationsInclude.h" +#import "BVNullHelper.h" @implementation BVAuthorResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVAuthor alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVAuthor alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVBadge.h b/Pod/BVConversations/Display/Model/BVBadge.h index a3541188..ee9288a0 100644 --- a/Pod/BVConversations/Display/Model/BVBadge.h +++ b/Pod/BVConversations/Display/Model/BVBadge.h @@ -5,25 +5,31 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVBadgeType.h" +#import /* Badge to attribute a piece of content with certain characteristics: * user received a free sample * user is a verified purchaser * user is affiliated with a certain company - See a full tutorial on badging here: https://developer.bazaarvoice.com/apis/conversations/tutorials/Badges_Display + See a full tutorial on badging here: + https://developer.bazaarvoice.com/apis/conversations/tutorials/Badges_Display */ @interface BVBadge : NSObject -/// The BadgeType lists the badge category. Values include "Merit", "Custom", "Affiliation", and "Rank". These are codes internal to Bazaarvoice. +/// The BadgeType lists the badge category. Values include "Merit", "Custom", +/// "Affiliation", and "Rank". These are codes internal to Bazaarvoice. @property BVBadgeType badgeType; -/// The badge Id can be used to obtain the related ContextDataValues that can also be returned in the API repsonse when configured. ContextDataValues can contain additional metadata such as a label to be displayed rather than an icon or ToolTip copy. -@property NSString* _Nullable identifier; -/// The badge ContentType indicates the specific item the badge is meant for. Typical values are 'REVIEWS', 'QUESTIONS', or 'ANSWERS'. -@property NSString* _Nullable contentType; +/// The badge Id can be used to obtain the related ContextDataValues that can +/// also be returned in the API repsonse when configured. ContextDataValues can +/// contain additional metadata such as a label to be displayed rather than an +/// icon or ToolTip copy. +@property(nullable) NSString *identifier; +/// The badge ContentType indicates the specific item the badge is meant for. +/// Typical values are 'REVIEWS', 'QUESTIONS', or 'ANSWERS'. +@property(nullable) NSString *contentType; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVBadge.m b/Pod/BVConversations/Display/Model/BVBadge.m index 73ecd946..d3999d0b 100644 --- a/Pod/BVConversations/Display/Model/BVBadge.m +++ b/Pod/BVConversations/Display/Model/BVBadge.m @@ -10,15 +10,15 @@ @implementation BVBadge --(id)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self) { - self.badgeType = [BVBadgeTypeUtil fromString:[apiResponse objectForKey:@"BadgeType"]]; - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.contentType, apiResponse[@"ContentType"]) - - } - return self; +- (id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + self.badgeType = + [BVBadgeTypeUtil fromString:[apiResponse objectForKey:@"BadgeType"]]; + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.contentType, apiResponse[@"ContentType"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVBadgeType.h b/Pod/BVConversations/Display/Model/BVBadgeType.h index fa2d86d7..4ff9b49d 100644 --- a/Pod/BVConversations/Display/Model/BVBadgeType.h +++ b/Pod/BVConversations/Display/Model/BVBadgeType.h @@ -11,15 +11,14 @@ Type of badge. See BVBadge.h for usage. */ typedef NS_ENUM(NSInteger, BVBadgeType) { - BVBadgeTypeMerit, - BVBadgeTypeAffiliation, - BVBadgeTypeRank, - BVBadgeTypeCustom + BVBadgeTypeMerit, + BVBadgeTypeAffiliation, + BVBadgeTypeRank, + BVBadgeTypeCustom }; @interface BVBadgeTypeUtil : NSObject -+ (BVBadgeType)fromString:(NSString* _Nullable)str; ++ (BVBadgeType)fromString:(nullable NSString *)str; @end - diff --git a/Pod/BVConversations/Display/Model/BVBadgeType.m b/Pod/BVConversations/Display/Model/BVBadgeType.m index cf370b73..08938ada 100644 --- a/Pod/BVConversations/Display/Model/BVBadgeType.m +++ b/Pod/BVConversations/Display/Model/BVBadgeType.m @@ -9,19 +9,18 @@ @implementation BVBadgeTypeUtil -+ (BVBadgeType)fromString:(NSString* _Nullable)str{ - - if([str isEqualToString:@"Merit"]) { - return BVBadgeTypeMerit; - } - if([str isEqualToString:@"Affiliation"]) { - return BVBadgeTypeAffiliation; - } - if([str isEqualToString:@"Rank"]) { - return BVBadgeTypeRank; - } - return BVBadgeTypeCustom; - ++ (BVBadgeType)fromString:(nullable NSString *)str { + + if ([str isEqualToString:@"Merit"]) { + return BVBadgeTypeMerit; + } + if ([str isEqualToString:@"Affiliation"]) { + return BVBadgeTypeAffiliation; + } + if ([str isEqualToString:@"Rank"]) { + return BVBadgeTypeRank; + } + return BVBadgeTypeCustom; } -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Model/BVBrand.h b/Pod/BVConversations/Display/Model/BVBrand.h index e747c1f8..7bd76853 100644 --- a/Pod/BVConversations/Display/Model/BVBrand.h +++ b/Pod/BVConversations/Display/Model/BVBrand.h @@ -12,9 +12,9 @@ */ @interface BVBrand : NSObject -@property NSString* _Nullable name; -@property NSString* _Nullable identifier; +@property(nullable) NSString *name; +@property(nullable) NSString *identifier; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVBrand.m b/Pod/BVConversations/Display/Model/BVBrand.m index 5dad0a0a..3761c65f 100644 --- a/Pod/BVConversations/Display/Model/BVBrand.m +++ b/Pod/BVConversations/Display/Model/BVBrand.m @@ -10,23 +10,20 @@ @implementation BVBrand --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - - self = [super init]; - if(self){ - if ([apiResponse isKindOfClass: [NSDictionary class]]){ - NSDictionary* apiObject = (NSDictionary*)apiResponse; - - SET_IF_NOT_NULL(self.name, apiObject[@"Name"]) - SET_IF_NOT_NULL(self.identifier, apiObject[@"Id"]) - - } - else { - return nil; - } +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + if ([apiResponse isKindOfClass:[NSDictionary class]]) { + NSDictionary *apiObject = (NSDictionary *)apiResponse; + + SET_IF_NOT_NULL(self.name, apiObject[@"Name"]) + SET_IF_NOT_NULL(self.identifier, apiObject[@"Id"]) + + } else { + return nil; } - return self; - + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVBulkProductResponse.h b/Pod/BVConversations/Display/Model/BVBulkProductResponse.h index 79ec3f5e..9be9ba83 100644 --- a/Pod/BVConversations/Display/Model/BVBulkProductResponse.h +++ b/Pod/BVConversations/Display/Model/BVBulkProductResponse.h @@ -5,9 +5,10 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import #import "BVBaseConversationsResponse.h" +#import -@interface BVBulkProductResponse : BVBaseConversationsResultsResponse +@interface BVBulkProductResponse + : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVBulkProductResponse.m b/Pod/BVConversations/Display/Model/BVBulkProductResponse.m index af959629..3497ce92 100644 --- a/Pod/BVConversations/Display/Model/BVBulkProductResponse.m +++ b/Pod/BVConversations/Display/Model/BVBulkProductResponse.m @@ -9,8 +9,9 @@ @implementation BVBulkProductResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVProduct alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVProduct alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.h b/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.h index bfc41eee..3bcfa5f6 100644 --- a/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.h +++ b/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.h @@ -5,15 +5,18 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVResponse.h" -#import "BVProductStatistics.h" #import "BVBaseConversationsResponse.h" +#import "BVProductStatistics.h" +#import "BVResponse.h" +#import /* - A response to a `BVBulkRatingsRequest`. Contains one or multiple (up to 50) `BVProductStatistics` in the `results` array. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVBulkRatingsRequest`. Contains one or multiple (up to 50) + `BVProductStatistics` in the `results` array. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). */ -@interface BVBulkRatingsResponse : BVBaseConversationsResultsResponse +@interface BVBulkRatingsResponse + : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.m b/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.m index 273fb4e7..77833cae 100644 --- a/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.m +++ b/Pod/BVConversations/Display/Model/BVBulkRatingsResponse.m @@ -10,8 +10,9 @@ @implementation BVBulkRatingsResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVProductStatistics alloc] initWithApiResponse: raw]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVProductStatistics alloc] initWithApiResponse:raw]; } @end diff --git a/Pod/BVConversations/Display/Model/BVCommentsResponse.h b/Pod/BVConversations/Display/Model/BVCommentsResponse.h index 450dbc22..4ae0b566 100644 --- a/Pod/BVConversations/Display/Model/BVCommentsResponse.h +++ b/Pod/BVConversations/Display/Model/BVCommentsResponse.h @@ -10,6 +10,6 @@ #import "BVBaseConversationsResponse.h" #import "BVComment.h" -@interface BVCommentsResponse : BVBaseConversationsResultsResponse +@interface BVCommentsResponse : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVCommentsResponse.m b/Pod/BVConversations/Display/Model/BVCommentsResponse.m index 7c930b95..df6503c3 100644 --- a/Pod/BVConversations/Display/Model/BVCommentsResponse.m +++ b/Pod/BVConversations/Display/Model/BVCommentsResponse.m @@ -9,8 +9,9 @@ @implementation BVCommentsResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVComment alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVComment alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVContextDataValue.h b/Pod/BVConversations/Display/Model/BVContextDataValue.h index ff27aa4d..e6a29145 100644 --- a/Pod/BVConversations/Display/Model/BVContextDataValue.h +++ b/Pod/BVConversations/Display/Model/BVContextDataValue.h @@ -8,17 +8,18 @@ #import /* - A Bazaarvoice Context Data Value. Generally, this is extra information collected when a user + A Bazaarvoice Context Data Value. Generally, this is extra information + collected when a user submitted a review, question, or answer. - A common Context Data Value is "Age" and "Gender". + A common Context Data Value is "Age" and "Gender". */ @interface BVContextDataValue : NSObject -@property NSString* _Nullable value; -@property NSString* _Nullable valueLabel; -@property NSString* _Nullable dimensionLabel; -@property NSString* _Nullable identifier; +@property(nullable) NSString *value; +@property(nullable) NSString *valueLabel; +@property(nullable) NSString *dimensionLabel; +@property(nullable) NSString *identifier; --(id _Nonnull)initWithApiResponse:(id _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull id)apiResponse; -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Model/BVContextDataValue.m b/Pod/BVConversations/Display/Model/BVContextDataValue.m index 92e00a30..d7c2a68b 100644 --- a/Pod/BVConversations/Display/Model/BVContextDataValue.m +++ b/Pod/BVConversations/Display/Model/BVContextDataValue.m @@ -10,15 +10,15 @@ @implementation BVContextDataValue --(id)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self) { - SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) - SET_IF_NOT_NULL(self.valueLabel, apiResponse[@"ValueLabel"]) - SET_IF_NOT_NULL(self.dimensionLabel, apiResponse[@"DimensionLabel"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - } - return self; +- (id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) + SET_IF_NOT_NULL(self.valueLabel, apiResponse[@"ValueLabel"]) + SET_IF_NOT_NULL(self.dimensionLabel, apiResponse[@"DimensionLabel"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVConversationsError.h b/Pod/BVConversations/Display/Model/BVConversationsError.h index 54177317..ee44574d 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsError.h +++ b/Pod/BVConversations/Display/Model/BVConversationsError.h @@ -7,17 +7,18 @@ #import -extern NSString* _Nonnull const BVKeyErrorMessage; -extern NSString* _Nonnull const BVKeyErrorCode; +extern NSString *__nonnull const BVKeyErrorMessage; +extern NSString *__nonnull const BVKeyErrorCode; /// Internal class - used only within BVSDK @interface BVConversationsError : NSObject -@property NSString* _Nonnull message; -@property NSString* _Nonnull code; +@property(nonnull) NSString *message; +@property(nonnull) NSString *code; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; --(NSError* _Nonnull)toNSError; -+(NSArray* _Nonnull)createErrorListFromApiResponse:(id _Nullable)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; +- (nonnull NSError *)toNSError; ++ (nonnull NSArray *)createErrorListFromApiResponse: + (nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVConversationsError.m b/Pod/BVConversations/Display/Model/BVConversationsError.m index b3f1862c..a267fd0a 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsError.m +++ b/Pod/BVConversations/Display/Model/BVConversationsError.m @@ -6,51 +6,49 @@ // #import "BVConversationsError.h" -#import "BVNullHelper.h" #import "BVCore.h" +#import "BVNullHelper.h" -NSString* _Nonnull const BVKeyErrorMessage = @"BVKeyErrorMessage"; -NSString* _Nonnull const BVKeyErrorCode = @"BVKeyErrorCode"; +NSString *__nonnull const BVKeyErrorMessage = @"BVKeyErrorMessage"; +NSString *__nonnull const BVKeyErrorCode = @"BVKeyErrorCode"; @implementation BVConversationsError --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.message, apiResponse[@"Message"]) - SET_IF_NOT_NULL(self.code, apiResponse[@"Code"]) - } - return self; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.message, apiResponse[@"Message"]) + SET_IF_NOT_NULL(self.code, apiResponse[@"Code"]) + } + return self; } --(NSError* _Nonnull)toNSError { - NSString* description = [NSString stringWithFormat:@"%@: %@", self.code, self.message]; - return [NSError - errorWithDomain:BVErrDomain - code:999 - userInfo:@{ - NSLocalizedDescriptionKey: description, - BVKeyErrorMessage: self.message, - BVKeyErrorCode: self.code - }]; +- (nonnull NSError *)toNSError { + NSString *description = + [NSString stringWithFormat:@"%@: %@", self.code, self.message]; + return [NSError errorWithDomain:BVErrDomain + code:999 + userInfo:@{ + NSLocalizedDescriptionKey : description, + BVKeyErrorMessage : self.message, + BVKeyErrorCode : self.code + }]; } -+(NSArray* _Nonnull)createErrorListFromApiResponse:(id _Nullable)apiResponse { - - NSMutableArray* errors = [NSMutableArray array]; - - if ([apiResponse isKindOfClass:[NSArray class]]){ - NSArray* apiObject = (NSArray*)apiResponse; - - for (NSDictionary* errorDict in apiObject){ - BVConversationsError* error = [[BVConversationsError alloc] initWithApiResponse:errorDict]; - [errors addObject:error]; - } - - } - return errors; -} ++ (nonnull NSArray *)createErrorListFromApiResponse: + (nullable id)apiResponse { + NSMutableArray *errors = [NSMutableArray array]; + if ([apiResponse isKindOfClass:[NSArray class]]) { + NSArray *apiObject = (NSArray *)apiResponse; + for (NSDictionary *errorDict in apiObject) { + BVConversationsError *error = + [[BVConversationsError alloc] initWithApiResponse:errorDict]; + [errors addObject:error]; + } + } + return errors; +} @end diff --git a/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.h b/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.h index 7b21df8a..2d3f1dc6 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.h +++ b/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.h @@ -8,14 +8,17 @@ #import /* - The API request reached the server successfully, but error(s) occured in the server. See the errors returned from `toNSError` + The API request reached the server successfully, but error(s) occured in the + server. See the errors returned from `toNSError` to debug this failure. - Typically, this happens when the `apiKeyConversations` is set incorrectly, or invalid parameters are attached to a request. - For example: a `BVReviewsRequest` object with a limit of 400 is invalid (max of 20) and will return with an error in this class. + Typically, this happens when the `apiKeyConversations` is set incorrectly, or + invalid parameters are attached to a request. + For example: a `BVReviewsRequest` object with a limit of 400 is invalid (max of + 20) and will return with an error in this class. */ @interface BVConversationsErrorResponse : NSObject --(id _Nullable)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; --(NSArray* _Nonnull)toNSErrors; +- (nullable id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; +- (nonnull NSArray *)toNSErrors; @end diff --git a/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.m b/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.m index cb418d2f..4b6100fb 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.m +++ b/Pod/BVConversations/Display/Model/BVConversationsErrorResponse.m @@ -8,54 +8,50 @@ #import "BVConversationsErrorResponse.h" #import "BVConversationsError.h" -@interface BVConversationsErrorResponse() +@interface BVConversationsErrorResponse () -@property NSArray* _Nonnull errors; +@property(nonnull) NSArray *errors; @end @implementation BVConversationsErrorResponse --(id _Nullable)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - - self = [super init]; - if(self){ - - if([[apiResponse objectForKey:@"Errors"] isKindOfClass:[NSArray class]]) { - NSArray* rawErrors = [apiResponse objectForKey:@"Errors"]; - - NSMutableArray* errorsArrayBuilder = [NSMutableArray array]; - for(NSDictionary* rawError in rawErrors) { - BVConversationsError* conversationsError = [[BVConversationsError alloc] initWithApiResponse:rawError]; - [errorsArrayBuilder addObject:conversationsError]; - } - - if ([errorsArrayBuilder count] == 0) { - return nil; - } - - self.errors = errorsArrayBuilder; - - } - else { - return nil; - } +- (nullable id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + if ([[apiResponse objectForKey:@"Errors"] + isKindOfClass:[NSArray class]]) { + NSArray *rawErrors = [apiResponse objectForKey:@"Errors"]; + + NSMutableArray *errorsArrayBuilder = [NSMutableArray array]; + for (NSDictionary *rawError in rawErrors) { + BVConversationsError *conversationsError = + [[BVConversationsError alloc] initWithApiResponse:rawError]; + [errorsArrayBuilder addObject:conversationsError]; + } + + if ([errorsArrayBuilder count] == 0) { + return nil; + } + + self.errors = errorsArrayBuilder; + + } else { + return nil; } - return self; - + } + return self; } --(NSArray* _Nonnull)toNSErrors { - - NSMutableArray* nsErrorsBuilder = [NSMutableArray array]; - - for (BVConversationsError* conversationsError in self.errors) { - NSError* nsError = [conversationsError toNSError]; - [nsErrorsBuilder addObject:nsError]; - } - - return nsErrorsBuilder; - +- (nonnull NSArray *)toNSErrors { + NSMutableArray *nsErrorsBuilder = [NSMutableArray array]; + + for (BVConversationsError *conversationsError in self.errors) { + NSError *nsError = [conversationsError toNSError]; + [nsErrorsBuilder addObject:nsError]; + } + + return nsErrorsBuilder; } -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Model/BVConversationsInclude.h b/Pod/BVConversations/Display/Model/BVConversationsInclude.h index 4ca953ec..997b30f3 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsInclude.h +++ b/Pod/BVConversations/Display/Model/BVConversationsInclude.h @@ -5,26 +5,26 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVProduct.h" #import "BVAnswer.h" +#import "BVAuthor.h" +#import "BVComment.h" +#import "BVProduct.h" #import "BVQuestion.h" #import "BVReview.h" -#import "BVComment.h" -#import "BVAuthor.h" +#import /// Internal utility - used only within BVSDK @interface BVConversationsInclude : NSObject --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; --(BVAnswer* _Nullable)getAnswerById:(NSString* _Nonnull)answerId; --(BVProduct* _Nullable)getProductById:(NSString* _Nonnull)productId; --(BVReview* _Nullable)getReviewById:(NSString* _Nonnull)reviewId; --(BVQuestion* _Nullable)getQuestionById:(NSString* _Nonnull)questionId; --(BVComment* _Nullable)getCommentById:(NSString* _Nonnull)commentId; --(BVAuthor* _Nullable)getAuthorById:(NSString* _Nonnull)authorId; +- (nullable BVAnswer *)getAnswerById:(nonnull NSString *)answerId; +- (nullable BVProduct *)getProductById:(nonnull NSString *)productId; +- (nullable BVReview *)getReviewById:(nonnull NSString *)reviewId; +- (nullable BVQuestion *)getQuestionById:(nonnull NSString *)questionId; +- (nullable BVComment *)getCommentById:(nonnull NSString *)commentId; +- (nullable BVAuthor *)getAuthorById:(nonnull NSString *)authorId; -@property (nonatomic, strong, readonly) NSDictionary * _Nonnull apiResponse; +@property(nonnull, nonatomic, strong, readonly) NSDictionary *apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVConversationsInclude.m b/Pod/BVConversations/Display/Model/BVConversationsInclude.m index 22a11c3c..c17592f3 100644 --- a/Pod/BVConversations/Display/Model/BVConversationsInclude.m +++ b/Pod/BVConversations/Display/Model/BVConversationsInclude.m @@ -7,91 +7,106 @@ #import "BVConversationsInclude.h" -@interface BVConversationsInclude() +@interface BVConversationsInclude () -@property NSDictionary* _Nonnull products; -@property NSDictionary* _Nonnull reviews; -@property NSDictionary* _Nonnull questions; -@property NSDictionary* _Nonnull answers; -@property NSDictionary* _Nonnull comments; -@property NSDictionary* _Nonnull authors; +@property(nonnull) NSDictionary *products; +@property(nonnull) NSDictionary *reviews; +@property(nonnull) NSDictionary *questions; +@property(nonnull) NSDictionary *answers; +@property(nonnull) NSDictionary *comments; +@property(nonnull) NSDictionary *authors; @end @implementation BVConversationsInclude --(BVAnswer* _Nullable)getAnswerById:(NSString* _Nonnull)answerId { - return self.answers[answerId]; +- (nullable BVAnswer *)getAnswerById:(nonnull NSString *)answerId { + return self.answers[answerId]; } --(BVProduct* _Nullable)getProductById:(NSString* _Nonnull)productId { - return self.products[productId]; +- (nullable BVProduct *)getProductById:(nonnull NSString *)productId { + return self.products[productId]; } --(BVReview* _Nullable)getReviewById:(NSString* _Nonnull)reviewId { - return self.reviews[reviewId]; +- (nullable BVReview *)getReviewById:(nonnull NSString *)reviewId { + return self.reviews[reviewId]; } --(BVQuestion* _Nullable)getQuestionById:(NSString* _Nonnull)questionId { - return self.questions[questionId]; +- (nullable BVQuestion *)getQuestionById:(nonnull NSString *)questionId { + return self.questions[questionId]; } --(BVComment* _Nullable)getCommentById:(NSString *)commentId { - return self.comments[commentId]; +- (nullable BVComment *)getCommentById:(NSString *)commentId { + return self.comments[commentId]; } --(BVAuthor* _Nullable)getAuthorById:(NSString *)authorId { - return self.authors[authorId]; +- (nullable BVAuthor *)getAuthorById:(NSString *)authorId { + return self.authors[authorId]; } --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self){ - _apiResponse = apiResponse; - - NSDictionary* reviewsDict = apiResponse[@"Reviews"]; - NSMutableDictionary* tempReviews = [NSMutableDictionary dictionary]; - for(NSString* key in reviewsDict){ - tempReviews[key] = [[BVReview alloc] initWithApiResponse:reviewsDict[key] includes:nil]; - } - self.reviews = [NSDictionary dictionaryWithDictionary:tempReviews]; - - NSDictionary* questionsDict = apiResponse[@"Questions"]; - NSMutableDictionary* tempQuestions = [NSMutableDictionary dictionary]; - for(NSString* key in questionsDict){ - tempQuestions[key] = [[BVQuestion alloc] initWithApiResponse:questionsDict[key] includes:nil]; - } - self.questions = [NSDictionary dictionaryWithDictionary:tempQuestions]; - - NSDictionary* productsDict = apiResponse[@"Products"]; - NSMutableDictionary* tempProducts = [NSMutableDictionary dictionary]; - for(NSString* key in productsDict){ - tempProducts[key] = [[BVProduct alloc] initWithApiResponse:productsDict[key] includes:nil]; - } - self.products = [NSDictionary dictionaryWithDictionary:tempProducts]; - - NSDictionary* answersDict = apiResponse[@"Answers"]; - NSMutableDictionary* tempAnswers = [NSMutableDictionary dictionary]; - for(NSString* key in answersDict){ - tempAnswers[key] = [[BVAnswer alloc] initWithApiResponse:answersDict[key] includes:nil]; - } - self.answers = [NSDictionary dictionaryWithDictionary:tempAnswers]; - - NSDictionary* commentsDict = apiResponse[@"Comments"]; - NSMutableDictionary *tempComments = [NSMutableDictionary dictionary]; - for (NSString *key in commentsDict){ - tempComments[key] = [[BVComment alloc] initWithApiResponse:commentsDict[key] includes:nil]; - } - self.comments = [NSDictionary dictionaryWithDictionary:tempComments]; - - NSDictionary* authorsDict = apiResponse[@"Authors"]; - NSMutableDictionary *tempAuthors = [NSMutableDictionary dictionary]; - for (NSString *key in authorsDict){ - tempAuthors[key] = [[BVAuthor alloc] initWithApiResponse:authorsDict[key] includes:nil]; - } - self.authors = [NSDictionary dictionaryWithDictionary:tempAuthors]; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + _apiResponse = apiResponse; + + NSDictionary *reviewsDict = apiResponse[@"Reviews"]; + NSMutableDictionary *tempReviews = + [NSMutableDictionary dictionary]; + for (NSString *key in reviewsDict) { + tempReviews[key] = + [[BVReview alloc] initWithApiResponse:reviewsDict[key] includes:nil]; + } + self.reviews = [NSDictionary dictionaryWithDictionary:tempReviews]; + + NSDictionary *questionsDict = apiResponse[@"Questions"]; + NSMutableDictionary *tempQuestions = + [NSMutableDictionary dictionary]; + for (NSString *key in questionsDict) { + tempQuestions[key] = + [[BVQuestion alloc] initWithApiResponse:questionsDict[key] + includes:nil]; + } + self.questions = [NSDictionary dictionaryWithDictionary:tempQuestions]; + + NSDictionary *productsDict = apiResponse[@"Products"]; + NSMutableDictionary *tempProducts = + [NSMutableDictionary dictionary]; + for (NSString *key in productsDict) { + tempProducts[key] = + [[BVProduct alloc] initWithApiResponse:productsDict[key] + includes:nil]; + } + self.products = [NSDictionary dictionaryWithDictionary:tempProducts]; + + NSDictionary *answersDict = apiResponse[@"Answers"]; + NSMutableDictionary *tempAnswers = + [NSMutableDictionary dictionary]; + for (NSString *key in answersDict) { + tempAnswers[key] = + [[BVAnswer alloc] initWithApiResponse:answersDict[key] includes:nil]; + } + self.answers = [NSDictionary dictionaryWithDictionary:tempAnswers]; + + NSDictionary *commentsDict = apiResponse[@"Comments"]; + NSMutableDictionary *tempComments = + [NSMutableDictionary dictionary]; + for (NSString *key in commentsDict) { + tempComments[key] = + [[BVComment alloc] initWithApiResponse:commentsDict[key] + includes:nil]; + } + self.comments = [NSDictionary dictionaryWithDictionary:tempComments]; + + NSDictionary *authorsDict = apiResponse[@"Authors"]; + NSMutableDictionary *tempAuthors = + [NSMutableDictionary dictionary]; + for (NSString *key in authorsDict) { + tempAuthors[key] = + [[BVAuthor alloc] initWithApiResponse:authorsDict[key] includes:nil]; } - return self; + self.authors = [NSDictionary dictionaryWithDictionary:tempAuthors]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.h b/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.h index 6c7a21a6..73af7462 100644 --- a/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.h +++ b/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.h @@ -5,17 +5,20 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVDistributionElement.h" #import "BVDimensionElement.h" +#import "BVDistributionElement.h" +#import -typedef NSMutableDictionary* TagDistribution; -typedef NSMutableDictionary* ContextDataDistribution; -typedef NSMutableDictionary* TagDimensions; +typedef NSMutableDictionary + *TagDistribution; +typedef NSMutableDictionary + *ContextDataDistribution; +typedef NSMutableDictionary *TagDimensions; /// Internal utility - used only within BVSDK @interface BVDimensionAndDistributionUtil : NSObject -+(TagDistribution _Nullable)createDistributionWithApiResponse:(id _Nullable)apiResponse; ++ (nullable TagDistribution)createDistributionWithApiResponse: + (nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.m b/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.m index 1b2c5c0d..4fd40b58 100644 --- a/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.m +++ b/Pod/BVConversations/Display/Model/BVDimensionAndDistributionUtil.m @@ -9,23 +9,22 @@ @implementation BVDimensionAndDistributionUtil -+(TagDistribution _Nullable)createDistributionWithApiResponse:(id _Nullable)apiResponse { - - - NSDictionary* apiObject = apiResponse; - if(apiObject == nil){ - return nil; - } - - TagDistribution tempValues = [NSMutableDictionary dictionary]; - for(NSString* key in apiObject) { - NSDictionary* value = [apiObject objectForKey:key]; - BVDistributionElement* element = [[BVDistributionElement alloc] initWithApiResponse:value]; - [tempValues setObject:element forKey:key]; - } - - return tempValues; - ++ (nullable TagDistribution)createDistributionWithApiResponse: + (nullable id)apiResponse { + NSDictionary *apiObject = apiResponse; + if (apiObject == nil) { + return nil; + } + + TagDistribution tempValues = [NSMutableDictionary dictionary]; + for (NSString *key in apiObject) { + NSDictionary *value = [apiObject objectForKey:key]; + BVDistributionElement *element = + [[BVDistributionElement alloc] initWithApiResponse:value]; + [tempValues setObject:element forKey:key]; + } + + return tempValues; } @end diff --git a/Pod/BVConversations/Display/Model/BVDimensionElement.h b/Pod/BVConversations/Display/Model/BVDimensionElement.h index 9e731dcb..5fc10b25 100644 --- a/Pod/BVConversations/Display/Model/BVDimensionElement.h +++ b/Pod/BVConversations/Display/Model/BVDimensionElement.h @@ -8,14 +8,14 @@ #import /* - A single tag dimension. + A single tag dimension. */ @interface BVDimensionElement : NSObject -@property NSString* _Nullable label; -@property NSString* _Nullable identifier; -@property NSArray* _Nullable values; +@property(nullable) NSString *label; +@property(nullable) NSString *identifier; +@property(nullable) NSArray *values; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVDimensionElement.m b/Pod/BVConversations/Display/Model/BVDimensionElement.m index 0e4fe003..bfb7f1b0 100644 --- a/Pod/BVConversations/Display/Model/BVDimensionElement.m +++ b/Pod/BVConversations/Display/Model/BVDimensionElement.m @@ -10,15 +10,15 @@ @implementation BVDimensionElement --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) - SET_IF_NOT_NULL(self.values, apiResponse[@"Values"]) - } - return self; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) + SET_IF_NOT_NULL(self.values, apiResponse[@"Values"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVDistributionElement.h b/Pod/BVConversations/Display/Model/BVDistributionElement.h index 77f2017c..1ecf9939 100644 --- a/Pod/BVConversations/Display/Model/BVDistributionElement.h +++ b/Pod/BVConversations/Display/Model/BVDistributionElement.h @@ -5,18 +5,18 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVDistributionValue.h" +#import /* A single tag distribition. */ @interface BVDistributionElement : NSObject -@property NSString* _Nonnull label; -@property NSString* _Nonnull identifier; -@property NSArray* _Nonnull values; +@property(nonnull) NSString *label; +@property(nonnull) NSString *identifier; +@property(nonnull) NSArray *values; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVDistributionElement.m b/Pod/BVConversations/Display/Model/BVDistributionElement.m index 1af1b840..2dcec331 100644 --- a/Pod/BVConversations/Display/Model/BVDistributionElement.m +++ b/Pod/BVConversations/Display/Model/BVDistributionElement.m @@ -10,20 +10,21 @@ @implementation BVDistributionElement --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - - NSMutableArray* tempValues = [NSMutableArray array]; - for(NSDictionary* value in apiResponse[@"Values"]) { - BVDistributionValue* distributionValue = [[BVDistributionValue alloc] initWithApiResponse:value]; - [tempValues addObject:distributionValue]; - } - self.values = tempValues; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + + NSMutableArray *tempValues = [NSMutableArray array]; + for (NSDictionary *value in apiResponse[@"Values"]) { + BVDistributionValue *distributionValue = + [[BVDistributionValue alloc] initWithApiResponse:value]; + [tempValues addObject:distributionValue]; } - return self; + self.values = tempValues; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVDistributionValue.h b/Pod/BVConversations/Display/Model/BVDistributionValue.h index fe278d92..712027c5 100644 --- a/Pod/BVConversations/Display/Model/BVDistributionValue.h +++ b/Pod/BVConversations/Display/Model/BVDistributionValue.h @@ -12,9 +12,9 @@ */ @interface BVDistributionValue : NSObject -@property NSString* _Nonnull value; -@property NSNumber* _Nonnull count; +@property(nonnull) NSString *value; +@property(nonnull) NSNumber *count; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVDistributionValue.m b/Pod/BVConversations/Display/Model/BVDistributionValue.m index 5e5537ba..3020b47f 100644 --- a/Pod/BVConversations/Display/Model/BVDistributionValue.m +++ b/Pod/BVConversations/Display/Model/BVDistributionValue.m @@ -10,13 +10,13 @@ @implementation BVDistributionValue --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) - SET_IF_NOT_NULL(self.count, apiResponse[@"Count"]) - } - return self; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) + SET_IF_NOT_NULL(self.count, apiResponse[@"Count"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVGenericConversationsResult.h b/Pod/BVConversations/Display/Model/BVGenericConversationsResult.h index 03e78cd9..c1237723 100644 --- a/Pod/BVConversations/Display/Model/BVGenericConversationsResult.h +++ b/Pod/BVConversations/Display/Model/BVGenericConversationsResult.h @@ -10,6 +10,7 @@ /// Internal protocol - used only within BVSDK @protocol BVGenericConversationsResult --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse includes:(BVConversationsInclude* _Nullable)includes; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse + includes:(nullable BVConversationsInclude *)includes; @end diff --git a/Pod/BVConversations/Display/Model/BVModelUtil.h b/Pod/BVConversations/Display/Model/BVModelUtil.h index 472c58aa..c1586bbf 100644 --- a/Pod/BVConversations/Display/Model/BVModelUtil.h +++ b/Pod/BVConversations/Display/Model/BVModelUtil.h @@ -5,26 +5,25 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVPhoto.h" -#import "BVVideo.h" -#import "BVContextDataValue.h" #import "BVBadge.h" -#import "BVSecondaryRating.h" +#import "BVContextDataValue.h" #import "BVDimensionAndDistributionUtil.h" +#import "BVPhoto.h" +#import "BVSecondaryRating.h" +#import "BVVideo.h" +#import /// Internal class - used only within BVSDK @interface BVModelUtil : NSObject -+ (NSDate * _Nullable)convertTimestampToDatetime:(id _Nullable)timestamp; -+ (NSArray * _Nonnull)parsePhotos:(id _Nullable)apiResponse; -+ (NSArray * _Nonnull)parseVideos:(id _Nullable)apiResponse; -+ (NSArray * _Nonnull)parseContextDataValues:(id _Nullable)apiResponse; -+ (NSArray * _Nonnull)parseBadges:(id _Nullable)apiResponse; -+ (NSArray * _Nonnull)parseSecondaryRatings:(id _Nullable)apiResponse; -+ (TagDimensions _Nullable)parseTagDimension:(id _Nullable)apiResponse; ++ (nullable NSDate *)convertTimestampToDatetime:(nullable id)timestamp; ++ (nonnull NSArray *)parsePhotos:(nullable id)apiResponse; ++ (nonnull NSArray *)parseVideos:(nullable id)apiResponse; ++ (nonnull NSArray *)parseContextDataValues: + (nullable id)apiResponse; ++ (nonnull NSArray *)parseBadges:(nullable id)apiResponse; ++ (nonnull NSArray *)parseSecondaryRatings: + (nullable id)apiResponse; ++ (nullable TagDimensions)parseTagDimension:(nullable id)apiResponse; @end - - - diff --git a/Pod/BVConversations/Display/Model/BVModelUtil.m b/Pod/BVConversations/Display/Model/BVModelUtil.m index 55004294..6afe52cc 100644 --- a/Pod/BVConversations/Display/Model/BVModelUtil.m +++ b/Pod/BVConversations/Display/Model/BVModelUtil.m @@ -9,86 +9,75 @@ @implementation BVModelUtil ++ (nullable NSDate *)convertTimestampToDatetime:(nullable id)timestamp { + if (timestamp == nil || ![timestamp isKindOfClass:[NSString class]]) { + return nil; + } + NSString *timestampString = timestamp; -+ (NSDate * _Nullable)convertTimestampToDatetime:(id _Nullable)timestamp { - - if (timestamp == nil || ![timestamp isKindOfClass:[NSString class]]) { - return nil; - } - NSString* timestampString = timestamp; - - NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSX"]; // UTC format - return [dateFormatter dateFromString:timestampString]; - + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSX"]; // UTC format + return [dateFormatter dateFromString:timestampString]; } ++ (nonnull NSArray *)parsePhotos:(nullable id)apiResponse { + NSMutableArray *tempValues = [NSMutableArray array]; -+ (NSArray * _Nonnull)parsePhotos:(id _Nullable)apiResponse { - - NSMutableArray* tempValues = [NSMutableArray array]; - - for(NSDictionary* photo in apiResponse) { - [tempValues addObject:[[BVPhoto alloc] initWithApiResponse:photo]]; - } - return tempValues; - + for (NSDictionary *photo in apiResponse) { + [tempValues addObject:[[BVPhoto alloc] initWithApiResponse:photo]]; + } + return tempValues; } -+ (NSArray * _Nonnull)parseVideos:(id _Nullable)apiResponse { - - NSMutableArray* tempValues = [NSMutableArray array]; - - for(NSDictionary* photo in apiResponse) { - [tempValues addObject:[[BVVideo alloc] initWithApiResponse:photo]]; - } - return tempValues; - ++ (nonnull NSArray *)parseVideos:(nullable id)apiResponse { + NSMutableArray *tempValues = [NSMutableArray array]; + + for (NSDictionary *photo in apiResponse) { + [tempValues addObject:[[BVVideo alloc] initWithApiResponse:photo]]; + } + return tempValues; } -+ (NSArray * _Nonnull)parseContextDataValues:(id _Nullable)apiResponse { - - NSMutableArray* tempValues = [NSMutableArray array]; - NSDictionary* apiObject = apiResponse; - for(NSString* key in apiObject){ - NSDictionary* value = [apiObject objectForKey:key]; - [tempValues addObject:[[BVContextDataValue alloc] initWithApiResponse:value]]; - } - return tempValues; - ++ (nonnull NSArray *)parseContextDataValues: + (nullable id)apiResponse { + NSMutableArray *tempValues = [NSMutableArray array]; + NSDictionary *apiObject = apiResponse; + for (NSString *key in apiObject) { + NSDictionary *value = [apiObject objectForKey:key]; + [tempValues + addObject:[[BVContextDataValue alloc] initWithApiResponse:value]]; + } + return tempValues; } -+ (NSArray * _Nonnull)parseBadges:(id _Nullable)apiResponse { - - NSMutableArray* tempValues = [NSMutableArray array]; - NSDictionary* apiObject = apiResponse; - for(NSString* key in apiObject){ - NSDictionary* value = [apiObject objectForKey:key]; - [tempValues addObject:[[BVBadge alloc] initWithApiResponse:value]]; - } - return tempValues; - ++ (nonnull NSArray *)parseBadges:(nullable id)apiResponse { + NSMutableArray *tempValues = [NSMutableArray array]; + NSDictionary *apiObject = apiResponse; + for (NSString *key in apiObject) { + NSDictionary *value = [apiObject objectForKey:key]; + [tempValues addObject:[[BVBadge alloc] initWithApiResponse:value]]; + } + return tempValues; } -+ (NSArray * _Nonnull)parseSecondaryRatings:(id _Nullable)apiResponse { - - NSMutableArray* tempValues = [NSMutableArray array]; - NSDictionary* apiObject = apiResponse; - for(NSString* key in apiObject){ - NSDictionary* value = [apiObject objectForKey:key]; - [tempValues addObject:[[BVSecondaryRating alloc] initWithApiResponse:value]]; - } - return tempValues; - ++ (nonnull NSArray *)parseSecondaryRatings: + (nullable id)apiResponse { + NSMutableArray *tempValues = [NSMutableArray array]; + NSDictionary *apiObject = apiResponse; + for (NSString *key in apiObject) { + NSDictionary *value = [apiObject objectForKey:key]; + [tempValues + addObject:[[BVSecondaryRating alloc] initWithApiResponse:value]]; + } + return tempValues; } -+ (TagDimensions _Nullable)parseTagDimension:(id _Nullable)apiResponse { - - TagDimensions tempResult = [NSMutableDictionary dictionary]; - NSDictionary* apiObject = apiResponse; - for(NSString* key in apiObject){ - NSDictionary* value = [apiObject objectForKey:key]; - [tempResult setObject:[[BVDimensionElement alloc] initWithApiResponse:value] forKey:key]; - } - return tempResult; - ++ (nullable TagDimensions)parseTagDimension:(nullable id)apiResponse { + TagDimensions tempResult = [NSMutableDictionary dictionary]; + NSDictionary *apiObject = apiResponse; + for (NSString *key in apiObject) { + NSDictionary *value = [apiObject objectForKey:key]; + [tempResult setObject:[[BVDimensionElement alloc] initWithApiResponse:value] + forKey:key]; + } + return tempResult; } @end diff --git a/Pod/BVConversations/Display/Model/BVPhoto.h b/Pod/BVConversations/Display/Model/BVPhoto.h index 03b264a4..6dcfd1b9 100644 --- a/Pod/BVConversations/Display/Model/BVPhoto.h +++ b/Pod/BVConversations/Display/Model/BVPhoto.h @@ -5,18 +5,19 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVPhotoSizes.h" +#import /* - A photo attached to a review, question, or answer. Check the `sizes` property for thumbnail URLs and normal URLs. + A photo attached to a review, question, or answer. Check the `sizes` property + for thumbnail URLs and normal URLs. */ @interface BVPhoto : NSObject -@property NSString* _Nullable caption; -@property BVPhotoSizes* _Nullable sizes; -@property NSString* _Nullable identifier; +@property(nullable) NSString *caption; +@property(nullable) BVPhotoSizes *sizes; +@property(nullable) NSString *identifier; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVPhoto.m b/Pod/BVConversations/Display/Model/BVPhoto.m index eb54f185..e088f58d 100644 --- a/Pod/BVConversations/Display/Model/BVPhoto.m +++ b/Pod/BVConversations/Display/Model/BVPhoto.m @@ -10,19 +10,19 @@ @implementation BVPhoto --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - self = [super init]; - if(self){ - - if (apiResponse == nil) { - return nil; - } - NSDictionary* apiObject = apiResponse; - SET_IF_NOT_NULL(self.caption, apiObject[@"Caption"]) - SET_IF_NOT_NULL(self.identifier, apiObject[@"Id"]) - self.sizes = [[BVPhotoSizes alloc] initWithApiResponse:apiObject[@"Sizes"]]; +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil) { + return nil; } - return self; + NSDictionary *apiObject = apiResponse; + SET_IF_NOT_NULL(self.caption, apiObject[@"Caption"]) + SET_IF_NOT_NULL(self.identifier, apiObject[@"Id"]) + self.sizes = [[BVPhotoSizes alloc] initWithApiResponse:apiObject[@"Sizes"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVPhotoSizes.h b/Pod/BVConversations/Display/Model/BVPhotoSizes.h index b3566067..a30bf6ea 100644 --- a/Pod/BVConversations/Display/Model/BVPhotoSizes.h +++ b/Pod/BVConversations/Display/Model/BVPhotoSizes.h @@ -12,9 +12,9 @@ */ @interface BVPhotoSizes : NSObject -@property NSString* _Nullable thumbnailUrl; -@property NSString* _Nullable normalUrl; +@property(nullable) NSString *thumbnailUrl; +@property(nullable) NSString *normalUrl; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVPhotoSizes.m b/Pod/BVConversations/Display/Model/BVPhotoSizes.m index 483bc264..c9804023 100644 --- a/Pod/BVConversations/Display/Model/BVPhotoSizes.m +++ b/Pod/BVConversations/Display/Model/BVPhotoSizes.m @@ -10,25 +10,25 @@ @implementation BVPhotoSizes --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - - self = [super init]; - if(self){ - NSDictionary*>* apiObject = apiResponse; - if (apiObject == nil){ - return nil; - } - - if (!isObjectNilOrNull(apiObject[@"thumbnail"][@"Url"])){ - self.thumbnailUrl = apiObject[@"thumbnail"][@"Url"]; - } - - if (!isObjectNilOrNull(apiObject[@"normal"][@"Url"])){ - self.normalUrl = apiObject[@"normal"][@"Url"]; - } - +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + + self = [super init]; + if (self) { + NSDictionary *> + *apiObject = apiResponse; + if (apiObject == nil) { + return nil; + } + + if (!isObjectNilOrNull(apiObject[@"thumbnail"][@"Url"])) { + self.thumbnailUrl = apiObject[@"thumbnail"][@"Url"]; + } + + if (!isObjectNilOrNull(apiObject[@"normal"][@"Url"])) { + self.normalUrl = apiObject[@"normal"][@"Url"]; } - return self; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVProductStatistics.h b/Pod/BVConversations/Display/Model/BVProductStatistics.h index bb0e89e9..2c46a503 100644 --- a/Pod/BVConversations/Display/Model/BVProductStatistics.h +++ b/Pod/BVConversations/Display/Model/BVProductStatistics.h @@ -5,18 +5,18 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVReviewStatistic.h" +#import /* Statistics about a product - included in `BVBulkRatingsResponse`. */ @interface BVProductStatistics : NSObject -@property NSString* _Nullable productId; -@property BVReviewStatistic* _Nullable reviewStatistics; -@property BVReviewStatistic* _Nullable nativeReviewStatistics; +@property(nullable) NSString *productId; +@property(nullable) BVReviewStatistic *reviewStatistics; +@property(nullable) BVReviewStatistic *nativeReviewStatistics; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVProductStatistics.m b/Pod/BVConversations/Display/Model/BVProductStatistics.m index 9c0065df..c7a04b70 100644 --- a/Pod/BVConversations/Display/Model/BVProductStatistics.m +++ b/Pod/BVConversations/Display/Model/BVProductStatistics.m @@ -10,18 +10,19 @@ @implementation BVProductStatistics --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - - self = [super init]; - if(self){ - NSDictionary* productStatistics = apiResponse[@"ProductStatistics"]; - - SET_IF_NOT_NULL(self.productId, productStatistics[@"ProductId"]) - self.reviewStatistics = [[BVReviewStatistic alloc] initWithApiResponse:productStatistics[@"ReviewStatistics"]]; - self.nativeReviewStatistics = [[BVReviewStatistic alloc] initWithApiResponse:productStatistics[@"NativeReviewStatistics"]]; - } - return self; - +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + + self = [super init]; + if (self) { + NSDictionary *productStatistics = apiResponse[@"ProductStatistics"]; + + SET_IF_NOT_NULL(self.productId, productStatistics[@"ProductId"]) + self.reviewStatistics = [[BVReviewStatistic alloc] + initWithApiResponse:productStatistics[@"ReviewStatistics"]]; + self.nativeReviewStatistics = [[BVReviewStatistic alloc] + initWithApiResponse:productStatistics[@"NativeReviewStatistics"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVProductsResponse.h b/Pod/BVConversations/Display/Model/BVProductsResponse.h index 71ba032a..542da28a 100644 --- a/Pod/BVConversations/Display/Model/BVProductsResponse.h +++ b/Pod/BVConversations/Display/Model/BVProductsResponse.h @@ -5,15 +5,17 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVResponse.h" -#import "BVProduct.h" #import "BVBaseConversationsResponse.h" +#import "BVProduct.h" +#import "BVResponse.h" +#import /* - A response to a `BVProductDisplayPageRequest`. Contains one `BVProduct` in the `result` object. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVProductDisplayPageRequest`. Contains one `BVProduct` in the + `result` object. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). */ -@interface BVProductsResponse: BVBaseConversationsResultResponse +@interface BVProductsResponse : BVBaseConversationsResultResponse @end diff --git a/Pod/BVConversations/Display/Model/BVProductsResponse.m b/Pod/BVConversations/Display/Model/BVProductsResponse.m index 045062b0..3269cb5c 100644 --- a/Pod/BVConversations/Display/Model/BVProductsResponse.m +++ b/Pod/BVConversations/Display/Model/BVProductsResponse.m @@ -6,14 +6,15 @@ // #import "BVProductsResponse.h" -#import "BVProduct.h" #import "BVConversationsInclude.h" #import "BVNullHelper.h" +#import "BVProduct.h" @implementation BVProductsResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVProduct alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVProduct alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVQAStatistics.h b/Pod/BVConversations/Display/Model/BVQAStatistics.h index 41e1e323..02da218e 100644 --- a/Pod/BVConversations/Display/Model/BVQAStatistics.h +++ b/Pod/BVConversations/Display/Model/BVQAStatistics.h @@ -5,35 +5,35 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVDimensionAndDistributionUtil.h" +#import /* Statistics about the Questions and Answers for a product. */ @interface BVQAStatistics : NSObject -@property NSNumber* _Nullable helpfulVoteCount; -@property NSNumber* _Nullable bestAnswerCount; -@property NSNumber* _Nullable totalAnswerCount; -@property NSNumber* _Nullable totalQuestionCount; -@property NSNumber* _Nullable featuredQuestionCount; -@property NSNumber* _Nullable featuredAnswerCount; +@property(nullable) NSNumber *helpfulVoteCount; +@property(nullable) NSNumber *bestAnswerCount; +@property(nullable) NSNumber *totalAnswerCount; +@property(nullable) NSNumber *totalQuestionCount; +@property(nullable) NSNumber *featuredQuestionCount; +@property(nullable) NSNumber *featuredAnswerCount; -@property NSNumber* _Nullable questionHelpfulVoteCount; -@property NSNumber* _Nullable answerNotHelpfulVoteCount; -@property NSNumber* _Nullable questionNotHelpfulVoteCount; -@property NSNumber* _Nullable answerHelpfulVoteCount; +@property(nullable) NSNumber *questionHelpfulVoteCount; +@property(nullable) NSNumber *answerNotHelpfulVoteCount; +@property(nullable) NSNumber *questionNotHelpfulVoteCount; +@property(nullable) NSNumber *answerHelpfulVoteCount; -@property TagDistribution _Nullable tagDistribution; -@property ContextDataDistribution _Nullable contextDataDistribution; +@property(nullable) TagDistribution tagDistribution; +@property(nullable) ContextDataDistribution contextDataDistribution; -@property NSDate* _Nullable firstQuestionTime; -@property NSDate* _Nullable lastQuestionTime; -@property NSDate* _Nullable firstAnswerTime; -@property NSDate* _Nullable lastAnswerTime; -@property NSDate* _Nullable lastQuestionAnswerTime; +@property(nullable) NSDate *firstQuestionTime; +@property(nullable) NSDate *lastQuestionTime; +@property(nullable) NSDate *firstAnswerTime; +@property(nullable) NSDate *lastAnswerTime; +@property(nullable) NSDate *lastQuestionAnswerTime; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVQAStatistics.m b/Pod/BVConversations/Display/Model/BVQAStatistics.m index 196b0059..8f627bfb 100644 --- a/Pod/BVConversations/Display/Model/BVQAStatistics.m +++ b/Pod/BVConversations/Display/Model/BVQAStatistics.m @@ -12,38 +12,49 @@ @implementation BVQAStatistics --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - - self = [super init]; - if(self){ - if (apiResponse == nil){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.helpfulVoteCount, apiObject[@"HelpfulVoteCount"]) - SET_IF_NOT_NULL(self.bestAnswerCount, apiObject[@"BestAnswerCount"]) - SET_IF_NOT_NULL(self.questionHelpfulVoteCount, apiObject[@"QuestionHelpfulVoteCount"]) - SET_IF_NOT_NULL(self.totalAnswerCount, apiObject[@"TotalAnswerCount"]) - SET_IF_NOT_NULL(self.answerNotHelpfulVoteCount, apiObject[@"AnswerNotHelpfulVoteCount"]) - SET_IF_NOT_NULL(self.totalQuestionCount, apiObject[@"TotalQuestionCount"]) - SET_IF_NOT_NULL(self.questionNotHelpfulVoteCount, apiObject[@"QuestionNotHelpfulVoteCount"]) - SET_IF_NOT_NULL(self.featuredQuestionCount, apiObject[@"FeaturedQuestionCount"]) - SET_IF_NOT_NULL(self.featuredAnswerCount, apiObject[@"FeaturedAnswerCount"]) - SET_IF_NOT_NULL(self.answerHelpfulVoteCount, apiObject[@"AnswerHelpfulVoteCount"]) - - self.tagDistribution = [BVDimensionAndDistributionUtil createDistributionWithApiResponse:apiObject[@"TagDistribution"]]; - self.contextDataDistribution = [BVDimensionAndDistributionUtil createDistributionWithApiResponse:apiObject[@"ContextDataDistribution"]]; - self.firstQuestionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"FirstQuestionTime"]]; - self.lastQuestionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"LastQuestionTime"]]; - self.firstAnswerTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"FirstAnswerTime"]]; - self.lastAnswerTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"LastAnswerTime"]]; - self.lastQuestionAnswerTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"LastQuestionAnswerTime"]]; +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + if (apiResponse == nil) { + return nil; } - return self; - + + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.helpfulVoteCount, apiObject[@"HelpfulVoteCount"]) + SET_IF_NOT_NULL(self.bestAnswerCount, apiObject[@"BestAnswerCount"]) + SET_IF_NOT_NULL(self.questionHelpfulVoteCount, + apiObject[@"QuestionHelpfulVoteCount"]) + SET_IF_NOT_NULL(self.totalAnswerCount, apiObject[@"TotalAnswerCount"]) + SET_IF_NOT_NULL(self.answerNotHelpfulVoteCount, + apiObject[@"AnswerNotHelpfulVoteCount"]) + SET_IF_NOT_NULL(self.totalQuestionCount, apiObject[@"TotalQuestionCount"]) + SET_IF_NOT_NULL(self.questionNotHelpfulVoteCount, + apiObject[@"QuestionNotHelpfulVoteCount"]) + SET_IF_NOT_NULL(self.featuredQuestionCount, + apiObject[@"FeaturedQuestionCount"]) + SET_IF_NOT_NULL(self.featuredAnswerCount, apiObject[@"FeaturedAnswerCount"]) + SET_IF_NOT_NULL(self.answerHelpfulVoteCount, + apiObject[@"AnswerHelpfulVoteCount"]) + + self.tagDistribution = [BVDimensionAndDistributionUtil + createDistributionWithApiResponse:apiObject[@"TagDistribution"]]; + self.contextDataDistribution = [BVDimensionAndDistributionUtil + createDistributionWithApiResponse:apiObject + [@"ContextDataDistribution"]]; + self.firstQuestionTime = [BVModelUtil + convertTimestampToDatetime:apiObject[@"FirstQuestionTime"]]; + self.lastQuestionTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"LastQuestionTime"]]; + self.firstAnswerTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"FirstAnswerTime"]]; + self.lastAnswerTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"LastAnswerTime"]]; + self.lastQuestionAnswerTime = [BVModelUtil + convertTimestampToDatetime:apiObject[@"LastQuestionAnswerTime"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.h b/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.h index 79956b47..fab669d9 100644 --- a/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.h +++ b/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.h @@ -5,15 +5,18 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVResponse.h" -#import "BVQuestion.h" #import "BVBaseConversationsResponse.h" +#import "BVQuestion.h" +#import "BVResponse.h" +#import /* - A response to a `BVQuestionsAndAnswersRequest`. Contains one or multiple (up to 20) `BVQuestion` objects in the `results` array. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVQuestionsAndAnswersRequest`. Contains one or multiple (up to + 20) `BVQuestion` objects in the `results` array. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). */ -@interface BVQuestionsAndAnswersResponse : BVBaseConversationsResultsResponse +@interface BVQuestionsAndAnswersResponse + : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.m b/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.m index 939df5f1..f0a16346 100644 --- a/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.m +++ b/Pod/BVConversations/Display/Model/BVQuestionsAndAnswersResponse.m @@ -7,13 +7,14 @@ #import "BVQuestionsAndAnswersResponse.h" #import "BVConversationsInclude.h" -#import "BVQuestion.h" #import "BVNullHelper.h" +#import "BVQuestion.h" @implementation BVQuestionsAndAnswersResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVQuestion alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVQuestion alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVRatingDistribution.h b/Pod/BVConversations/Display/Model/BVRatingDistribution.h index 9aaf14c7..48317644 100644 --- a/Pod/BVConversations/Display/Model/BVRatingDistribution.h +++ b/Pod/BVConversations/Display/Model/BVRatingDistribution.h @@ -8,16 +8,17 @@ #import /* - The number of ratings for each star count. For example, 12 one-star reviews, 53 five-star reviews. + The number of ratings for each star count. For example, 12 one-star reviews, 53 + five-star reviews. */ @interface BVRatingDistribution : NSObject -@property NSNumber* _Nonnull oneStarCount; -@property NSNumber* _Nonnull twoStarCount; -@property NSNumber* _Nonnull threeStarCount; -@property NSNumber* _Nonnull fourStarCount; -@property NSNumber* _Nonnull fiveStarCount; +@property(nonnull) NSNumber *oneStarCount; +@property(nonnull) NSNumber *twoStarCount; +@property(nonnull) NSNumber *threeStarCount; +@property(nonnull) NSNumber *fourStarCount; +@property(nonnull) NSNumber *fiveStarCount; --(id _Nullable)initWithApiResponse:(id _Nullable)apiRepsonse; +- (nullable id)initWithApiResponse:(nullable id)apiRepsonse; @end diff --git a/Pod/BVConversations/Display/Model/BVRatingDistribution.m b/Pod/BVConversations/Display/Model/BVRatingDistribution.m index 1874302c..e5bc5b13 100644 --- a/Pod/BVConversations/Display/Model/BVRatingDistribution.m +++ b/Pod/BVConversations/Display/Model/BVRatingDistribution.m @@ -9,41 +9,40 @@ @implementation BVRatingDistribution --(id _Nullable)initWithApiResponse:(id _Nullable)apiRepsonse { - - self = [super init]; - if(self){ - if (apiRepsonse == nil || ![apiRepsonse isKindOfClass:[NSArray class]]) { - return nil; - } - - NSArray* apiObject = apiRepsonse; - for(NSDictionary* value in apiObject) { - NSNumber* count = value[@"Count"]; - NSNumber* valueNum = value[@"RatingValue"]; - - int valueInt = [valueNum intValue]; - switch (valueInt) { - case 1: - self.oneStarCount = count; - break; - case 2: - self.twoStarCount = count; - break; - case 3: - self.threeStarCount = count; - break; - case 4: - self.fourStarCount = count; - break; - default: - self.fiveStarCount = count; - break; - } - } +- (nullable id)initWithApiResponse:(nullable id)apiRepsonse { + + self = [super init]; + if (self) { + if (apiRepsonse == nil || ![apiRepsonse isKindOfClass:[NSArray class]]) { + return nil; + } + + NSArray *apiObject = apiRepsonse; + for (NSDictionary *value in apiObject) { + NSNumber *count = value[@"Count"]; + NSNumber *valueNum = value[@"RatingValue"]; + + int valueInt = [valueNum intValue]; + switch (valueInt) { + case 1: + self.oneStarCount = count; + break; + case 2: + self.twoStarCount = count; + break; + case 3: + self.threeStarCount = count; + break; + case 4: + self.fourStarCount = count; + break; + default: + self.fiveStarCount = count; + break; + } } - return self; - + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVResponse.h b/Pod/BVConversations/Display/Model/BVResponse.h index 7145bd22..87043369 100644 --- a/Pod/BVConversations/Display/Model/BVResponse.h +++ b/Pod/BVConversations/Display/Model/BVResponse.h @@ -8,6 +8,6 @@ /// Internal protocol - used only within BVSDK @protocol BVResponse --(instancetype _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull instancetype)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVReviewStatistic.h b/Pod/BVConversations/Display/Model/BVReviewStatistic.h index 305c1fff..2613b648 100644 --- a/Pod/BVConversations/Display/Model/BVReviewStatistic.h +++ b/Pod/BVConversations/Display/Model/BVReviewStatistic.h @@ -8,14 +8,14 @@ #import /* - A set of statistics about reviews. Used in `BVProductStatistics`. + A set of statistics about reviews. Used in `BVProductStatistics`. */ @interface BVReviewStatistic : NSObject -@property NSNumber* _Nullable totalReviewCount; -@property NSNumber* _Nullable averageOverallRating; -@property NSNumber* _Nullable overallRatingRange; +@property(nullable) NSNumber *totalReviewCount; +@property(nullable) NSNumber *averageOverallRating; +@property(nullable) NSNumber *overallRatingRange; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVReviewStatistic.m b/Pod/BVConversations/Display/Model/BVReviewStatistic.m index 29396bbb..2d0f0ddc 100644 --- a/Pod/BVConversations/Display/Model/BVReviewStatistic.m +++ b/Pod/BVConversations/Display/Model/BVReviewStatistic.m @@ -5,39 +5,40 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // - #import "BVReviewStatistic.h" #import "BVNullHelper.h" @implementation BVReviewStatistic --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - - self = [super init]; - if(self) { - - if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]] ) { - return nil; - } - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.totalReviewCount, apiObject[@"TotalReviewCount"]) - SET_IF_NOT_NULL(self.averageOverallRating, apiObject[@"AverageOverallRating"]) - SET_IF_NOT_NULL(self.overallRatingRange, apiObject[@"OverallRatingRange"]) - - if (!self.totalReviewCount) { - self.totalReviewCount = [NSNumber numberWithInt:0]; - } - - if (!self.averageOverallRating) { - self.averageOverallRating = [NSNumber numberWithInt:0]; - } - - if (!self.overallRatingRange) { - self.overallRatingRange = [NSNumber numberWithInt:0]; - } +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; + } + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.totalReviewCount, apiObject[@"TotalReviewCount"]) + SET_IF_NOT_NULL(self.averageOverallRating, + apiObject[@"AverageOverallRating"]) + SET_IF_NOT_NULL(self.overallRatingRange, apiObject[@"OverallRatingRange"]) + + if (!self.totalReviewCount) { + self.totalReviewCount = [NSNumber numberWithInt:0]; + } + + if (!self.averageOverallRating) { + self.averageOverallRating = [NSNumber numberWithInt:0]; + } + + if (!self.overallRatingRange) { + self.overallRatingRange = [NSNumber numberWithInt:0]; } - return self; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVReviewStatistics.h b/Pod/BVConversations/Display/Model/BVReviewStatistics.h index a9e907d8..be860110 100644 --- a/Pod/BVConversations/Display/Model/BVReviewStatistics.h +++ b/Pod/BVConversations/Display/Model/BVReviewStatistics.h @@ -5,35 +5,35 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSecondaryRatingsAverages.h" -#import "BVRatingDistribution.h" #import "BVDimensionAndDistributionUtil.h" +#import "BVRatingDistribution.h" +#import "BVSecondaryRatingsAverages.h" +#import /* Statistics about the reviews about a product. */ @interface BVReviewStatistics : NSObject -@property NSNumber* _Nullable helpfulVoteCount; -@property NSNumber* _Nullable notRecommendedCount; -@property NSNumber* _Nullable averageOverallRating; -@property NSNumber* _Nullable featuredReviewCount; -@property NSNumber* _Nullable notHelpfulVoteCount; -@property NSNumber* _Nullable overallRatingRange; -@property NSNumber* _Nullable totalReviewCount; -@property NSNumber* _Nullable ratingsOnlyReviewCount; -@property NSNumber* _Nullable recommendedCount; +@property(nullable) NSNumber *helpfulVoteCount; +@property(nullable) NSNumber *notRecommendedCount; +@property(nullable) NSNumber *averageOverallRating; +@property(nullable) NSNumber *featuredReviewCount; +@property(nullable) NSNumber *notHelpfulVoteCount; +@property(nullable) NSNumber *overallRatingRange; +@property(nullable) NSNumber *totalReviewCount; +@property(nullable) NSNumber *ratingsOnlyReviewCount; +@property(nullable) NSNumber *recommendedCount; -@property BVSecondaryRatingsAverages* _Nullable secondaryRatingsAverages; -@property BVRatingDistribution* _Nullable ratingDistribution; +@property(nullable) BVSecondaryRatingsAverages *secondaryRatingsAverages; +@property(nullable) BVRatingDistribution *ratingDistribution; -@property TagDistribution _Nullable tagDistribution; -@property ContextDataDistribution _Nullable contextDataDistribution; +@property(nullable) TagDistribution tagDistribution; +@property(nullable) ContextDataDistribution contextDataDistribution; -@property NSDate* _Nullable firstSubmissionTime; -@property NSDate* _Nullable lastSubmissionTime; +@property(nullable) NSDate *firstSubmissionTime; +@property(nullable) NSDate *lastSubmissionTime; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVReviewStatistics.m b/Pod/BVConversations/Display/Model/BVReviewStatistics.m index af94f7f4..7ec12e20 100644 --- a/Pod/BVConversations/Display/Model/BVReviewStatistics.m +++ b/Pod/BVConversations/Display/Model/BVReviewStatistics.m @@ -11,36 +11,45 @@ @implementation BVReviewStatistics --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - self = [super init]; - if(self) { - if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.helpfulVoteCount, apiObject[@"HelpfulVoteCount"]) - SET_IF_NOT_NULL(self.notRecommendedCount, apiObject[@"NotRecommendedCount"]) - SET_IF_NOT_NULL(self.averageOverallRating, apiObject[@"AverageOverallRating"]) - SET_IF_NOT_NULL(self.featuredReviewCount, apiObject[@"FeaturedReviewCount"]) - SET_IF_NOT_NULL(self.notHelpfulVoteCount, apiObject[@"NotHelpfulVoteCount"]) - SET_IF_NOT_NULL(self.overallRatingRange, apiObject[@"OverallRatingRange"]) - SET_IF_NOT_NULL(self.totalReviewCount, apiObject[@"TotalReviewCount"]) - SET_IF_NOT_NULL(self.ratingsOnlyReviewCount, apiObject[@"RatingsOnlyReviewCount"]) - SET_IF_NOT_NULL(self.recommendedCount, apiObject[@"RecommendedCount"]) - - self.secondaryRatingsAverages = [BVSecondaryRatingsAverages createWithDictionary:apiObject[@"SecondaryRatingsAverages"]]; - self.tagDistribution = [BVDimensionAndDistributionUtil createDistributionWithApiResponse:apiObject[@"TagDistribution"]]; - self.contextDataDistribution = [BVDimensionAndDistributionUtil createDistributionWithApiResponse:apiObject[@"ContextDataDistribution"]]; - - self.firstSubmissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"FirstSubmissionTime"]]; - self.lastSubmissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"LastSubmissionTime"]]; - - self.ratingDistribution = [[BVRatingDistribution alloc] initWithApiResponse:apiObject[@"RatingDistribution"]]; - +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; + + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.helpfulVoteCount, apiObject[@"HelpfulVoteCount"]) + SET_IF_NOT_NULL(self.notRecommendedCount, apiObject[@"NotRecommendedCount"]) + SET_IF_NOT_NULL(self.averageOverallRating, + apiObject[@"AverageOverallRating"]) + SET_IF_NOT_NULL(self.featuredReviewCount, apiObject[@"FeaturedReviewCount"]) + SET_IF_NOT_NULL(self.notHelpfulVoteCount, apiObject[@"NotHelpfulVoteCount"]) + SET_IF_NOT_NULL(self.overallRatingRange, apiObject[@"OverallRatingRange"]) + SET_IF_NOT_NULL(self.totalReviewCount, apiObject[@"TotalReviewCount"]) + SET_IF_NOT_NULL(self.ratingsOnlyReviewCount, + apiObject[@"RatingsOnlyReviewCount"]) + SET_IF_NOT_NULL(self.recommendedCount, apiObject[@"RecommendedCount"]) + + self.secondaryRatingsAverages = [BVSecondaryRatingsAverages + createWithDictionary:apiObject[@"SecondaryRatingsAverages"]]; + self.tagDistribution = [BVDimensionAndDistributionUtil + createDistributionWithApiResponse:apiObject[@"TagDistribution"]]; + self.contextDataDistribution = [BVDimensionAndDistributionUtil + createDistributionWithApiResponse:apiObject + [@"ContextDataDistribution"]]; + + self.firstSubmissionTime = [BVModelUtil + convertTimestampToDatetime:apiObject[@"FirstSubmissionTime"]]; + self.lastSubmissionTime = [BVModelUtil + convertTimestampToDatetime:apiObject[@"LastSubmissionTime"]]; + + self.ratingDistribution = [[BVRatingDistribution alloc] + initWithApiResponse:apiObject[@"RatingDistribution"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVReviewsResponse.h b/Pod/BVConversations/Display/Model/BVReviewsResponse.h index 338a5d47..8737382c 100644 --- a/Pod/BVConversations/Display/Model/BVReviewsResponse.h +++ b/Pod/BVConversations/Display/Model/BVReviewsResponse.h @@ -5,15 +5,17 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import +#import "BVBaseConversationsResponse.h" #import "BVResponse.h" #import "BVReview.h" -#import "BVBaseConversationsResponse.h" +#import /* - A response to a `BVReviewsResponse`. Contains one or multiple (up to 20) `BVReview` objects in the `results` array. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVReviewsResponse`. Contains one or multiple (up to 20) + `BVReview` objects in the `results` array. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). */ -@interface BVReviewsResponse : BVBaseConversationsResultsResponse +@interface BVReviewsResponse : BVBaseConversationsResultsResponse @end diff --git a/Pod/BVConversations/Display/Model/BVReviewsResponse.m b/Pod/BVConversations/Display/Model/BVReviewsResponse.m index 67944040..187e6238 100644 --- a/Pod/BVConversations/Display/Model/BVReviewsResponse.m +++ b/Pod/BVConversations/Display/Model/BVReviewsResponse.m @@ -6,14 +6,15 @@ // #import "BVReviewsResponse.h" -#import "BVReview.h" #import "BVConversationsInclude.h" #import "BVNullHelper.h" +#import "BVReview.h" @implementation BVReviewsResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVReview alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVReview alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/BVSecondaryRating.h b/Pod/BVConversations/Display/Model/BVSecondaryRating.h index df7842a9..87edd160 100644 --- a/Pod/BVConversations/Display/Model/BVSecondaryRating.h +++ b/Pod/BVConversations/Display/Model/BVSecondaryRating.h @@ -9,19 +9,20 @@ /* A secondary rating value associated with a review. - The definition of your secondary ratings (if any) are specific to your configuration. + The definition of your secondary ratings (if any) are specific to your + configuration. */ @interface BVSecondaryRating : NSObject -@property NSNumber* _Nullable value; -@property NSNumber* _Nullable valueRange; -@property NSString* _Nullable valueLabel; -@property NSString* _Nullable maxLabel; -@property NSString* _Nullable minLabel; -@property NSString* _Nullable label; -@property NSString* _Nullable displayType; -@property NSString* _Nullable identifier; +@property(nullable) NSNumber *value; +@property(nullable) NSNumber *valueRange; +@property(nullable) NSString *valueLabel; +@property(nullable) NSString *maxLabel; +@property(nullable) NSString *minLabel; +@property(nullable) NSString *label; +@property(nullable) NSString *displayType; +@property(nullable) NSString *identifier; --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVSecondaryRating.m b/Pod/BVConversations/Display/Model/BVSecondaryRating.m index 5f3a1011..d36d6be6 100644 --- a/Pod/BVConversations/Display/Model/BVSecondaryRating.m +++ b/Pod/BVConversations/Display/Model/BVSecondaryRating.m @@ -10,19 +10,19 @@ @implementation BVSecondaryRating --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse { - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) - SET_IF_NOT_NULL(self.valueLabel, apiResponse[@"ValueLabel"]) - SET_IF_NOT_NULL(self.maxLabel, apiResponse[@"MaxLabel"]) - SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.valueRange, apiResponse[@"ValueRange"]) - SET_IF_NOT_NULL(self.minLabel, apiResponse[@"MinLabel"]) - SET_IF_NOT_NULL(self.displayType, apiResponse[@"DisplayType"]) - } - return self; +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.value, apiResponse[@"Value"]) + SET_IF_NOT_NULL(self.valueLabel, apiResponse[@"ValueLabel"]) + SET_IF_NOT_NULL(self.maxLabel, apiResponse[@"MaxLabel"]) + SET_IF_NOT_NULL(self.label, apiResponse[@"Label"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.valueRange, apiResponse[@"ValueRange"]) + SET_IF_NOT_NULL(self.minLabel, apiResponse[@"MinLabel"]) + SET_IF_NOT_NULL(self.displayType, apiResponse[@"DisplayType"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.h b/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.h index d67c129c..538f2b1a 100644 --- a/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.h +++ b/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.h @@ -9,6 +9,7 @@ @interface BVSecondaryRatingsAverages : NSDictionary -+(BVSecondaryRatingsAverages* _Nullable)createWithDictionary:(id _Nullable)apiResponse; ++ (nullable BVSecondaryRatingsAverages *)createWithDictionary: + (nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.m b/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.m index 5affb409..659dbe41 100644 --- a/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.m +++ b/Pod/BVConversations/Display/Model/BVSecondaryRatingsAverages.m @@ -10,25 +10,24 @@ @implementation BVSecondaryRatingsAverages -+(BVSecondaryRatingsAverages* _Nullable)createWithDictionary:(id _Nullable)apiResponse { ++ (nullable BVSecondaryRatingsAverages *)createWithDictionary: + (nullable id)apiResponse { + if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; + } + NSMutableDictionary *result = [NSMutableDictionary dictionary]; - if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { - return nil; - } - NSMutableDictionary* result = [NSMutableDictionary dictionary]; - - NSDictionary* apiObject = apiResponse; - - for(NSString* key in apiObject){ - NSDictionary* value = apiObject[key]; - id avgRating = value[@"AverageRating"]; - - if(avgRating != [NSNull null]){ - result[key] = avgRating; - } + NSDictionary *apiObject = apiResponse; + + for (NSString *key in apiObject) { + NSDictionary *value = apiObject[key]; + id avgRating = value[@"AverageRating"]; + + if (avgRating != [NSNull null]) { + result[key] = avgRating; } - return (BVSecondaryRatingsAverages*)result; - + } + return (BVSecondaryRatingsAverages *)result; } @end diff --git a/Pod/BVConversations/Display/Model/BVSyndicationSource.h b/Pod/BVConversations/Display/Model/BVSyndicationSource.h index 70c23f2a..c9ebf068 100644 --- a/Pod/BVConversations/Display/Model/BVSyndicationSource.h +++ b/Pod/BVConversations/Display/Model/BVSyndicationSource.h @@ -7,16 +7,17 @@ #import - /** - If a review is syndicated, a BVSyndicationSource will contain the details of where the syndication is coming from is displayed. NOTE: The API key must be configured to show syndicated content. + If a review is syndicated, a BVSyndicationSource will contain the details of + where the syndication is coming from is displayed. NOTE: The API key must be + configured to show syndicated content. */ @interface BVSyndicationSource : NSObject -@property (readonly) NSString * _Nullable logoImageUrl; -@property (readonly) NSString * _Nullable contentLink; -@property (readonly) NSString * _Nullable name; +@property(nullable, readonly) NSString *logoImageUrl; +@property(nullable, readonly) NSString *contentLink; +@property(nullable, readonly) NSString *name; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVSyndicationSource.m b/Pod/BVConversations/Display/Model/BVSyndicationSource.m index a43548aa..001a0d6c 100644 --- a/Pod/BVConversations/Display/Model/BVSyndicationSource.m +++ b/Pod/BVConversations/Display/Model/BVSyndicationSource.m @@ -10,23 +10,23 @@ @implementation BVSyndicationSource --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - self = [super init]; - if(self) { - if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSDictionary* apiObject = [apiResponse objectForKey:@"SyndicationSource"]; - - if (apiObject){ - SET_IF_NOT_NULL(_name, apiObject[@"Name"]); - SET_IF_NOT_NULL(_contentLink, apiObject[@"ContentLink"]); - SET_IF_NOT_NULL(_logoImageUrl, apiObject[@"LogoImageUrl"]); - } - +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; + + NSDictionary *apiObject = [apiResponse objectForKey:@"SyndicationSource"]; + + if (apiObject) { + SET_IF_NOT_NULL(_name, apiObject[@"Name"]); + SET_IF_NOT_NULL(_contentLink, apiObject[@"ContentLink"]); + SET_IF_NOT_NULL(_logoImageUrl, apiObject[@"LogoImageUrl"]); + } + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/BVVideo.h b/Pod/BVConversations/Display/Model/BVVideo.h index 9c7c8f58..19ef8c08 100644 --- a/Pod/BVConversations/Display/Model/BVVideo.h +++ b/Pod/BVConversations/Display/Model/BVVideo.h @@ -12,13 +12,13 @@ */ @interface BVVideo : NSObject -@property NSString* _Nullable videoHost; -@property NSString* _Nullable caption; -@property NSString* _Nullable videoThumbnailUrl; -@property NSString* _Nullable videoId; -@property NSString* _Nullable videoUrl; -@property NSString* _Nullable videoIframeUrl; +@property(nullable) NSString *videoHost; +@property(nullable) NSString *caption; +@property(nullable) NSString *videoThumbnailUrl; +@property(nullable) NSString *videoId; +@property(nullable) NSString *videoUrl; +@property(nullable) NSString *videoIframeUrl; --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse; +- (nullable id)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/BVVideo.m b/Pod/BVConversations/Display/Model/BVVideo.m index a0942f2e..10aada61 100644 --- a/Pod/BVConversations/Display/Model/BVVideo.m +++ b/Pod/BVConversations/Display/Model/BVVideo.m @@ -10,23 +10,23 @@ @implementation BVVideo --(id _Nullable)initWithApiResponse:(id _Nullable)apiResponse { - self = [super init]; - if(self){ - if (apiResponse == nil){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.videoHost, apiObject[@"VideoHost"]) - SET_IF_NOT_NULL(self.caption, apiObject[@"Caption"]) - SET_IF_NOT_NULL(self.videoThumbnailUrl, apiObject[@"VideoThumbnailUrl"]) - SET_IF_NOT_NULL(self.videoId, apiObject[@"VideoId"]) - SET_IF_NOT_NULL(self.videoUrl, apiObject[@"VideoUrl"]) - SET_IF_NOT_NULL(self.videoIframeUrl, apiObject[@"VideoIframeUrl"]) +- (nullable id)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + if (apiResponse == nil) { + return nil; } - return self; + + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.videoHost, apiObject[@"VideoHost"]) + SET_IF_NOT_NULL(self.caption, apiObject[@"Caption"]) + SET_IF_NOT_NULL(self.videoThumbnailUrl, apiObject[@"VideoThumbnailUrl"]) + SET_IF_NOT_NULL(self.videoId, apiObject[@"VideoId"]) + SET_IF_NOT_NULL(self.videoUrl, apiObject[@"VideoUrl"]) + SET_IF_NOT_NULL(self.videoIframeUrl, apiObject[@"VideoIframeUrl"]) + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.h index fdfeeda3..f64b2994 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVContextDataValue.h" #import "BVBadge.h" +#import "BVContextDataValue.h" #import "BVGenericConversationsResult.h" #import "BVPhoto.h" -#import "BVVideo.h" #import "BVSyndicationSource.h" +#import "BVVideo.h" +#import /* The answer to a question asked by a consumer. @@ -20,33 +20,34 @@ Answer text is included in the `answerText` property. User nickname is included in the `userNickname` property. */ -@interface BVAnswer : NSObject +@interface BVAnswer : NSObject -@property NSString* _Nullable answerText; -@property NSString* _Nullable userNickname; -@property NSString* _Nullable questionId; -@property NSArray* _Nonnull photos; -@property NSArray* _Nonnull contextDataValues; -@property NSArray* _Nonnull videos; -@property NSArray* _Nonnull badges; -@property NSString* _Nullable submissionId; -@property NSString* _Nullable userLocation; -@property NSString* _Nullable authorId; -@property NSNumber* _Nullable isFeatured; -@property NSArray* _Nonnull productRecommendationIds; -@property NSDictionary* _Nullable additionalFields; -@property NSNumber* _Nullable totalFeedbackCount; -@property NSNumber* _Nullable totalPositiveFeedbackCount; -@property NSNumber* _Nullable totalNegativeFeedbackCount; -@property NSString* _Nullable contentLocale; -@property NSString* _Nullable moderationStatus; -@property NSString* _Nullable campaignId; -@property NSString* _Nullable brandImageLogoURL; -@property NSString* _Nullable identifier; -@property NSDate* _Nullable submissionTime; -@property NSDate* _Nullable lastModificationTime; -@property NSDate* _Nullable lastModeratedTime; -@property (readonly) BOOL isSyndicated; -@property (nonatomic, strong, readonly, nullable) BVSyndicationSource* syndicationSource; +@property(nullable) NSString *answerText; +@property(nullable) NSString *userNickname; +@property(nullable) NSString *questionId; +@property(nonnull) NSArray *photos; +@property(nonnull) NSArray *contextDataValues; +@property(nonnull) NSArray *videos; +@property(nonnull) NSArray *badges; +@property(nullable) NSString *submissionId; +@property(nullable) NSString *userLocation; +@property(nullable) NSString *authorId; +@property(nullable) NSNumber *isFeatured; +@property(nonnull) NSArray *productRecommendationIds; +@property(nullable) NSDictionary *additionalFields; +@property(nullable) NSNumber *totalFeedbackCount; +@property(nullable) NSNumber *totalPositiveFeedbackCount; +@property(nullable) NSNumber *totalNegativeFeedbackCount; +@property(nullable) NSString *contentLocale; +@property(nullable) NSString *moderationStatus; +@property(nullable) NSString *campaignId; +@property(nullable) NSString *brandImageLogoURL; +@property(nullable) NSString *identifier; +@property(nullable) NSDate *submissionTime; +@property(nullable) NSDate *lastModificationTime; +@property(nullable) NSDate *lastModeratedTime; +@property(readonly) BOOL isSyndicated; +@property(nullable, nonatomic, strong, readonly) + BVSyndicationSource *syndicationSource; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.m index 93c9bedf..1a3126e7 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAnswer.m @@ -11,46 +11,54 @@ @implementation BVAnswer --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse includes:(BVConversationsInclude* _Nullable)includes { - self = [super init]; - if(self){ - SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) - SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) - SET_IF_NOT_NULL(self.questionId, apiResponse[@"QuestionId"]) - SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) - SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, apiResponse[@"TotalPositiveFeedbackCount"]) - SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) - SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) - SET_IF_NOT_NULL(self.isFeatured, apiResponse[@"IsFeatured"]) - SET_IF_NOT_NULL(self.productRecommendationIds, apiResponse[@"ProductRecommendationIds"]) - SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) - SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) - SET_IF_NOT_NULL(self.brandImageLogoURL, apiResponse[@"BrandImageLogoURL"]) - SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, apiResponse[@"TotalNegativeFeedbackCount"]) - SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) - SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.answerText, apiResponse[@"AnswerText"]) - - self.lastModificationTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; - self.lastModeratedTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; - - self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; - self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; - self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; - self.contextDataValues = [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; - NSNumber* isSyndicated = apiResponse[@"IsSyndicated"]; - if(![isSyndicated isKindOfClass:[NSNull class]]) { - _isSyndicated = [isSyndicated boolValue]; - - if (self.isSyndicated) { - _syndicationSource = [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; - } - } +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse + includes:(nullable BVConversationsInclude *)includes { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) + SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) + SET_IF_NOT_NULL(self.questionId, apiResponse[@"QuestionId"]) + SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) + SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, + apiResponse[@"TotalPositiveFeedbackCount"]) + SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) + SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) + SET_IF_NOT_NULL(self.isFeatured, apiResponse[@"IsFeatured"]) + SET_IF_NOT_NULL(self.productRecommendationIds, + apiResponse[@"ProductRecommendationIds"]) + SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) + SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) + SET_IF_NOT_NULL(self.brandImageLogoURL, apiResponse[@"BrandImageLogoURL"]) + SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, + apiResponse[@"TotalNegativeFeedbackCount"]) + SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) + SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.answerText, apiResponse[@"AnswerText"]) + self.lastModificationTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; + self.lastModeratedTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + + self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; + self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; + self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + self.contextDataValues = + [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; + NSNumber *isSyndicated = apiResponse[@"IsSyndicated"]; + if (![isSyndicated isKindOfClass:[NSNull class]]) { + _isSyndicated = [isSyndicated boolValue]; + + if (self.isSyndicated) { + _syndicationSource = + [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; + } } - return self; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.h index 76ce8f5a..153b3ddf 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.h @@ -5,43 +5,44 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import "BVGenericConversationsResult.h" -#import "BVPhoto.h" -#import "BVVideo.h" +#import "BVAnswer.h" #import "BVBadge.h" #import "BVContextDataValue.h" #import "BVDimensionAndDistributionUtil.h" -#import "BVSecondaryRating.h" -#import "BVReview.h" +#import "BVGenericConversationsResult.h" +#import "BVPhoto.h" +#import "BVQAStatistics.h" #import "BVQuestion.h" -#import "BVAnswer.h" +#import "BVReview.h" #import "BVReviewStatistics.h" -#import "BVQAStatistics.h" +#import "BVSecondaryRating.h" +#import "BVVideo.h" +#import -@interface BVAuthor : NSObject +@interface BVAuthor : NSObject -@property NSString* _Nullable userNickname; -@property NSString* _Nullable authorId; -@property NSDate* _Nullable submissionTime; -@property NSDate* _Nullable lastModeratedTime; -@property TagDimensions _Nullable tagDimensions; -@property NSArray* _Nonnull photos; -@property NSArray* _Nonnull contextDataValues; -@property NSArray* _Nonnull videos; -@property NSString* _Nullable submissionId; -@property NSString* _Nullable userLocation; -@property NSArray* _Nonnull badges; -@property NSArray* _Nonnull secondaryRatings; +@property(nullable) NSString *userNickname; +@property(nullable) NSString *authorId; +@property(nullable) NSDate *submissionTime; +@property(nullable) NSDate *lastModeratedTime; +@property(nullable) TagDimensions tagDimensions; +@property(nonnull) NSArray *photos; +@property(nonnull) NSArray *contextDataValues; +@property(nonnull) NSArray *videos; +@property(nullable) NSString *submissionId; +@property(nullable) NSString *userLocation; +@property(nonnull) NSArray *badges; +@property(nonnull) NSArray *secondaryRatings; -@property NSArray* _Nonnull includedReviews; -@property NSArray* _Nonnull includedQuestions; -@property NSArray* _Nonnull includedAnswers; -@property NSArray* _Nonnull includedComments; +@property(nonnull) NSArray *includedReviews; +@property(nonnull) NSArray *includedQuestions; +@property(nonnull) NSArray *includedAnswers; +@property(nonnull) NSArray *includedComments; -@property BVReviewStatistics* _Nullable reviewStatistics; -@property BVQAStatistics* _Nullable qaStatistics; +@property(nullable) BVReviewStatistics *reviewStatistics; +@property(nullable) BVQAStatistics *qaStatistics; -@property (nonatomic, strong, readonly) BVConversationsInclude * _Nullable includes; +@property(nullable, nonatomic, strong, readonly) + BVConversationsInclude *includes; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.m index 76932c6d..9e9b3d97 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVAuthor.m @@ -6,72 +6,75 @@ // #import "BVAuthor.h" -#import "BVNullHelper.h" -#import "BVModelUtil.h" #import "BVConversationsInclude.h" +#import "BVModelUtil.h" +#import "BVNullHelper.h" @implementation BVAuthor --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - self = [super init]; - if(self){ - - _includes = includes; - - SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) - SET_IF_NOT_NULL(self.userLocation, apiResponse[@"Location"]) - SET_IF_NOT_NULL(self.authorId, apiResponse[@"Id"]) - - self.reviewStatistics = [[BVReviewStatistics alloc] initWithApiResponse:apiResponse[@"ReviewStatistics"]]; - self.qaStatistics = [[BVQAStatistics alloc] initWithApiResponse:apiResponse[@"QAStatistics"]]; - - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; - self.lastModeratedTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; - - self.tagDimensions = [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; - self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; - self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; - self.contextDataValues = [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; - self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; - self.secondaryRatings = [BVModelUtil parseSecondaryRatings:apiResponse[@"SecondaryRatings"]]; - - NSArray* reviewIds = apiResponse[@"ReviewIds"]; - NSMutableArray* tempReviews = [NSMutableArray array]; - for(NSString* reviewId in reviewIds) { - BVReview* review = [includes getReviewById:reviewId]; - [tempReviews addObject:review]; - } - self.includedReviews = tempReviews; - - NSArray* questionIds = apiResponse[@"QuestionIds"]; - NSMutableArray* tempQuestions = [NSMutableArray array]; - for(NSString* questionId in questionIds) { - BVQuestion* question = [includes getQuestionById:questionId]; - [tempQuestions addObject:question]; - - } - self.includedQuestions = tempQuestions; - - NSArray* commentIds = apiResponse[@"CommentIds"]; - NSMutableArray* tempComments = [NSMutableArray array]; - for(NSString* commentId in commentIds) { - BVComment* comment = [includes getCommentById:commentId]; - [tempComments addObject:comment]; - - } - self.includedComments = tempComments; - - NSArray* answerIds = apiResponse[@"AnswerIds"]; - NSMutableArray* tempAnswers = [NSMutableArray array]; - for(NSString* answerId in answerIds) { - BVAnswer* answer = [includes getAnswerById:answerId]; - [tempAnswers addObject:answer]; - - } - self.includedAnswers = tempAnswers; +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + self = [super init]; + if (self) { + _includes = includes; + + SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) + SET_IF_NOT_NULL(self.userLocation, apiResponse[@"Location"]) + SET_IF_NOT_NULL(self.authorId, apiResponse[@"Id"]) + + self.reviewStatistics = [[BVReviewStatistics alloc] + initWithApiResponse:apiResponse[@"ReviewStatistics"]]; + self.qaStatistics = [[BVQAStatistics alloc] + initWithApiResponse:apiResponse[@"QAStatistics"]]; + + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; + self.lastModeratedTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + + self.tagDimensions = + [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; + self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; + self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; + self.contextDataValues = + [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; + self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + self.secondaryRatings = + [BVModelUtil parseSecondaryRatings:apiResponse[@"SecondaryRatings"]]; + + NSArray *reviewIds = apiResponse[@"ReviewIds"]; + NSMutableArray *tempReviews = [NSMutableArray array]; + for (NSString *reviewId in reviewIds) { + BVReview *review = [includes getReviewById:reviewId]; + [tempReviews addObject:review]; + } + self.includedReviews = tempReviews; + + NSArray *questionIds = apiResponse[@"QuestionIds"]; + NSMutableArray *tempQuestions = [NSMutableArray array]; + for (NSString *questionId in questionIds) { + BVQuestion *question = [includes getQuestionById:questionId]; + [tempQuestions addObject:question]; + } + self.includedQuestions = tempQuestions; + + NSArray *commentIds = apiResponse[@"CommentIds"]; + NSMutableArray *tempComments = [NSMutableArray array]; + for (NSString *commentId in commentIds) { + BVComment *comment = [includes getCommentById:commentId]; + [tempComments addObject:comment]; + } + self.includedComments = tempComments; + NSArray *answerIds = apiResponse[@"AnswerIds"]; + NSMutableArray *tempAnswers = [NSMutableArray array]; + for (NSString *answerId in answerIds) { + BVAnswer *answer = [includes getAnswerById:answerId]; + [tempAnswers addObject:answer]; } - return self; + self.includedAnswers = tempAnswers; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.h index bfad0ada..a0f9948e 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.h @@ -7,38 +7,39 @@ #import -#import "BVGenericConversationsResult.h" #import "BVBadge.h" +#import "BVGenericConversationsResult.h" #import "BVSyndicationSource.h" -@interface BVComment : NSObject - -@property (readonly) NSString* _Nullable title; -@property (readonly) NSString* _Nullable commentText; -@property (readonly) NSString* _Nullable reviewId; -@property (readonly) NSString* _Nullable commentId; -@property (readonly) NSString* _Nullable campaignId; +@interface BVComment : NSObject -@property (readonly) NSString* _Nullable userNickname; -@property (readonly) NSString* _Nullable authorId; -@property (readonly) NSString* _Nullable userLocation; -@property (readonly) NSDate* _Nullable submissionTime; -@property (readonly) NSDate* _Nullable lastModeratedTime; -@property (readonly) NSDate* _Nullable lastModificationTime; +@property(nullable, readonly) NSString *title; +@property(nullable, readonly) NSString *commentText; +@property(nullable, readonly) NSString *reviewId; +@property(nullable, readonly) NSString *commentId; +@property(nullable, readonly) NSString *campaignId; -@property (readonly) NSString* _Nullable submissionId; -@property (readonly) NSNumber* _Nullable totalFeedbackCount; -@property (readonly) NSNumber* _Nullable totalPositiveFeedbackCount; -@property (readonly) NSNumber* _Nullable totalNegativeFeedbackCount; +@property(nullable, readonly) NSString *userNickname; +@property(nullable, readonly) NSString *authorId; +@property(nullable, readonly) NSString *userLocation; +@property(nullable, readonly) NSDate *submissionTime; +@property(nullable, readonly) NSDate *lastModeratedTime; +@property(nullable, readonly) NSDate *lastModificationTime; -@property (readonly) NSString* _Nullable contentLocale; +@property(nullable, readonly) NSString *submissionId; +@property(nullable, readonly) NSNumber *totalFeedbackCount; +@property(nullable, readonly) NSNumber *totalPositiveFeedbackCount; +@property(nullable, readonly) NSNumber *totalNegativeFeedbackCount; -@property (readonly) NSArray* _Nonnull badges; +@property(nullable, readonly) NSString *contentLocale; -@property (readonly) BOOL isSyndicated; -@property (nonatomic, strong, readonly, nullable) BVSyndicationSource* syndicationSource; +@property(nonnull, readonly) NSArray *badges; -@property (nonatomic, strong, readonly) BVConversationsInclude * _Nullable includes; +@property(readonly) BOOL isSyndicated; +@property(nullable, nonatomic, strong, readonly) + BVSyndicationSource *syndicationSource; +@property(nullable, nonatomic, strong, readonly) + BVConversationsInclude *includes; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.m index a9710d3a..8342b813 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVComment.m @@ -6,52 +6,55 @@ // #import "BVComment.h" -#import "BVNullHelper.h" #import "BVModelUtil.h" +#import "BVNullHelper.h" @implementation BVComment --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - - self = [super init]; - if(self) { - - _includes = includes; - - SET_IF_NOT_NULL(_userNickname, apiResponse[@"UserNickname"]) - SET_IF_NOT_NULL(_userLocation, apiResponse[@"UserLocation"]) - SET_IF_NOT_NULL(_authorId, apiResponse[@"AuthorId"]) - SET_IF_NOT_NULL(_reviewId, apiResponse[@"ReviewId"]) - SET_IF_NOT_NULL(_commentId, apiResponse[@"Id"]) - SET_IF_NOT_NULL(_commentText, apiResponse[@"CommentText"]) - SET_IF_NOT_NULL(_title, apiResponse[@"Title"]) - - SET_IF_NOT_NULL(_submissionId, apiResponse[@"SubmissionId"]) - SET_IF_NOT_NULL(_totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) - SET_IF_NOT_NULL(_totalPositiveFeedbackCount, apiResponse[@"TotalPositiveFeedbackCount"]) - SET_IF_NOT_NULL(_totalNegativeFeedbackCount, apiResponse[@"TotalNegativeFeedbackCount"]) - SET_IF_NOT_NULL(_contentLocale, apiResponse[@"ContentLocale"]) - SET_IF_NOT_NULL(_campaignId, apiResponse[@"CampaignId"]) - - _submissionTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; - _lastModeratedTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; - _lastModificationTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; - - NSNumber* isSyndicated = apiResponse[@"IsSyndicated"]; - if(![isSyndicated isKindOfClass:[NSNull class]]) { - _isSyndicated = [isSyndicated boolValue]; - - if (self.isSyndicated) { - _syndicationSource = [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; - } - } - - _badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; - - } - - return self; - +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + self = [super init]; + if (self) { + _includes = includes; + + SET_IF_NOT_NULL(_userNickname, apiResponse[@"UserNickname"]) + SET_IF_NOT_NULL(_userLocation, apiResponse[@"UserLocation"]) + SET_IF_NOT_NULL(_authorId, apiResponse[@"AuthorId"]) + SET_IF_NOT_NULL(_reviewId, apiResponse[@"ReviewId"]) + SET_IF_NOT_NULL(_commentId, apiResponse[@"Id"]) + SET_IF_NOT_NULL(_commentText, apiResponse[@"CommentText"]) + SET_IF_NOT_NULL(_title, apiResponse[@"Title"]) + + SET_IF_NOT_NULL(_submissionId, apiResponse[@"SubmissionId"]) + SET_IF_NOT_NULL(_totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) + SET_IF_NOT_NULL(_totalPositiveFeedbackCount, + apiResponse[@"TotalPositiveFeedbackCount"]) + SET_IF_NOT_NULL(_totalNegativeFeedbackCount, + apiResponse[@"TotalNegativeFeedbackCount"]) + SET_IF_NOT_NULL(_contentLocale, apiResponse[@"ContentLocale"]) + SET_IF_NOT_NULL(_campaignId, apiResponse[@"CampaignId"]) + + _submissionTime = + [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; + _lastModeratedTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + _lastModificationTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; + + NSNumber *isSyndicated = apiResponse[@"IsSyndicated"]; + if (![isSyndicated isKindOfClass:[NSNull class]]) { + _isSyndicated = [isSyndicated boolValue]; + + if (self.isSyndicated) { + _syndicationSource = + [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; + } + } + + _badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + } + + return self; } - + @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.h index c85cebe7..84217ead 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.h @@ -5,48 +5,55 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVGenericConversationsResult.h" #import "BVBrand.h" -#import "BVReviewStatistics.h" +#import "BVDisplayableProductContent.h" +#import "BVGenericConversationsResult.h" #import "BVQAStatistics.h" -#import "BVReview.h" #import "BVQuestion.h" -#import "BVDisplayableProductContent.h" +#import "BVReview.h" +#import "BVReviewStatistics.h" +#import @class BVConversationsInclude; /* - The main information contained within a `BVProductResponse` which is a response to `BVProductDisplayPageRequest`. + The main information contained within a `BVProductResponse` which is a response + to `BVProductDisplayPageRequest`. Some commonly used data in a product: Product page URL is included in the `productPageUrl` property. Product image URL is included in the `imageUrl` property. - Review statistics is included in the `reviewStatistics` property, if requested in the `BVReviewsRequest` object. - Questions&Answers statistics is included in the `qaStatistics` property, if requested in the `BVReviewsRequest` object. - Reviews attached to this product are included in the `includedReviews` property, if requested in the `BVReviewsRequest` object. - Questions attached to this product are included in the `includedQuestion` property, if requested in the `BVReviewsRequest` object. + Review statistics is included in the `reviewStatistics` property, if + requested in the `BVReviewsRequest` object. + Questions&Answers statistics is included in the `qaStatistics` property, if + requested in the `BVReviewsRequest` object. + Reviews attached to this product are included in the `includedReviews` + property, if requested in the `BVReviewsRequest` object. + Questions attached to this product are included in the `includedQuestion` + property, if requested in the `BVReviewsRequest` object. */ -@interface BVProduct : NSObject +@interface BVProduct + : NSObject -@property BVBrand* _Nullable brand; -@property NSArray* _Nonnull ISBNs; -@property NSArray* _Nonnull UPCs; -@property NSString* _Nullable productDescription; -@property NSArray* _Nonnull manufacturerPartNumbers; -@property NSDictionary* _Nullable attributes; -@property NSString* _Nullable brandExternalId; -@property NSString* _Nullable productPageUrl; -@property NSArray* _Nonnull familyIds; -@property NSString* _Nullable imageUrl; -@property NSArray* _Nonnull EANs; -@property NSString* _Nullable name; -@property NSString* _Nullable categoryId; -@property NSArray* _Nonnull modelNumbers; -@property BVReviewStatistics* _Nullable reviewStatistics; -@property BVQAStatistics* _Nullable qaStatistics; +@property(nullable) BVBrand *brand; +@property(nonnull) NSArray *ISBNs; +@property(nonnull) NSArray *UPCs; +@property(nullable) NSString *productDescription; +@property(nonnull) NSArray *manufacturerPartNumbers; +@property(nullable) NSDictionary *attributes; +@property(nullable) NSString *brandExternalId; +@property(nullable) NSString *productPageUrl; +@property(nonnull) NSArray *familyIds; +@property(nullable) NSString *imageUrl; +@property(nonnull) NSArray *EANs; +@property(nullable) NSString *name; +@property(nullable) NSString *categoryId; +@property(nonnull) NSArray *modelNumbers; +@property(nullable) BVReviewStatistics *reviewStatistics; +@property(nullable) BVQAStatistics *qaStatistics; -@property NSArray* _Nonnull includedReviews; -@property NSArray* _Nonnull includedQuestions; +@property(nonnull) NSArray *includedReviews; +@property(nonnull) NSArray *includedQuestions; -@property (nonatomic, strong, readonly) BVConversationsInclude * _Nullable includes; -@property (nonatomic, strong, readonly) NSDictionary * _Nonnull apiResponse; +@property(nullable, nonatomic, strong, readonly) + BVConversationsInclude *includes; +@property(nonnull, nonatomic, strong, readonly) NSDictionary *apiResponse; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.m index cee1dd63..9eb6ed07 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVProduct.m @@ -16,73 +16,75 @@ @implementation BVProduct @synthesize displayName; @synthesize averageRating; --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - - self = [super init]; - if(self) { - _includes = includes; - _apiResponse = apiResponse; - self.brand = [[BVBrand alloc] initWithApiResponse:apiResponse[@"Brand"]]; - - SET_IF_NOT_NULL(self.productDescription, apiResponse[@"Description"]) - SET_IF_NOT_NULL(self.brandExternalId, apiResponse[@"BrandExternalId"]) - SET_IF_NOT_NULL(self.productPageUrl, apiResponse[@"ProductPageUrl"]) - SET_IF_NOT_NULL(self.name, apiResponse[@"Name"]) - SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) - SET_IF_NOT_NULL(_identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.imageUrl, apiResponse[@"ImageUrl"]) - SET_IF_NOT_NULL(self.attributes, apiResponse[@"Attributes"]) - SET_IF_NOT_NULL(self.manufacturerPartNumbers, apiResponse[@"ManufacturerPartNumbers"]) - SET_IF_NOT_NULL(self.familyIds, apiResponse[@"FamilyIds"]) - SET_IF_NOT_NULL(self.ISBNs, apiResponse[@"ISBNs"]) - SET_IF_NOT_NULL(self.UPCs, apiResponse[@"UPCs"]) - SET_IF_NOT_NULL(self.EANs, apiResponse[@"EANs"]) - SET_IF_NOT_NULL(self.modelNumbers, apiResponse[@"ModelNumbers"]) +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + self = [super init]; + if (self) { + _includes = includes; + _apiResponse = apiResponse; + self.brand = [[BVBrand alloc] initWithApiResponse:apiResponse[@"Brand"]]; - self.reviewStatistics = [[BVReviewStatistics alloc] initWithApiResponse:apiResponse[@"FilteredReviewStatistics"]]; - - if (!self.reviewStatistics ) { - self.reviewStatistics = [[BVReviewStatistics alloc] initWithApiResponse:apiResponse[@"ReviewStatistics"]]; - } - - self.qaStatistics = [[BVQAStatistics alloc] initWithApiResponse:apiResponse[@"FilteredQAStatistics"]]; - - if (!self.qaStatistics){ - self.qaStatistics = [[BVQAStatistics alloc] initWithApiResponse:apiResponse[@"QAStatistics"]]; - } + SET_IF_NOT_NULL(self.productDescription, apiResponse[@"Description"]) + SET_IF_NOT_NULL(self.brandExternalId, apiResponse[@"BrandExternalId"]) + SET_IF_NOT_NULL(self.productPageUrl, apiResponse[@"ProductPageUrl"]) + SET_IF_NOT_NULL(self.name, apiResponse[@"Name"]) + SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) + SET_IF_NOT_NULL(_identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.imageUrl, apiResponse[@"ImageUrl"]) + SET_IF_NOT_NULL(self.attributes, apiResponse[@"Attributes"]) + SET_IF_NOT_NULL(self.manufacturerPartNumbers, + apiResponse[@"ManufacturerPartNumbers"]) + SET_IF_NOT_NULL(self.familyIds, apiResponse[@"FamilyIds"]) + SET_IF_NOT_NULL(self.ISBNs, apiResponse[@"ISBNs"]) + SET_IF_NOT_NULL(self.UPCs, apiResponse[@"UPCs"]) + SET_IF_NOT_NULL(self.EANs, apiResponse[@"EANs"]) + SET_IF_NOT_NULL(self.modelNumbers, apiResponse[@"ModelNumbers"]) - NSArray* reviewIds = apiResponse[@"ReviewIds"]; - NSMutableArray* tempReviews = [NSMutableArray array]; - for(NSString* reviewId in reviewIds) { - BVReview* review = [includes getReviewById:reviewId]; - [tempReviews addObject:review]; - } - self.includedReviews = tempReviews; - - NSArray* questionIds = apiResponse[@"QuestionIds"]; - NSMutableArray* tempQuestions = [NSMutableArray array]; - for(NSString* questionId in questionIds) { - BVQuestion* question = [includes getQuestionById:questionId]; - [tempQuestions addObject:question]; - - } - self.includedQuestions = tempQuestions; + self.reviewStatistics = [[BVReviewStatistics alloc] + initWithApiResponse:apiResponse[@"FilteredReviewStatistics"]]; + if (!self.reviewStatistics) { + self.reviewStatistics = [[BVReviewStatistics alloc] + initWithApiResponse:apiResponse[@"ReviewStatistics"]]; } - return self; - + + self.qaStatistics = [[BVQAStatistics alloc] + initWithApiResponse:apiResponse[@"FilteredQAStatistics"]]; + + if (!self.qaStatistics) { + self.qaStatistics = [[BVQAStatistics alloc] + initWithApiResponse:apiResponse[@"QAStatistics"]]; + } + + NSArray *reviewIds = apiResponse[@"ReviewIds"]; + NSMutableArray *tempReviews = [NSMutableArray array]; + for (NSString *reviewId in reviewIds) { + BVReview *review = [includes getReviewById:reviewId]; + [tempReviews addObject:review]; + } + self.includedReviews = tempReviews; + + NSArray *questionIds = apiResponse[@"QuestionIds"]; + NSMutableArray *tempQuestions = [NSMutableArray array]; + for (NSString *questionId in questionIds) { + BVQuestion *question = [includes getQuestionById:questionId]; + [tempQuestions addObject:question]; + } + self.includedQuestions = tempQuestions; + } + return self; } --(NSString*)displayName { - return _name; +- (NSString *)displayName { + return _name; } --(NSString*)displayImageUrl { - return _imageUrl; +- (NSString *)displayImageUrl { + return _imageUrl; } --(NSNumber*)averageRating { - return _reviewStatistics.averageOverallRating; +- (NSNumber *)averageRating { + return _reviewStatistics.averageOverallRating; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.h index 8ef02e94..383dac87 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.h @@ -5,19 +5,20 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVGenericConversationsResult.h" #import "BVAnswer.h" +#import "BVBadge.h" +#import "BVContextDataValue.h" #import "BVDimensionAndDistributionUtil.h" +#import "BVGenericConversationsResult.h" #import "BVPhoto.h" -#import "BVVideo.h" -#import "BVContextDataValue.h" -#import "BVBadge.h" #import "BVSyndicationSource.h" +#import "BVVideo.h" +#import /* - A consumer generated question about a product. For example: "Is this toaster heavy?" or "How many HDMI ports does this TV have?" - + A consumer generated question about a product. For example: "Is this toaster + heavy?" or "How many HDMI ports does this TV have?" + Some commonly used data in a question: Answers to this question are included in the `answers` property. Question summary is included in the `questionSummary` property. @@ -25,36 +26,39 @@ */ @interface BVQuestion : NSObject -@property (nonatomic, copy) NSArray * _Nonnull answers; -@property (nonatomic, copy) NSString * _Nullable questionSummary; -@property (nonatomic, copy) NSString * _Nullable questionDetails; -@property (nonatomic, copy) NSString * _Nullable userNickname; -@property (nonatomic, strong) TagDimensions _Nullable tagDimensions; -@property (nonatomic, copy) NSArray * _Nonnull photos; -@property (nonatomic, copy) NSString * _Nullable categoryId; -@property (nonatomic, copy) NSArray * _Nonnull contextDataValues; -@property (nonatomic, copy) NSArray * _Nonnull videos; -@property (nonatomic, copy) NSString * _Nullable submissionId; -@property (nonatomic, strong) NSDate * _Nullable lastModificationTime; -@property (nonatomic, copy) NSString * _Nullable userLocation; -@property (nonatomic, copy) NSArray * _Nonnull badges; -@property (nonatomic, copy) NSString * _Nullable authorId; -@property (nonatomic, copy) NSArray * _Nonnull productRecommendationIds; -@property (nonatomic, copy) NSString * _Nullable productId; -@property (nonatomic, strong) NSDictionary * _Nonnull additionalFields; -@property (nonatomic, copy) NSString * _Nullable campaignId; -@property (nonatomic, strong) NSDate * _Nullable submissionTime; -@property (nonatomic, copy) NSString * _Nullable contentLocale; -@property (nonatomic, copy) NSString * _Nullable moderationStatus; -@property (nonatomic, copy) NSString * _Nullable identifier; -@property (nonatomic, strong) NSDate * _Nullable lastModeratedTime; -@property (nonatomic, copy) NSNumber* _Nullable totalAnswerCount; -@property (nonatomic, copy) NSNumber* _Nullable totalFeedbackCount; -@property (nonatomic, copy) NSNumber* _Nullable totalPositiveFeedbackCount; -@property (nonatomic, copy) NSNumber* _Nullable totalInappropriateFeedbackCount; -@property (nonatomic, copy) NSNumber* _Nullable totalNegativeFeedbackCount; +@property(nonnull, nonatomic, copy) NSArray *answers; +@property(nullable, nonatomic, copy) NSString *questionSummary; +@property(nullable, nonatomic, copy) NSString *questionDetails; +@property(nullable, nonatomic, copy) NSString *userNickname; +@property(nullable, nonatomic, strong) TagDimensions tagDimensions; +@property(nonnull, nonatomic, copy) NSArray *photos; +@property(nullable, nonatomic, copy) NSString *categoryId; +@property(nonnull, nonatomic, copy) + NSArray *contextDataValues; +@property(nonnull, nonatomic, copy) NSArray *videos; +@property(nullable, nonatomic, copy) NSString *submissionId; +@property(nullable, nonatomic, strong) NSDate *lastModificationTime; +@property(nullable, nonatomic, copy) NSString *userLocation; +@property(nonnull, nonatomic, copy) NSArray *badges; +@property(nullable, nonatomic, copy) NSString *authorId; +@property(nonnull, nonatomic, copy) + NSArray *productRecommendationIds; +@property(nullable, nonatomic, copy) NSString *productId; +@property(nonnull, nonatomic, strong) NSDictionary *additionalFields; +@property(nullable, nonatomic, copy) NSString *campaignId; +@property(nullable, nonatomic, strong) NSDate *submissionTime; +@property(nullable, nonatomic, copy) NSString *contentLocale; +@property(nullable, nonatomic, copy) NSString *moderationStatus; +@property(nullable, nonatomic, copy) NSString *identifier; +@property(nullable, nonatomic, strong) NSDate *lastModeratedTime; +@property(nullable, nonatomic, copy) NSNumber *totalAnswerCount; +@property(nullable, nonatomic, copy) NSNumber *totalFeedbackCount; +@property(nullable, nonatomic, copy) NSNumber *totalPositiveFeedbackCount; +@property(nullable, nonatomic, copy) NSNumber *totalInappropriateFeedbackCount; +@property(nullable, nonatomic, copy) NSNumber *totalNegativeFeedbackCount; @property bool isFeatured; -@property (readonly) BOOL isSyndicated; -@property (nonatomic, strong, readonly, nullable) BVSyndicationSource* syndicationSource; +@property(readonly) BOOL isSyndicated; +@property(nullable, nonatomic, strong, readonly) + BVSyndicationSource *syndicationSource; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.m index c71359a4..d44f40f1 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVQuestion.m @@ -6,75 +6,85 @@ // #import "BVQuestion.h" -#import "BVModelUtil.h" #import "BVConversationsInclude.h" +#import "BVModelUtil.h" #import "BVNullHelper.h" @implementation BVQuestion --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - self = [super init]; - if(self) { - - NSArray* answerIds = apiResponse[@"AnswerIds"]; - NSMutableArray* tempAnswers = [NSMutableArray array]; - - if (includes){ - for(NSString* answerId in answerIds) { - BVAnswer* answer = [includes getAnswerById:answerId]; - [tempAnswers addObject:answer]; - } - } - - self.answers = tempAnswers; - - SET_IF_NOT_NULL(self.questionSummary, apiResponse[@"QuestionSummary"]) - SET_IF_NOT_NULL(self.totalAnswerCount, apiResponse[@"TotalAnswerCount"]) - SET_IF_NOT_NULL(self.questionDetails, apiResponse[@"QuestionDetails"]) - SET_IF_NOT_NULL(self.totalAnswerCount, apiResponse[@"TotalAnswerCount"]) - SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) - SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) - SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) - SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) - SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, apiResponse[@"TotalPositiveFeedbackCount"]) - SET_IF_NOT_NULL(self.totalInappropriateFeedbackCount, apiResponse[@"TotalInappropriateFeedbackCount"]) - SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) - SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) - - NSNumber* featured = apiResponse[@"IsFeatured"]; - if(![featured isKindOfClass:[NSNull class]]) { - self.isFeatured = [featured boolValue]; - } +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + self = [super init]; + if (self) { + NSArray *answerIds = apiResponse[@"AnswerIds"]; + NSMutableArray *tempAnswers = [NSMutableArray array]; - NSNumber* isSyndicated = apiResponse[@"IsSyndicated"]; - if(![isSyndicated isKindOfClass:[NSNull class]]) { - _isSyndicated = [isSyndicated boolValue]; - - if (self.isSyndicated) { - _syndicationSource = [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; - } - } + if (includes) { + for (NSString *answerId in answerIds) { + BVAnswer *answer = [includes getAnswerById:answerId]; + [tempAnswers addObject:answer]; + } + } - SET_IF_NOT_NULL(self.productRecommendationIds, apiResponse[@"ProductRecommendationIds"]) - SET_IF_NOT_NULL(self.productId, apiResponse[@"ProductId"]) - SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) - SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) - SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, apiResponse[@"TotalNegativeFeedbackCount"]) - SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) - SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + self.answers = tempAnswers; - self.lastModificationTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; - self.lastModeratedTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + SET_IF_NOT_NULL(self.questionSummary, apiResponse[@"QuestionSummary"]) + SET_IF_NOT_NULL(self.totalAnswerCount, apiResponse[@"TotalAnswerCount"]) + SET_IF_NOT_NULL(self.questionDetails, apiResponse[@"QuestionDetails"]) + SET_IF_NOT_NULL(self.totalAnswerCount, apiResponse[@"TotalAnswerCount"]) + SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) + SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) + SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) + SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) + SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, + apiResponse[@"TotalPositiveFeedbackCount"]) + SET_IF_NOT_NULL(self.totalInappropriateFeedbackCount, + apiResponse[@"TotalInappropriateFeedbackCount"]) + SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) + SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) - self.tagDimensions = [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; - self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; - self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; - self.contextDataValues = [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; - self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + NSNumber *featured = apiResponse[@"IsFeatured"]; + if (![featured isKindOfClass:[NSNull class]]) { + self.isFeatured = [featured boolValue]; } - return self; + + NSNumber *isSyndicated = apiResponse[@"IsSyndicated"]; + if (![isSyndicated isKindOfClass:[NSNull class]]) { + _isSyndicated = [isSyndicated boolValue]; + + if (self.isSyndicated) { + _syndicationSource = + [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; + } + } + + SET_IF_NOT_NULL(self.productRecommendationIds, + apiResponse[@"ProductRecommendationIds"]) + SET_IF_NOT_NULL(self.productId, apiResponse[@"ProductId"]) + SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) + SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) + SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, + apiResponse[@"TotalNegativeFeedbackCount"]) + SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) + SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + + self.lastModificationTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; + self.lastModeratedTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + + self.tagDimensions = + [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; + self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; + self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; + self.contextDataValues = + [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; + self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.h index e1a38e71..f96b2d12 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.h @@ -5,69 +5,70 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVGenericConversationsResult.h" +#import "BVBadge.h" +#import "BVComment.h" +#import "BVContextDataValue.h" #import "BVDimensionAndDistributionUtil.h" +#import "BVGenericConversationsResult.h" #import "BVPhoto.h" -#import "BVVideo.h" -#import "BVContextDataValue.h" -#import "BVBadge.h" #import "BVSecondaryRating.h" #import "BVSyndicationSource.h" -#import "BVComment.h" +#import "BVVideo.h" +#import @class BVProduct; /* A consumer generated review about a review. - + Some commonly used data in a review: Review title is available in the `title` property. Review text is available in the `reviewText` property. Review rating is availble in the `rating` property. User nickname is available in the `userNickname` property. */ -@interface BVReview : NSObject +@interface BVReview : NSObject -@property NSString* _Nullable reviewText; -@property NSString* _Nullable userNickname; -@property NSString* _Nullable title; +@property(nullable) NSString *reviewText; +@property(nullable) NSString *userNickname; +@property(nullable) NSString *title; @property int rating; -@property BVProduct* _Nullable product; -@property TagDimensions _Nullable tagDimensions; -@property NSString* _Nullable cons; +@property(nullable) BVProduct *product; +@property(nullable) TagDimensions tagDimensions; +@property(nullable) NSString *cons; @property bool isRecommended; @property bool isRatingsOnly; @property bool isSyndicated; -@property NSString* _Nullable pros; -@property NSArray* _Nonnull photos; -@property NSArray* _Nonnull contextDataValues; -@property NSArray* _Nonnull videos; -@property NSString* _Nullable submissionId; -@property NSNumber* _Nullable totalFeedbackCount; -@property NSNumber* _Nullable totalPositiveFeedbackCount; -@property NSString* _Nullable userLocation; -@property NSArray* _Nonnull badges; -@property NSString* _Nullable authorId; +@property(nullable) NSString *pros; +@property(nonnull) NSArray *photos; +@property(nonnull) NSArray *contextDataValues; +@property(nonnull) NSArray *videos; +@property(nullable) NSString *submissionId; +@property(nullable) NSNumber *totalFeedbackCount; +@property(nullable) NSNumber *totalPositiveFeedbackCount; +@property(nullable) NSString *userLocation; +@property(nonnull) NSArray *badges; +@property(nullable) NSString *authorId; @property bool isFeatured; -@property NSString* _Nullable productId; -@property NSArray* _Nonnull productRecommendationIds; -@property NSDictionary* _Nullable additionalFields; -@property NSString* _Nullable campaignId; -@property NSString* _Nullable helpfulness; -@property NSNumber* _Nullable totalNegativeFeedbackCount; -@property NSString* _Nullable contentLocale; -@property NSString* _Nullable ratingRange; -@property NSNumber* _Nullable totalCommentCount; -@property NSString* _Nullable moderationStatus; -@property NSString* _Nullable identifier; -@property NSArray* _Nonnull clientResponses; -@property NSArray* _Nonnull secondaryRatings; -@property NSDate* _Nullable lastModificationTime; -@property NSDate* _Nullable submissionTime; -@property NSDate* _Nullable lastModeratedTime; -@property (nonatomic, strong, readonly, nullable) BVSyndicationSource* syndicationSource; -@property (readonly) NSArray * _Nonnull comments; +@property(nullable) NSString *productId; +@property(nonnull) NSArray *productRecommendationIds; +@property(nullable) NSDictionary *additionalFields; +@property(nullable) NSString *campaignId; +@property(nullable) NSString *helpfulness; +@property(nullable) NSNumber *totalNegativeFeedbackCount; +@property(nullable) NSString *contentLocale; +@property(nullable) NSString *ratingRange; +@property(nullable) NSNumber *totalCommentCount; +@property(nullable) NSString *moderationStatus; +@property(nullable) NSString *identifier; +@property(nonnull) NSArray *clientResponses; +@property(nonnull) NSArray *secondaryRatings; +@property(nullable) NSDate *lastModificationTime; +@property(nullable) NSDate *submissionTime; +@property(nullable) NSDate *lastModeratedTime; +@property(nullable, nonatomic, strong, readonly) + BVSyndicationSource *syndicationSource; +@property(nonnull, readonly) NSArray *comments; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.m index cb6ad40a..673402ff 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/BVReview.m @@ -6,101 +6,109 @@ // #import "BVReview.h" -#import "BVModelUtil.h" #import "BVConversationsInclude.h" -#import "BVProduct.h" +#import "BVModelUtil.h" #import "BVNullHelper.h" +#import "BVProduct.h" @implementation BVReview --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - self = [super init]; - if(self){ - - NSMutableArray *tmpComments = [NSMutableArray array]; - NSArray *commentIds = apiResponse[@"CommentIds"]; - if (commentIds) { - for (NSString *commentId in (NSArray *)commentIds){ - BVComment *comment = [includes getCommentById:commentId]; - if (comment){ - [tmpComments addObject:comment]; - } - } - } - _comments = [NSArray arrayWithArray:tmpComments]; - - NSString* productId = apiResponse[@"ProductId"]; - self.product = [includes getProductById:productId]; - self.cons = apiResponse[@"Cons"]; - - NSNumber* recommended = apiResponse[@"IsRecommended"]; - if(![recommended isKindOfClass:[NSNull class]]) { - self.isRecommended = [recommended boolValue]; - } - - NSNumber* ratingsOnly = apiResponse[@"IsRatingsOnly"]; - if(![ratingsOnly isKindOfClass:[NSNull class]]) { - self.isRatingsOnly = [ratingsOnly boolValue]; - } - - NSNumber* isSyndicated = apiResponse[@"IsSyndicated"]; - if(![isSyndicated isKindOfClass:[NSNull class]]) { - self.isSyndicated = [isSyndicated boolValue]; - - if (self.isSyndicated) { - _syndicationSource = [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; - } - +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + self = [super init]; + if (self) { + NSMutableArray *tmpComments = [NSMutableArray array]; + NSArray *commentIds = apiResponse[@"CommentIds"]; + if (commentIds) { + for (NSString *commentId in (NSArray *)commentIds) { + BVComment *comment = [includes getCommentById:commentId]; + if (comment) { + [tmpComments addObject:comment]; } - - NSNumber* featured = apiResponse[@"IsFeatured"]; - if(![featured isKindOfClass:[NSNull class]]) { - self.isFeatured = [featured boolValue]; - } - - SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) - SET_IF_NOT_NULL(self.pros, apiResponse[@"Pros"]) - SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) - SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) - SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, apiResponse[@"TotalPositiveFeedbackCount"]) - SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) - SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) - SET_IF_NOT_NULL(self.productId, apiResponse[@"ProductId"]) - SET_IF_NOT_NULL(self.title, apiResponse[@"Title"]) - SET_IF_NOT_NULL(self.productRecommendationIds, apiResponse[@"ProductRecommendationIds"]) - SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) - SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) - SET_IF_NOT_NULL(self.helpfulness, apiResponse[@"Helpfulness"]) - SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, apiResponse[@"TotalNegativeFeedbackCount"]) - - NSNumber* num = apiResponse[@"Rating"]; - if (num && [num isKindOfClass:[NSNumber class]]) { - self.rating = (int)[num integerValue]; - } - else { - self.rating = 0; - } - - SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) - SET_IF_NOT_NULL(self.ratingRange, apiResponse[@"RatingRange"]) - SET_IF_NOT_NULL(self.totalCommentCount, apiResponse[@"TotalCommentCount"]) - SET_IF_NOT_NULL(self.reviewText, apiResponse[@"ReviewText"]) - SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) - SET_IF_NOT_NULL(self.clientResponses, apiResponse[@"ClientResponses"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - - self.lastModificationTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; - self.lastModeratedTime = [BVModelUtil convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; - - self.tagDimensions = [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; - self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; - self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; - self.contextDataValues = [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; - self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; - self.secondaryRatings = [BVModelUtil parseSecondaryRatings:apiResponse[@"SecondaryRatings"]]; + } + } + _comments = [NSArray arrayWithArray:tmpComments]; + + NSString *productId = apiResponse[@"ProductId"]; + self.product = [includes getProductById:productId]; + self.cons = apiResponse[@"Cons"]; + + NSNumber *recommended = apiResponse[@"IsRecommended"]; + if (![recommended isKindOfClass:[NSNull class]]) { + self.isRecommended = [recommended boolValue]; } - return self; + + NSNumber *ratingsOnly = apiResponse[@"IsRatingsOnly"]; + if (![ratingsOnly isKindOfClass:[NSNull class]]) { + self.isRatingsOnly = [ratingsOnly boolValue]; + } + + NSNumber *isSyndicated = apiResponse[@"IsSyndicated"]; + if (![isSyndicated isKindOfClass:[NSNull class]]) { + self.isSyndicated = [isSyndicated boolValue]; + + if (self.isSyndicated) { + _syndicationSource = + [[BVSyndicationSource alloc] initWithApiResponse:apiResponse]; + } + } + + NSNumber *featured = apiResponse[@"IsFeatured"]; + if (![featured isKindOfClass:[NSNull class]]) { + self.isFeatured = [featured boolValue]; + } + + SET_IF_NOT_NULL(self.userNickname, apiResponse[@"UserNickname"]) + SET_IF_NOT_NULL(self.pros, apiResponse[@"Pros"]) + SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) + SET_IF_NOT_NULL(self.totalFeedbackCount, apiResponse[@"TotalFeedbackCount"]) + SET_IF_NOT_NULL(self.totalPositiveFeedbackCount, + apiResponse[@"TotalPositiveFeedbackCount"]) + SET_IF_NOT_NULL(self.userLocation, apiResponse[@"UserLocation"]) + SET_IF_NOT_NULL(self.authorId, apiResponse[@"AuthorId"]) + SET_IF_NOT_NULL(self.productId, apiResponse[@"ProductId"]) + SET_IF_NOT_NULL(self.title, apiResponse[@"Title"]) + SET_IF_NOT_NULL(self.productRecommendationIds, + apiResponse[@"ProductRecommendationIds"]) + SET_IF_NOT_NULL(self.additionalFields, apiResponse[@"AdditionalFields"]) + SET_IF_NOT_NULL(self.campaignId, apiResponse[@"CampaignId"]) + SET_IF_NOT_NULL(self.helpfulness, apiResponse[@"Helpfulness"]) + SET_IF_NOT_NULL(self.totalNegativeFeedbackCount, + apiResponse[@"TotalNegativeFeedbackCount"]) + + NSNumber *num = apiResponse[@"Rating"]; + if (num && [num isKindOfClass:[NSNumber class]]) { + self.rating = (int)[num integerValue]; + } else { + self.rating = 0; + } + + SET_IF_NOT_NULL(self.contentLocale, apiResponse[@"ContentLocale"]) + SET_IF_NOT_NULL(self.ratingRange, apiResponse[@"RatingRange"]) + SET_IF_NOT_NULL(self.totalCommentCount, apiResponse[@"TotalCommentCount"]) + SET_IF_NOT_NULL(self.reviewText, apiResponse[@"ReviewText"]) + SET_IF_NOT_NULL(self.moderationStatus, apiResponse[@"ModerationStatus"]) + SET_IF_NOT_NULL(self.clientResponses, apiResponse[@"ClientResponses"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + + self.lastModificationTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModificationTime"]]; + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiResponse[@"SubmissionTime"]]; + self.lastModeratedTime = [BVModelUtil + convertTimestampToDatetime:apiResponse[@"LastModeratedTime"]]; + + self.tagDimensions = + [BVModelUtil parseTagDimension:apiResponse[@"TagDimensions"]]; + self.photos = [BVModelUtil parsePhotos:apiResponse[@"Photos"]]; + self.videos = [BVModelUtil parseVideos:apiResponse[@"Videos"]]; + self.contextDataValues = + [BVModelUtil parseContextDataValues:apiResponse[@"ContextDataValues"]]; + self.badges = [BVModelUtil parseBadges:apiResponse[@"Badges"]]; + self.secondaryRatings = + [BVModelUtil parseSecondaryRatings:apiResponse[@"SecondaryRatings"]]; + } + return self; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.h index 32e0a80a..37616f26 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.h @@ -5,50 +5,58 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import +#import -#import "BVGenericConversationsResult.h" #import "BVBrand.h" -#import "BVReviewStatistics.h" +#import "BVGenericConversationsResult.h" #import "BVQAStatistics.h" -#import "BVReview.h" #import "BVQuestion.h" +#import "BVReview.h" +#import "BVReviewStatistics.h" #import "BVStoreLocation.h" /** - A BVStore object is found within the response objects: BVBulkStoresResponse and BVStoreReviewsResponse. + A BVStore object is found within the response objects: BVBulkStoresResponse and + BVStoreReviewsResponse. Some commonly used data in a store: - Product page URL is included in the `productPageUrl` property. This would be the icon to display the store in your app. + Product page URL is included in the `productPageUrl` property. This would be + the icon to display the store in your app. Product image URL is included in the `imageUrl` property. - Store review statistics are included in the `reviewStatistics` property, if requested in the original request object. - The storeLocation provides additional attributes about the store, such as geo-location and phone number. + Store review statistics are included in the `reviewStatistics` property, if + requested in the original request object. + The storeLocation provides additional attributes about the store, such as + geo-location and phone number. */ -@interface BVStore : NSObject - -@property BVBrand* _Nullable brand; -@property BVStoreLocation *_Nullable storeLocation; -@property NSString* _Nullable productDescription; -@property NSDictionary* _Nullable attributes; -@property NSString* _Nullable brandExternalId; -@property NSString* _Nullable productPageUrl; -@property NSString* _Nullable imageUrl; -@property NSString* _Nullable name; -@property NSString* _Nullable categoryId; -@property NSString* _Nullable identifier; -@property BVReviewStatistics* _Nullable reviewStatistics; -@property CLLocation * _Nullable deviceLocation; -@property NSDictionary * _Nonnull apiResponse; - --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse; - -/// Helper to construct a CLLoation object for a store, if the latitue and longitude were provided in the API response model. -- (CLLocation * _Nullable)getCLLocation; - -/// Given a CLLocation object with latitude and longitude, get the distance in meters from the current store object. Returns -1 if the store does not have a location. +@interface BVStore : NSObject + +@property(nullable) BVBrand *brand; +@property(nullable) BVStoreLocation *storeLocation; +@property(nullable) NSString *productDescription; +@property(nullable) NSDictionary *attributes; +@property(nullable) NSString *brandExternalId; +@property(nullable) NSString *productPageUrl; +@property(nullable) NSString *imageUrl; +@property(nullable) NSString *name; +@property(nullable) NSString *categoryId; +@property(nullable) NSString *identifier; +@property(nullable) BVReviewStatistics *reviewStatistics; +@property(nullable) CLLocation *deviceLocation; +@property(nonnull) NSDictionary *apiResponse; + +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse; + +/// Helper to construct a CLLoation object for a store, if the latitue and +/// longitude were provided in the API response model. +- (nullable CLLocation *)getCLLocation; + +/// Given a CLLocation object with latitude and longitude, get the distance in +/// meters from the current store object. Returns -1 if the store does not have +/// a location. - (CLLocationDistance)distanceInMetersFromCurrentLocation; -/// Returns true of a valid latitude and longitude is present for this store in the BVStoreLocation object. +/// Returns true of a valid latitude and longitude is present for this store in +/// the BVStoreLocation object. - (BOOL)hasGeoLoation; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.m index de0bca01..c5eb03e7 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStore.m @@ -7,85 +7,82 @@ #import -#import "BVStore.h" #import "BVConversationsInclude.h" #import "BVNullHelper.h" +#import "BVStore.h" @implementation BVStore --(id)initWithApiResponse:(NSDictionary *)apiResponse includes:(BVConversationsInclude *)includes { - - return [self initWithApiResponse:apiResponse]; +- (id)initWithApiResponse:(NSDictionary *)apiResponse + includes:(BVConversationsInclude *)includes { + return [self initWithApiResponse:apiResponse]; } --(id _Nonnull)initWithApiResponse:(NSDictionary* _Nonnull)apiResponse{ - - self = [super init]; - if(self) { - _apiResponse = apiResponse; - self.brand = [[BVBrand alloc] initWithApiResponse:apiResponse[@"Brand"]]; - - SET_IF_NOT_NULL(self.productDescription, apiResponse[@"Description"]) - SET_IF_NOT_NULL(self.brandExternalId, apiResponse[@"BrandExternalId"]) - SET_IF_NOT_NULL(self.productPageUrl, apiResponse[@"ProductPageUrl"]) - SET_IF_NOT_NULL(self.name, apiResponse[@"Name"]) - SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) - SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) - SET_IF_NOT_NULL(self.imageUrl, apiResponse[@"ImageUrl"]) - SET_IF_NOT_NULL(self.attributes, apiResponse[@"Attributes"]) - - if (self.attributes){ - self.storeLocation = [[BVStoreLocation alloc] initWithStoreAtrributes:self.attributes]; - } - - self.reviewStatistics = [[BVReviewStatistics alloc] initWithApiResponse:apiResponse[@"ReviewStatistics"]]; - +- (nonnull id)initWithApiResponse:(nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + _apiResponse = apiResponse; + self.brand = [[BVBrand alloc] initWithApiResponse:apiResponse[@"Brand"]]; + + SET_IF_NOT_NULL(self.productDescription, apiResponse[@"Description"]) + SET_IF_NOT_NULL(self.brandExternalId, apiResponse[@"BrandExternalId"]) + SET_IF_NOT_NULL(self.productPageUrl, apiResponse[@"ProductPageUrl"]) + SET_IF_NOT_NULL(self.name, apiResponse[@"Name"]) + SET_IF_NOT_NULL(self.categoryId, apiResponse[@"CategoryId"]) + SET_IF_NOT_NULL(self.identifier, apiResponse[@"Id"]) + SET_IF_NOT_NULL(self.imageUrl, apiResponse[@"ImageUrl"]) + SET_IF_NOT_NULL(self.attributes, apiResponse[@"Attributes"]) + + if (self.attributes) { + self.storeLocation = + [[BVStoreLocation alloc] initWithStoreAtrributes:self.attributes]; } - return self; + + self.reviewStatistics = [[BVReviewStatistics alloc] + initWithApiResponse:apiResponse[@"ReviewStatistics"]]; + } + return self; } +- (nullable CLLocation *)getCLLocation { + CGFloat storeLongitude = 0.0; + CGFloat storeLatitute = 0.0; + CLLocation *location = nil; -- (CLLocation * _Nullable)getCLLocation { - - CGFloat storeLongitude = 0.0; - CGFloat storeLatitute = 0.0; - CLLocation *location = nil; - - if (self.storeLocation != nil){ - storeLongitude = [self.storeLocation.longitude floatValue]; - storeLatitute = [self.storeLocation.latitude floatValue]; - } - - if (storeLongitude != 0.0 && storeLatitute != 0.0){ - location = [[CLLocation alloc] initWithLatitude:storeLatitute longitude:storeLongitude]; - } - - return location; + if (self.storeLocation != nil) { + storeLongitude = [self.storeLocation.longitude floatValue]; + storeLatitute = [self.storeLocation.latitude floatValue]; + } + + if (storeLongitude != 0.0 && storeLatitute != 0.0) { + location = [[CLLocation alloc] initWithLatitude:storeLatitute + longitude:storeLongitude]; + } + + return location; } - (CLLocationDistance)distanceInMetersFromCurrentLocation { - - CLLocationDistance distanceMeters = -1.0; - - CLLocation *storeLocation = [self getCLLocation]; - - if (storeLocation && self.deviceLocation){ - distanceMeters = [storeLocation distanceFromLocation:self.deviceLocation]; - } - - return distanceMeters; + CLLocationDistance distanceMeters = -1.0; + + CLLocation *storeLocation = [self getCLLocation]; + + if (storeLocation && self.deviceLocation) { + distanceMeters = [storeLocation distanceFromLocation:self.deviceLocation]; + } + + return distanceMeters; } +- (BOOL)hasGeoLoation { + BOOL hasLocation = NO; -- (BOOL)hasGeoLoation{ - - BOOL hasLocation = NO; - - if (self.storeLocation != nil && self.storeLocation.longitude != nil && self.storeLocation.latitude != nil){ - hasLocation = YES; - } - - return hasLocation; + if (self.storeLocation != nil && self.storeLocation.longitude != nil && + self.storeLocation.latitude != nil) { + hasLocation = YES; + } + + return hasLocation; } @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.h b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.h index 67b6dda3..abeb5dce 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.h +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.h @@ -9,19 +9,20 @@ #import /** - A BVStoreLocation object is used to define the geo-location attributes of a BVStore object. + A BVStoreLocation object is used to define the geo-location attributes of a + BVStore object. */ @interface BVStoreLocation : NSObject -@property NSString* _Nullable latitude; -@property NSString* _Nullable longitude; -@property NSString* _Nullable address; -@property NSString* _Nullable city; -@property NSString* _Nullable state; -@property NSString* _Nullable country; -@property NSString* _Nullable postalcode; -@property NSString* _Nullable phone; - --(id _Nullable)initWithStoreAtrributes:(NSDictionary * _Nonnull)attributes; +@property(nullable) NSString *latitude; +@property(nullable) NSString *longitude; +@property(nullable) NSString *address; +@property(nullable) NSString *city; +@property(nullable) NSString *state; +@property(nullable) NSString *country; +@property(nullable) NSString *postalcode; +@property(nullable) NSString *phone; + +- (nullable id)initWithStoreAtrributes:(nonnull NSDictionary *)attributes; @end diff --git a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.m b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.m index 29a3af85..b82b5cd1 100644 --- a/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.m +++ b/Pod/BVConversations/Display/Model/GenericConversationsResult/Stores/BVStoreLocation.m @@ -10,34 +10,36 @@ @implementation BVStoreLocation --(id _Nullable)initWithStoreAtrributes:(NSDictionary* _Nonnull)attributes { - - self = [super init]; - if (self) { - self.longitude = [self getValueForKey:@"Longitude" withDictionary:attributes]; - self.latitude = [self getValueForKey:@"Latitude" withDictionary:attributes]; - self.country = [self getValueForKey:@"Country" withDictionary:attributes]; - self.city = [self getValueForKey:@"City" withDictionary:attributes]; - self.postalcode = [self getValueForKey:@"PostalCode" withDictionary:attributes]; - self.phone = [self getValueForKey:@"Phone" withDictionary:attributes]; - self.address = [self getValueForKey:@"Address" withDictionary:attributes]; - self.state = [self getValueForKey:@"State" withDictionary:attributes]; - } - return self; - +- (nullable id)initWithStoreAtrributes:(nonnull NSDictionary *)attributes { + + self = [super init]; + if (self) { + self.longitude = + [self getValueForKey:@"Longitude" withDictionary:attributes]; + self.latitude = [self getValueForKey:@"Latitude" withDictionary:attributes]; + self.country = [self getValueForKey:@"Country" withDictionary:attributes]; + self.city = [self getValueForKey:@"City" withDictionary:attributes]; + self.postalcode = + [self getValueForKey:@"PostalCode" withDictionary:attributes]; + self.phone = [self getValueForKey:@"Phone" withDictionary:attributes]; + self.address = [self getValueForKey:@"Address" withDictionary:attributes]; + self.state = [self getValueForKey:@"State" withDictionary:attributes]; + } + return self; } -- (NSString * _Nullable)getValueForKey:(NSString *)key withDictionary:(NSDictionary *)dict{ - NSString *value; +- (nullable NSString *)getValueForKey:(NSString *)key + withDictionary:(NSDictionary *)dict { + NSString *value; - if ([dict objectForKey:key]){ - NSArray *valuesArray = [[dict objectForKey:key] objectForKey:@"Values"]; - if (valuesArray && valuesArray.count){ - value = [valuesArray[0] objectForKey:@"Value"]; - } + if ([dict objectForKey:key]) { + NSArray *valuesArray = [[dict objectForKey:key] objectForKey:@"Values"]; + if (valuesArray && valuesArray.count) { + value = [valuesArray[0] objectForKey:@"Value"]; } - - return value; + } + + return value; } - + @end diff --git a/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.h b/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.h index ff4ff295..d5db9489 100644 --- a/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.h +++ b/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.h @@ -5,10 +5,9 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVErrorCode.h" +#import @interface NSError (BVErrorCodeParser) --(BVErrorCode)bvErrorCode; +- (BVErrorCode)bvErrorCode; @end - diff --git a/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.m b/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.m index 519c1a0c..023f0ab0 100644 --- a/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.m +++ b/Pod/BVConversations/Display/Model/NSError+BVErrorCodeParser.m @@ -5,66 +5,65 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import "NSError+BVErrorCodeParser.h" #import "BVConversationsError.h" +#import "NSError+BVErrorCodeParser.h" @implementation NSError (BVErrorCodeParser) --(BVErrorCode)bvErrorCode -{ - NSString* code = [self userInfo][BVKeyErrorCode]; - if (!code) { - return BVErrorCodeUnknown; - } - if([code isEqualToString:@"ERROR_BAD_REQUEST"]) { - return BVErrorCodeBadRequest; - } - if([code isEqualToString:@"ERROR_ACCESS_DENIED"]) { - return BVErrorCodeAccessDenied; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_API_KEY"]) { - return BVErrorCodeParamInvalidApiKey; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_LOCALE"]) { - return BVErrorCodeParamInvalidLocale; - } - if([code isEqualToString:@"ERROR_REQUEST_LIMIT_REACHED"]) { - return BVErrorCodeRequestLimitReached; - } - if([code isEqualToString:@"ERROR_UNSUPPORTED"]) { - return BVErrorCodeUnsupported; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_CALLBACK"]) { - return BVErrorCodeParamInvalidCallback; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_FILTER_ATTRIBUTE"]) { - return BVErrorCodeParamInvalidFilterAttribute; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_INCLUDED"]) { - return BVErrorCodeParamInvalidIncluded; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_LIMIT"]) { - return BVErrorCodeParamInvalidLimit; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_OFFSET"]) { - return BVErrorCodeParamInvalidOffset; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_SEARCH_ATTRIBUTE"]) { - return BVErrorCodeParamInvalidSearchAttribute; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_SORT_ATTRIBUTE"]) { - return BVErrorCodeParamInvalidSortAttribute; - } - if([code isEqualToString:@"ERROR_DUPLICATE_SUBMISSION"]) { - return BVErrorCodeDuplicateSubmission; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_PARAMETERS"]) { - return BVErrorCodeParamInvalidParameters; - } - if([code isEqualToString:@"ERROR_PARAM_MISSING_USER_ID"]) { - return BVErrorCodeParamMissingUserId; - } +- (BVErrorCode)bvErrorCode { + NSString *code = [self userInfo][BVKeyErrorCode]; + if (!code) { return BVErrorCodeUnknown; + } + if ([code isEqualToString:@"ERROR_BAD_REQUEST"]) { + return BVErrorCodeBadRequest; + } + if ([code isEqualToString:@"ERROR_ACCESS_DENIED"]) { + return BVErrorCodeAccessDenied; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_API_KEY"]) { + return BVErrorCodeParamInvalidApiKey; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_LOCALE"]) { + return BVErrorCodeParamInvalidLocale; + } + if ([code isEqualToString:@"ERROR_REQUEST_LIMIT_REACHED"]) { + return BVErrorCodeRequestLimitReached; + } + if ([code isEqualToString:@"ERROR_UNSUPPORTED"]) { + return BVErrorCodeUnsupported; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_CALLBACK"]) { + return BVErrorCodeParamInvalidCallback; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_FILTER_ATTRIBUTE"]) { + return BVErrorCodeParamInvalidFilterAttribute; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_INCLUDED"]) { + return BVErrorCodeParamInvalidIncluded; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_LIMIT"]) { + return BVErrorCodeParamInvalidLimit; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_OFFSET"]) { + return BVErrorCodeParamInvalidOffset; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_SEARCH_ATTRIBUTE"]) { + return BVErrorCodeParamInvalidSearchAttribute; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_SORT_ATTRIBUTE"]) { + return BVErrorCodeParamInvalidSortAttribute; + } + if ([code isEqualToString:@"ERROR_DUPLICATE_SUBMISSION"]) { + return BVErrorCodeDuplicateSubmission; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_PARAMETERS"]) { + return BVErrorCodeParamInvalidParameters; + } + if ([code isEqualToString:@"ERROR_PARAM_MISSING_USER_ID"]) { + return BVErrorCodeParamMissingUserId; + } + return BVErrorCodeUnknown; } @end diff --git a/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.h b/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.h index 86807c76..4a5a9e0b 100644 --- a/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.h +++ b/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.h @@ -5,15 +5,18 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import +#import "BVBaseConversationsResponse.h" #import "BVResponse.h" #import "BVStore.h" -#import "BVBaseConversationsResponse.h" +#import /** - A response to a `BVBulkStoresRequest`. Contains one or multiple (up to 20) `BVStore` objects in the `results` array. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVBulkStoresRequest`. Contains one or multiple (up to 20) + `BVStore` objects in the `results` array. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). */ -@interface BVBulkStoresResponse : BVBaseConversationsResultsResponse +@interface BVBulkStoresResponse : BVBaseConversationsResultsResponse + -@end + @end diff --git a/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.m b/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.m index 133cb8c1..37da5901 100644 --- a/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.m +++ b/Pod/BVConversations/Display/Model/Stores/BVBulkStoresResponse.m @@ -11,8 +11,9 @@ @implementation BVBulkStoresResponse --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVStore alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVStore alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.h b/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.h index e081e9ff..6a797ecb 100644 --- a/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.h +++ b/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.h @@ -5,21 +5,26 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import +#import "BVBaseConversationsResponse.h" #import "BVResponse.h" #import "BVReview.h" #import "BVStore.h" -#import "BVBaseConversationsResponse.h" +#import /** - A response to a `BVStoreReviewsRequest`. Contains one or multiple (up to 20) `BVReview` objects in the `results` array. - Contains other response information like the current index of pagination (`offset` property), and how many total results + A response to a `BVStoreReviewsRequest`. Contains one or multiple (up to 20) + `BVReview` objects in the `results` array. + Contains other response information like the current index of pagination + (`offset` property), and how many total results are available (`totalResults` property). - - The `BVStore` is also available and non-nil as long as the results array is not empty. The `BVStore` property may also have `BVReviewStatistics` associated with it if the request indicitated to include review statistics. + + The `BVStore` is also available and non-nil as long as the results array is not + empty. The `BVStore` property may also have `BVReviewStatistics` associated + with it if the request indicitated to include review statistics. */ -@interface BVStoreReviewsResponse : BVBaseConversationsResultsResponse +@interface BVStoreReviewsResponse + : BVBaseConversationsResultsResponse -@property BVStore* _Nullable store; +@property(nullable) BVStore *store; @end diff --git a/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.m b/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.m index d3670e07..03ea5d25 100644 --- a/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.m +++ b/Pod/BVConversations/Display/Model/Stores/BVStoreReviewsResponse.m @@ -6,41 +6,40 @@ // #import "BVStoreReviewsResponse.h" -#import "BVReview.h" #import "BVConversationsInclude.h" #import "BVNullHelper.h" +#import "BVReview.h" #import "BVStore.h" @implementation BVStoreReviewsResponse --(id)initWithApiResponse:(NSDictionary *)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - if(self){ - NSDictionary* rawIncludes = apiResponse[@"Includes"]; - self.store = [self extractStoreFromIncludes:rawIncludes]; - } - return self; - +- (id)initWithApiResponse:(NSDictionary *)apiResponse { + self = [super initWithApiResponse:apiResponse]; + if (self) { + NSDictionary *rawIncludes = apiResponse[@"Includes"]; + self.store = [self extractStoreFromIncludes:rawIncludes]; + } + return self; } -- (BVStore *)extractStoreFromIncludes:(NSDictionary *)includesDict{ - - NSDictionary* productsDict = includesDict[@"Products"]; - for(NSString* key in productsDict){ - // We limit the initial request to only one store, so we just pick out the first store. - NSDictionary *storeDict = [productsDict objectForKey:key]; - - return [[BVStore alloc] initWithApiResponse:storeDict]; - } - - // If there are no reviews in the response, the Products dictionary holding the stores will be empty. - return nil; - +- (BVStore *)extractStoreFromIncludes:(NSDictionary *)includesDict { + NSDictionary *productsDict = includesDict[@"Products"]; + for (NSString *key in productsDict) { + // We limit the initial request to only one store, so we just pick out + // the first store. + NSDictionary *storeDict = [productsDict objectForKey:key]; + + return [[BVStore alloc] initWithApiResponse:storeDict]; + } + + // If there are no reviews in the response, the Products dictionary holding + // the stores will be empty. + return nil; } --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - return [[BVReview alloc] initWithApiResponse:raw includes:includes]; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + return [[BVReview alloc] initWithApiResponse:raw includes:includes]; } @end diff --git a/Pod/BVConversations/Display/PDPContentType.h b/Pod/BVConversations/Display/PDPContentType.h index 8e96b745..fcf8f0cd 100644 --- a/Pod/BVConversations/Display/PDPContentType.h +++ b/Pod/BVConversations/Display/PDPContentType.h @@ -11,13 +11,13 @@ Types of Bazaarvoice content. */ typedef NS_ENUM(NSInteger, PDPContentType) { - PDPContentTypeReviews, - PDPContentTypeQuestions, - PDPContentTypeAnswers + PDPContentTypeReviews, + PDPContentTypeQuestions, + PDPContentTypeAnswers }; @interface PDPContentTypeUtil : NSObject -+(NSString*)toString:(PDPContentType)type; ++ (NSString *)toString:(PDPContentType)type; @end diff --git a/Pod/BVConversations/Display/PDPContentType.m b/Pod/BVConversations/Display/PDPContentType.m index 3805a1fe..25c73a53 100644 --- a/Pod/BVConversations/Display/PDPContentType.m +++ b/Pod/BVConversations/Display/PDPContentType.m @@ -9,15 +9,15 @@ @implementation PDPContentTypeUtil -+(NSString*)toString:(PDPContentType)type { - switch (type) { - case PDPContentTypeReviews: - return @"Reviews"; - case PDPContentTypeAnswers: - return @"Answers"; - case PDPContentTypeQuestions: - return @"Questions"; - } ++ (NSString *)toString:(PDPContentType)type { + switch (type) { + case PDPContentTypeReviews: + return @"Reviews"; + case PDPContentTypeAnswers: + return @"Answers"; + case PDPContentTypeQuestions: + return @"Questions"; + } } @end diff --git a/Pod/BVConversations/Display/PDPInclude.h b/Pod/BVConversations/Display/PDPInclude.h index 5207d494..d00ee024 100644 --- a/Pod/BVConversations/Display/PDPInclude.h +++ b/Pod/BVConversations/Display/PDPInclude.h @@ -5,16 +5,17 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "PDPContentType.h" +#import /// Internal class - used only within BVSDK @interface PDPInclude : NSObject @property PDPContentType type; -@property NSNumber* _Nullable limit; +@property(nullable) NSNumber *limit; --(id _Nonnull)initWithContentType:(PDPContentType)type limit:(NSNumber* _Nullable)limit; --(NSString* _Nonnull)toParamString; +- (nonnull id)initWithContentType:(PDPContentType)type + limit:(nullable NSNumber *)limit; +- (nonnull NSString *)toParamString; @end diff --git a/Pod/BVConversations/Display/PDPInclude.m b/Pod/BVConversations/Display/PDPInclude.m index 9ab4d123..7d4521ad 100644 --- a/Pod/BVConversations/Display/PDPInclude.m +++ b/Pod/BVConversations/Display/PDPInclude.m @@ -9,17 +9,18 @@ @implementation PDPInclude --(id _Nonnull)initWithContentType:(PDPContentType)type limit:(NSNumber* _Nullable)limit { - self = [super init]; - if(self){ - self.type = type; - self.limit = limit; - } - return self; +- (nonnull id)initWithContentType:(PDPContentType)type + limit:(nullable NSNumber *)limit { + self = [super init]; + if (self) { + self.type = type; + self.limit = limit; + } + return self; } --(NSString* _Nonnull)toParamString { - return [PDPContentTypeUtil toString:self.type]; +- (nonnull NSString *)toParamString { + return [PDPContentTypeUtil toString:self.type]; } @end diff --git a/Pod/BVConversations/Display/Requests/BVAuthorRequest.h b/Pod/BVConversations/Display/Requests/BVAuthorRequest.h index 091057cd..0bae0707 100644 --- a/Pod/BVConversations/Display/Requests/BVAuthorRequest.h +++ b/Pod/BVConversations/Display/Requests/BVAuthorRequest.h @@ -5,42 +5,57 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import "BVConversationsRequest.h" -#import "BVAuthorResponse.h" #import "BVAuthorContentType.h" -#import "BVSortOptionReviews.h" -#import "BVSortOptionQuestions.h" -#import "BVSortOptionAnswers.h" +#import "BVAuthorResponse.h" +#import "BVConversationsRequest.h" #import "BVSort.h" +#import "BVSortOptionAnswers.h" +#import "BVSortOptionQuestions.h" +#import "BVSortOptionReviews.h" -/** This class allow you to build request parameters and request a user profile (author) and accepted content (Reviews, Questions, Answers) the author has submitted. The request builder conforms to parameters described in https://developer.bazaarvoice.com/docs/read/conversations_api/reference/latest/profiles/display#parameters */ +/** This class allow you to build request parameters and request a user profile + * (author) and accepted content (Reviews, Questions, Answers) the author has + * submitted. The request builder conforms to parameters described in + * https://developer.bazaarvoice.com/docs/read/conversations_api/reference/latest/profiles/display#parameters + */ @interface BVAuthorRequest : BVConversationsRequest -@property (readonly) NSString* _Nonnull authorId; +@property(nonnull, readonly) NSString *authorId; /// The id for the author's profile you are trying to fetch -- (nonnull instancetype)initWithAuthorId:(NSString * _Nonnull)authorId; +- (nonnull instancetype)initWithAuthorId:(nonnull NSString *)authorId; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; -/// Add the statistics for the content type. Call multiple times for more than one submission type. +/// Add the statistics for the content type. Call multiple times for more than +/// one submission type. - (nonnull instancetype)includeStatistics:(BVAuthorContentType)contentType; -/// The type of submitted content to include. Call once for each type of Reviews, Questions, Answers. -- (nonnull instancetype)includeContent:(BVAuthorContentType)contentType limit:(int)limit; - -/// When Reviews are included in the response, optinally add one or more sort parameters. -- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option order:(BVSortOrder)order; -/// When Questions are included in the response, optinally add one or more sort parameters. -- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option order:(BVSortOrder)order; -/// When Answers are included in the response, optinally add one or more sort parameters. -- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option order:(BVSortOrder)order; - -/// Make an asynch http request to fetch the Author's profile data. See the BVAuthorResponse model for available fields. -- (void)load:(void (^ _Nonnull)(BVAuthorResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; +/// The type of submitted content to include. Call once for each type of +/// Reviews, Questions, Answers. +- (nonnull instancetype)includeContent:(BVAuthorContentType)contentType + limit:(int)limit; + +/// When Reviews are included in the response, optinally add one or more sort +/// parameters. +- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option + order:(BVSortOrder)order; +/// When Questions are included in the response, optinally add one or more sort +/// parameters. +- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option + order:(BVSortOrder)order; +/// When Answers are included in the response, optinally add one or more sort +/// parameters. +- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option + order:(BVSortOrder)order; + +/// Make an asynch http request to fetch the Author's profile data. See the +/// BVAuthorResponse model for available fields. +- (void)load:(nonnull void (^)(BVAuthorResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; // internal use -- (NSString * _Nonnull)endpoint; +- (nonnull NSString *)endpoint; // internal use -- (NSMutableArray * _Nonnull)createParams; +- (nonnull NSMutableArray *)createParams; @end diff --git a/Pod/BVConversations/Display/Requests/BVAuthorRequest.m b/Pod/BVConversations/Display/Requests/BVAuthorRequest.m index 37fd60a4..518b4f8c 100644 --- a/Pod/BVConversations/Display/Requests/BVAuthorRequest.m +++ b/Pod/BVConversations/Display/Requests/BVAuthorRequest.m @@ -6,229 +6,263 @@ // #import "BVAuthorRequest.h" -#import "BVFilter.h" -#import "BVSort.h" +#import "BVAuthorInclude.h" #import "BVCommaUtil.h" +#import "BVFilter.h" #import "BVLogger.h" -#import "BVAuthorInclude.h" #import "BVPixel.h" +#import "BVSort.h" @interface BVAuthorRequest () @property int limit; @property int offset; -@property NSString* _Nullable search; -@property NSMutableArray* _Nonnull filters; -@property NSMutableArray* _Nonnull authorContentTypeStatistics; -@property NSMutableArray* includes; -@property NSMutableArray* _Nonnull reviewSorts; -@property NSMutableArray* _Nonnull questionSorts; -@property NSMutableArray* _Nonnull answerSorts; +@property(nullable) NSString *search; +@property(nonnull) NSMutableArray *filters; +@property(nonnull) NSMutableArray *authorContentTypeStatistics; +@property NSMutableArray *includes; +@property(nonnull) NSMutableArray *reviewSorts; +@property(nonnull) NSMutableArray *questionSorts; +@property(nonnull) NSMutableArray *answerSorts; @end @implementation BVAuthorRequest -- (nonnull instancetype)initWithAuthorId:(NSString * _Nonnull)authorId{ - return [self initWithAuthorId:authorId limit:1 offset:0]; +- (nonnull instancetype)initWithAuthorId:(nonnull NSString *)authorId { + return [self initWithAuthorId:authorId limit:1 offset:0]; } -- (nonnull instancetype)initWithAuthorId:(NSString * _Nonnull)authorId limit:(int)limit offset:(int)offset{ - self = [super init]; - if(self){ - - self.authorContentTypeStatistics = [NSMutableArray array]; - self.filters = [NSMutableArray array]; - - self.includes = [NSMutableArray array]; - self.reviewSorts = [NSMutableArray array]; - self.questionSorts = [NSMutableArray array]; - self.answerSorts = [NSMutableArray array]; - - _authorId = [BVCommaUtil escape:authorId]; - - self.limit = (int)limit; - self.offset = (int)offset; - - self.filters = [NSMutableArray array]; - - - // filter the request to the given productId - BVFilter* filter = [[BVFilter alloc] initWithString:@"Id" filterOperator:BVFilterOperatorEqualTo values:@[self.authorId]]; - [self.filters addObject:filter]; - } - return self; +- (nonnull instancetype)initWithAuthorId:(nonnull NSString *)authorId + limit:(int)limit + offset:(int)offset { + self = [super init]; + if (self) { + self.authorContentTypeStatistics = [NSMutableArray array]; + self.filters = [NSMutableArray array]; + + self.includes = [NSMutableArray array]; + self.reviewSorts = [NSMutableArray array]; + self.questionSorts = [NSMutableArray array]; + self.answerSorts = [NSMutableArray array]; + + _authorId = [BVCommaUtil escape:authorId]; + + self.limit = (int)limit; + self.offset = (int)offset; + + self.filters = [NSMutableArray array]; + + // filter the request to the given productId + BVFilter *filter = [[BVFilter alloc] initWithString:@"Id" + filterOperator:BVFilterOperatorEqualTo + values:@[ self.authorId ]]; + [self.filters addObject:filter]; + } + return self; } - (nonnull instancetype)includeStatistics:(BVAuthorContentType)contentType { - - if (contentType == BVAuthorContentTypeReviewComments){ - NSAssert(NO, @"Including Review Comment Statistics is not supported with an authors request."); - return self; - } - - [self.authorContentTypeStatistics addObject:@(contentType)]; + if (contentType == BVAuthorContentTypeReviewComments) { + NSAssert(NO, @"Including Review Comment Statistics is not supported " + @"with an authors request."); return self; + } + + [self.authorContentTypeStatistics addObject:@(contentType)]; + return self; } -- (nonnull instancetype)includeContent:(BVAuthorContentType)contentType limit:(int)limit { - BVAuthorInclude* include = [[BVAuthorInclude alloc] initWithContentType:contentType limit:@(limit)]; - [self.includes addObject:include]; - return self; +- (nonnull instancetype)includeContent:(BVAuthorContentType)contentType + limit:(int)limit { + BVAuthorInclude *include = + [[BVAuthorInclude alloc] initWithContentType:contentType limit:@(limit)]; + [self.includes addObject:include]; + return self; } -- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionReviewUtil toString:option] order:order]; - [self.reviewSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionReviewUtil toString:option] + order:order]; + [self.reviewSorts addObject:sort]; + return self; } -- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionQuestionsUtil toString:option] order:order]; - [self.questionSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionQuestionsUtil toString:option] + order:order]; + [self.questionSorts addObject:sort]; + return self; } -- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionAnswersUtil toString:option] order:order]; - [self.answerSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionAnswersUtil toString:option] + order:order]; + [self.answerSorts addObject:sort]; + return self; } -- (void)load:(void (^ _Nonnull)(BVAuthorResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure { - - if (self.limit < 1 || self.limit > 100) { - // invalid request - [self sendError:[super limitError:self.limit] failureCallback:failure]; - } - else { - [self loadProfile:self completion:success failure:failure]; - } - +- (void)load:(nonnull void (^)(BVAuthorResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.limit < 1 || self.limit > 100) { + // invalid request + [self sendError:[super limitError:self.limit] failureCallback:failure]; + } else { + [self loadProfile:self completion:success failure:failure]; + } } -- (void)loadProfile:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVAuthorResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - BVAuthorResponse* authorResponse = [[BVAuthorResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(authorResponse); - }); - - if (authorResponse && authorResponse.results){ - [self sendAuthorAnalytics:authorResponse.results.firstObject]; - } - - } failure:failure]; +- (void) +loadProfile:(nonnull BVConversationsRequest *)request + completion:(nonnull void (^)(BVAuthorResponse *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + BVAuthorResponse *authorResponse = + [[BVAuthorResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(authorResponse); + }); + + if (authorResponse && authorResponse.results) { + [self sendAuthorAnalytics:authorResponse.results.firstObject]; + } + + } + failure:failure]; } -- (void)sendAuthorAnalytics:(BVAuthor*)author { - - if (author) { - // send usedfeature for the author display - - BVFeatureUsedEvent *event = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeConversationsProfile - withEventName:BVPixelFeatureUsedNameProfile - withAdditionalParams:@{@"interaction":@"false", - @"page":author.authorId}]; - - [BVPixel trackEvent:event]; - - } - +- (void)sendAuthorAnalytics:(BVAuthor *)author { + if (author) { + // send usedfeature for the author display + + BVFeatureUsedEvent *event = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsProfile + withEventName:BVPixelFeatureUsedNameProfile + withAdditionalParams:@{ + @"interaction" : @"false", + @"page" : author.authorId + }]; + + [BVPixel trackEvent:event]; + } } -- (NSString * _Nonnull)endpoint { - return @"authors.json"; +- (nonnull NSString *)endpoint { + return @"authors.json"; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Limit" value: [NSString stringWithFormat:@"%i", self.limit]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Offset" value: [NSString stringWithFormat:@"%i", self.offset]]]; - - for(BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; - } - - if ([self statisticsToParams:self.authorContentTypeStatistics].length > 0){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Stats" value:[self statisticsToParams:self.authorContentTypeStatistics]]]; - } - - if (self.includes.count > 0){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:[self includesToParams:self.includes]]]; - } - - for (BVAuthorInclude* include in self.includes) { - if(include.limit != nil) { - NSString* key = [NSString stringWithFormat:@"Limit_%@", [BVAuthorContentTypeUtil toString:include.type]]; - BVStringKeyValuePair* pair = [BVStringKeyValuePair pairWithKey:key value:[NSString stringWithFormat:@"%@", include.limit]]; - [params addObject:pair]; - } - } +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Limit" + value:[NSString stringWithFormat:@"%i", self.limit]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Offset" + value:[NSString + stringWithFormat:@"%i", self.offset]]]; - if (self.reviewSorts.count > 0){ - [params addObject:[self sortParams:self.reviewSorts withKey:@"Sort_Reviews"]]; - } - - if (self.questionSorts.count > 0){ - [params addObject:[self sortParams:self.questionSorts withKey:@"Sort_Questions"]]; - } - - if (self.answerSorts.count > 0){ - [params addObject:[self sortParams:self.answerSorts withKey:@"Sort_Answers"]]; + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + if ([self statisticsToParams:self.authorContentTypeStatistics].length > 0) { + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Stats" + value:[self statisticsToParams: + self.authorContentTypeStatistics]]]; + } + + if (self.includes.count > 0) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Include" + value:[self includesToParams:self.includes]]]; + } + + for (BVAuthorInclude *include in self.includes) { + if (include.limit != nil) { + NSString *key = [NSString + stringWithFormat:@"Limit_%@", + [BVAuthorContentTypeUtil toString:include.type]]; + BVStringKeyValuePair *pair = [BVStringKeyValuePair + pairWithKey:key + value:[NSString stringWithFormat:@"%@", include.limit]]; + [params addObject:pair]; } + } + + if (self.reviewSorts.count > 0) { + [params + addObject:[self sortParams:self.reviewSorts withKey:@"Sort_Reviews"]]; + } + + if (self.questionSorts.count > 0) { + [params addObject:[self sortParams:self.questionSorts + withKey:@"Sort_Questions"]]; + } - return params; - + if (self.answerSorts.count > 0) { + [params + addObject:[self sortParams:self.answerSorts withKey:@"Sort_Answers"]]; + } + + return params; } +- (nonnull NSString *)includesToParams: + (nonnull NSArray *)includes { + NSMutableArray *strings = [NSMutableArray array]; --(NSString* _Nonnull)includesToParams:(NSArray* _Nonnull)includes { - - NSMutableArray* strings = [NSMutableArray array]; - - for(BVAuthorInclude* include in includes) { - [strings addObject:[include toParamString]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - + for (BVAuthorInclude *include in includes) { + [strings addObject:[include toParamString]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; } --(NSString* _Nonnull)statisticsToParams:(NSArray* _Nonnull)statistics { - - NSMutableArray* strings = [NSMutableArray array]; - - for(NSNumber* stat in statistics) { - [strings addObject:[BVAuthorContentTypeUtil toString:[stat intValue]]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - +- (nonnull NSString *)statisticsToParams: + (nonnull NSArray *)statistics { + NSMutableArray *strings = [NSMutableArray array]; + + for (NSNumber *stat in statistics) { + [strings addObject:[BVAuthorContentTypeUtil toString:[stat intValue]]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; } --(BVStringKeyValuePair* _Nonnull)sortParams:(NSArray* _Nonnull)sorts withKey:(NSString*)paramKey { - - NSMutableArray* strings = [NSMutableArray array]; - - for(BVSort* sort in sorts) { - [strings addObject:[sort toString]]; - } - - NSString* combined = [strings componentsJoinedByString:@","]; - - return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; - +- (nonnull BVStringKeyValuePair *)sortParams:(nonnull NSArray *)sorts + withKey:(NSString *)paramKey { + NSMutableArray *strings = [NSMutableArray array]; + + for (BVSort *sort in sorts) { + [strings addObject:[sort toString]]; + } + + NSString *combined = [strings componentsJoinedByString:@","]; + + return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; } @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.h b/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.h index 7b834ab4..4cf454ec 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.h +++ b/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.h @@ -5,30 +5,30 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import -#import "BVResponse.h" #import "BVConversationsInclude.h" +#import "BVResponse.h" +#import -@interface BVBaseConversationsResponse<__covariant ResultType> : NSObject +@interface BVBaseConversationsResponse <__covariant ResultType> : NSObject -@property NSNumber* _Nullable offset; -@property NSString* _Nullable locale; -@property NSNumber* _Nullable totalResults; -@property NSNumber* _Nullable limit; +@property (nullable) NSNumber *offset; +@property(nullable) NSString *locale; +@property(nullable) NSNumber *totalResults; +@property(nullable) NSNumber *limit; -- (nonnull ResultType)createResult:(NSDictionary * _Nonnull)raw includes:(BVConversationsInclude * _Nullable)includes; +- (nonnull ResultType)createResult:(nonnull NSDictionary *)raw + includes:(nullable BVConversationsInclude *)includes; @end -@interface BVBaseConversationsResultResponse<__covariant ResultType>: BVBaseConversationsResponse +@interface BVBaseConversationsResultResponse <__covariant ResultType>: BVBaseConversationsResponse -@property ResultType _Nullable result; +@property (nullable) ResultType result; @end +@interface BVBaseConversationsResultsResponse <__covariant ResultType>: BVBaseConversationsResponse -@interface BVBaseConversationsResultsResponse<__covariant ResultType>: BVBaseConversationsResponse - -@property NSArray * _Nonnull results; +@property (nonnull) NSArray *results; @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.m b/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.m index 55751ba5..56442b84 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.m +++ b/Pod/BVConversations/Display/Requests/BVBaseConversationsResponse.m @@ -10,66 +10,66 @@ @implementation BVBaseConversationsResponse --(id)initWithApiResponse:(NSDictionary *)apiResponse { - self = [super init]; - if(self){ - - SET_IF_NOT_NULL(self.limit, apiResponse[@"Limit"]) - SET_IF_NOT_NULL(self.totalResults, apiResponse[@"TotalResults"]) - SET_IF_NOT_NULL(self.locale, apiResponse[@"Locale"]) - SET_IF_NOT_NULL(self.offset, apiResponse[@"Offset"]) - } - - return self; -} +- (id)initWithApiResponse:(NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.limit, apiResponse[@"Limit"]) + SET_IF_NOT_NULL(self.totalResults, apiResponse[@"TotalResults"]) + SET_IF_NOT_NULL(self.locale, apiResponse[@"Locale"]) + SET_IF_NOT_NULL(self.offset, apiResponse[@"Offset"]) + } --(BVConversationsInclude*)getIncludes:(NSDictionary*)apiResponse { - NSDictionary* rawIncludes = apiResponse[@"Includes"]; - BVConversationsInclude* includes = [[BVConversationsInclude alloc] initWithApiResponse:rawIncludes]; - return includes; + return self; } +- (BVConversationsInclude *)getIncludes:(NSDictionary *)apiResponse { + NSDictionary *rawIncludes = apiResponse[@"Includes"]; + BVConversationsInclude *includes = + [[BVConversationsInclude alloc] initWithApiResponse:rawIncludes]; + return includes; +} --(id)createResult:(NSDictionary *)raw includes:(BVConversationsInclude *)includes { - NSAssert(NO, @"createResult method should be overridden"); - return nil; +- (id)createResult:(NSDictionary *)raw + includes:(BVConversationsInclude *)includes { + NSAssert(NO, @"createResult method should be overridden"); + return nil; } @end @implementation BVBaseConversationsResultResponse --(id)initWithApiResponse:(NSDictionary *)apiResponse { - self = [super initWithApiResponse:apiResponse]; - if(self){ - NSArray* results = apiResponse[@"Results"]; - if (results.count) { - _result = [self createResult:results.firstObject includes:[self getIncludes:apiResponse]]; - } +- (id)initWithApiResponse:(NSDictionary *)apiResponse { + self = [super initWithApiResponse:apiResponse]; + if (self) { + NSArray *results = apiResponse[@"Results"]; + if (results.count) { + _result = [self createResult:results.firstObject + includes:[self getIncludes:apiResponse]]; } - - return self; + } + + return self; } @end @implementation BVBaseConversationsResultsResponse --(id)initWithApiResponse:(NSDictionary *)apiResponse { - self = [super initWithApiResponse:apiResponse]; - if(self){ - - NSArray* apiResults = apiResponse[@"Results"]; - BVConversationsInclude *includes = [self getIncludes:apiResponse]; - NSMutableArray *results = [NSMutableArray new]; - for (NSDictionary *result in apiResults) { - [results addObject:[self createResult:result includes:includes]]; - } - - _results = [NSArray arrayWithArray:results]; +- (id)initWithApiResponse:(NSDictionary *)apiResponse { + self = [super initWithApiResponse:apiResponse]; + if (self) { + NSArray *apiResults = apiResponse[@"Results"]; + BVConversationsInclude *includes = [self getIncludes:apiResponse]; + NSMutableArray *results = [NSMutableArray new]; + for (NSDictionary *result in apiResults) { + [results addObject:[self createResult:result includes:includes]]; } - - return self; + + _results = [NSArray arrayWithArray:results]; + } + + return self; } @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseProductRequest.h b/Pod/BVConversations/Display/Requests/BVBaseProductRequest.h index b18ccb31..711035bf 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseProductRequest.h +++ b/Pod/BVConversations/Display/Requests/BVBaseProductRequest.h @@ -5,56 +5,74 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import -#import "PDPInclude.h" -#import "BVFilterOperator.h" -#import "BVReviewFilterType.h" -#import "BVQuestionFilterType.h" +#import "BVBulkProductResponse.h" #import "BVConversationsRequest.h" #import "BVFilter.h" -#import "BVSortOptionReviews.h" -#import "BVSortOptionQuestions.h" -#import "BVSortOptionAnswers.h" +#import "BVFilterOperator.h" +#import "BVQuestionFilterType.h" +#import "BVReviewFilterType.h" #import "BVSort.h" -#import "BVBulkProductResponse.h" +#import "BVSortOptionAnswers.h" +#import "BVSortOptionQuestions.h" +#import "BVSortOptionReviews.h" +#import "PDPInclude.h" +#import -typedef void (^ProductSearchRequestCompletionHandler)(BVBulkProductResponse* _Nonnull response); +typedef void (^ProductSearchRequestCompletionHandler)( + BVBulkProductResponse *__nonnull response); @interface BVBaseProductRequest : BVConversationsRequest -/// Type of social content to inlcude with the product request. NOTE: PDPContentType is only supported for statistics, no for Includes. -- (nonnull instancetype)includeContent:(PDPContentType)contentType limit:(int)limit; +/// Type of social content to inlcude with the product request. NOTE: +/// PDPContentType is only supported for statistics, no for Includes. +- (nonnull instancetype)includeContent:(PDPContentType)contentType + limit:(int)limit; // Includes statistics for the included content type. - (nonnull instancetype)includeStatistics:(PDPContentType)contentType; /// Inclusive filter to add for included reviews. -- (nonnull instancetype)addIncludedReviewsFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; +- (nonnull instancetype)addIncludedReviewsFilter:(BVReviewFilterType)type + filterOperator: + (BVFilterOperator)filterOperator + value:(nonnull NSString *)value; /// Inclusive filter to add for included questions. -- (nonnull instancetype)addIncludedQuestionsFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; +- (nonnull instancetype)addIncludedQuestionsFilter:(BVQuestionFilterType)type + filterOperator: + (BVFilterOperator)filterOperator + value:(nonnull NSString *)value; /// Asynchronous call to fetch data for this request. -- (NSString* _Nonnull)statisticsToParams:(NSArray* _Nonnull)statistics; -- (NSString* _Nonnull)includesToParams:(NSArray* _Nonnull)includes; +- (nonnull NSString *)statisticsToParams: + (nonnull NSArray *)statistics; +- (nonnull NSString *)includesToParams: + (nonnull NSArray *)includes; @end @interface BVBaseProductsRequest : BVBaseProductRequest -- (void)load:(ProductSearchRequestCompletionHandler _Nonnull)success failure:(ConversationsFailureHandler _Nonnull)failure; +- (void)load:(nonnull ProductSearchRequestCompletionHandler)success + failure:(nonnull ConversationsFailureHandler)failure; @end @interface BVBaseSortableProductRequest : BVBaseProductsRequest - -/// When adding reviews to include, you can add a sort parameter on the included reviews. -- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option order:(BVSortOrder)order; -/// When adding questions to include, you can add a sort parameter on the included questions. -- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option order:(BVSortOrder)order; -/// When adding answers to include, you can add a sort parameter on the included answer. -- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option order:(BVSortOrder)order __deprecated_msg("Including answers is not supported on product calls."); +/// When adding reviews to include, you can add a sort parameter on the included +/// reviews. +- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option + order:(BVSortOrder)order; +/// When adding questions to include, you can add a sort parameter on the +/// included questions. +- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option + order:(BVSortOrder)order; +/// When adding answers to include, you can add a sort parameter on the included +/// answer. +- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option + order:(BVSortOrder)order + __deprecated_msg("Including answers is not supported on product calls."); @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseProductRequest.m b/Pod/BVConversations/Display/Requests/BVBaseProductRequest.m index 9e78c2f7..09ee2a61 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseProductRequest.m +++ b/Pod/BVConversations/Display/Requests/BVBaseProductRequest.m @@ -1,3 +1,5 @@ + + // // BVBaseProductRequest.m // Bazaarvoice SDK @@ -7,222 +9,262 @@ #import "BVBaseProductRequest.h" -@interface BVBaseProductRequest() +@interface BVBaseProductRequest () -@property (nonatomic, strong, nonnull, readonly) NSMutableArray* reviewFilters; -@property (nonatomic, strong, nonnull, readonly) NSMutableArray* questionFilters; -@property (nonatomic, strong, nonnull, readonly) NSMutableArray* PDPContentTypeStatistics; -@property (nonatomic, strong, nonnull, readonly) NSMutableArray* includes; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *reviewFilters; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *questionFilters; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *PDPContentTypeStatistics; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *includes; @end @implementation BVBaseProductRequest --(instancetype)init { - - if (self = [super init]) { - _includes = [NSMutableArray array]; - _reviewFilters = [NSMutableArray array]; - _questionFilters = [NSMutableArray array]; - _PDPContentTypeStatistics = [NSMutableArray array]; - } - - return self; +- (instancetype)init { + if (self = [super init]) { + _includes = [NSMutableArray array]; + _reviewFilters = [NSMutableArray array]; + _questionFilters = [NSMutableArray array]; + _PDPContentTypeStatistics = [NSMutableArray array]; + } + + return self; } --(NSString* _Nonnull)includesToParams:(NSArray* _Nonnull)includes { - - NSMutableArray* strings = [NSMutableArray array]; - - for(PDPInclude* include in includes) { - [strings addObject:[include toParamString]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - -} - --(NSString* _Nonnull)statisticsToParams:(NSArray* _Nonnull)statistics { - - NSMutableArray* strings = [NSMutableArray array]; - - for(NSNumber* stat in statistics) { - [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - +- (nonnull NSString *)includesToParams: + (nonnull NSArray *)includes { + NSMutableArray *strings = [NSMutableArray array]; + + for (PDPInclude *include in includes) { + [strings addObject:[include toParamString]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; } -- (NSString * _Nonnull)endpoint { - return @"products.json"; +- (nonnull NSString *)statisticsToParams: + (nonnull NSArray *)statistics { + NSMutableArray *strings = [NSMutableArray array]; + + for (NSNumber *stat in statistics) { + [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; } -- (nonnull instancetype)includeContent:(PDPContentType)contentType limit:(int)limit { - PDPInclude* include = [[PDPInclude alloc] initWithContentType:contentType limit:@(limit)]; - [self.includes addObject:include]; - return self; +- (nonnull NSString *)endpoint { + return @"products.json"; +} + +- (nonnull instancetype)includeContent:(PDPContentType)contentType + limit:(int)limit { + PDPInclude *include = + [[PDPInclude alloc] initWithContentType:contentType limit:@(limit)]; + [self.includes addObject:include]; + return self; } - (nonnull instancetype)includeStatistics:(PDPContentType)contentType { - [self.PDPContentTypeStatistics addObject:@(contentType)]; - return self; -} - -- (nonnull instancetype)addIncludedReviewsFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value{ - - BVFilter* filter = [[BVFilter alloc] - initWithString:[BVReviewFilterTypeUtil toString:type] - filterOperator:filterOperator values:@[value]]; - - [self.reviewFilters addObject:filter]; - return self; -} - -- (nonnull instancetype)addIncludedQuestionsFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value{ - - BVFilter* filter = [[BVFilter alloc] - initWithString:[BVQuestionFilterTypeUtil toString:type] - filterOperator:filterOperator values:@[value]]; - - [self.questionFilters addObject:filter]; - return self; - -} - -- (NSMutableArray * _Nonnull)createParams { - NSMutableArray* params = [super createParams]; - - for(BVFilter* filter in self.reviewFilters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter_Reviews" value:[filter toParameterString]]]; - } - - for(BVFilter* filter in self.questionFilters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter_Questions" value:[filter toParameterString]]]; - } - - if (self.includes.count > 0){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:[self includesToParams:self.includes]]]; - } - - if ([self statisticsToParams:self.PDPContentTypeStatistics].length > 0){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Stats" value:[self statisticsToParams:self.PDPContentTypeStatistics]]]; - } - - for (PDPInclude* include in self.includes) { - if(include.limit != nil) { - NSString* key = [NSString stringWithFormat:@"Limit_%@", [PDPContentTypeUtil toString:include.type]]; - BVStringKeyValuePair* pair = [BVStringKeyValuePair pairWithKey:key value:[NSString stringWithFormat:@"%@", include.limit]]; - [params addObject:pair]; - } + [self.PDPContentTypeStatistics addObject:@(contentType)]; + return self; +} + +- (nonnull instancetype)addIncludedReviewsFilter:(BVReviewFilterType)type + filterOperator: + (BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVReviewFilterTypeUtil toString:type] + filterOperator:filterOperator + values:@[ value ]]; + + [self.reviewFilters addObject:filter]; + return self; +} + +- (nonnull instancetype)addIncludedQuestionsFilter:(BVQuestionFilterType)type + filterOperator: + (BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVQuestionFilterTypeUtil toString:type] + filterOperator:filterOperator + values:@[ value ]]; + + [self.questionFilters addObject:filter]; + return self; +} + +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + for (BVFilter *filter in self.reviewFilters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter_Reviews" + value:[filter toParameterString]]]; + } + + for (BVFilter *filter in self.questionFilters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter_Questions" + value:[filter toParameterString]]]; + } + + if (self.includes.count > 0) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Include" + value:[self includesToParams:self.includes]]]; + } + + if ([self statisticsToParams:self.PDPContentTypeStatistics].length > 0) { + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Stats" + value:[self statisticsToParams: + self.PDPContentTypeStatistics]]]; + } + + for (PDPInclude *include in self.includes) { + if (include.limit != nil) { + NSString *key = [NSString + stringWithFormat:@"Limit_%@", + [PDPContentTypeUtil toString:include.type]]; + BVStringKeyValuePair *pair = [BVStringKeyValuePair + pairWithKey:key + value:[NSString stringWithFormat:@"%@", include.limit]]; + [params addObject:pair]; } - - return params; + } + + return params; } @end -@interface BVBaseSortableProductRequest() +@interface BVBaseSortableProductRequest () -@property NSMutableArray* _Nonnull reviewSorts; -@property NSMutableArray* _Nonnull questionSorts; -@property NSMutableArray* _Nonnull answerSorts; +@property(nonnull) NSMutableArray *reviewSorts; +@property(nonnull) NSMutableArray *questionSorts; +@property(nonnull) NSMutableArray *answerSorts; @end @implementation BVBaseProductsRequest --(void)load:(ProductSearchRequestCompletionHandler)success failure:(ConversationsFailureHandler)failure { - [self loadContent:self completion:^(NSDictionary * _Nonnull response) { - BVBulkProductResponse* productsResponse = [[BVBulkProductResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - success(productsResponse); - }); - - } failure:failure]; +- (void)load:(ProductSearchRequestCompletionHandler)success + failure:(ConversationsFailureHandler)failure { + [self loadContent:self + completion:^(NSDictionary *__nonnull response) { + BVBulkProductResponse *productsResponse = + [[BVBulkProductResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + success(productsResponse); + }); + + } + failure:failure]; } @end @implementation BVBaseSortableProductRequest --(instancetype)init { - - if (self = [super init]) { - self.reviewSorts = [NSMutableArray array]; - self.questionSorts = [NSMutableArray array]; - self.answerSorts = [NSMutableArray array]; - } - - return self; +- (instancetype)init { + if (self = [super init]) { + self.reviewSorts = [NSMutableArray array]; + self.questionSorts = [NSMutableArray array]; + self.answerSorts = [NSMutableArray array]; + } + + return self; } --(void)load:(ProductSearchRequestCompletionHandler)success failure:(ConversationsFailureHandler)failure { - [self loadContent:self completion:^(NSDictionary * _Nonnull response) { - BVBulkProductResponse* productsResponse = [[BVBulkProductResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - success(productsResponse); - }); - - } failure:failure]; +- (void)load:(ProductSearchRequestCompletionHandler)success + failure:(ConversationsFailureHandler)failure { + [self loadContent:self + completion:^(NSDictionary *__nonnull response) { + BVBulkProductResponse *productsResponse = + [[BVBulkProductResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + success(productsResponse); + }); + + } + failure:failure]; } +- (nonnull BVStringKeyValuePair *)sortParams:(nonnull NSArray *)sorts + withKey:(NSString *)paramKey { + NSMutableArray *strings = [NSMutableArray array]; --(BVStringKeyValuePair* _Nonnull)sortParams:(NSArray* _Nonnull)sorts withKey:(NSString*)paramKey { - - NSMutableArray* strings = [NSMutableArray array]; - - for(BVSort* sort in sorts) { - [strings addObject:[sort toString]]; - } - - NSString* combined = [strings componentsJoinedByString:@","]; - - return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; - + for (BVSort *sort in sorts) { + [strings addObject:[sort toString]]; + } + + NSString *combined = [strings componentsJoinedByString:@","]; + + return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; } -- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionReviewUtil toString:option] order:order]; - [self.reviewSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedReviews:(BVSortOptionReviews)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionReviewUtil toString:option] + order:order]; + [self.reviewSorts addObject:sort]; + return self; } -- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionQuestionsUtil toString:option] order:order]; - [self.questionSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedQuestions:(BVSortOptionQuestions)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionQuestionsUtil toString:option] + order:order]; + [self.questionSorts addObject:sort]; + return self; } -- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionAnswersUtil toString:option] order:order]; - [self.answerSorts addObject:sort]; - return self; +- (nonnull instancetype)sortIncludedAnswers:(BVSortOptionAnswers)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionAnswersUtil toString:option] + order:order]; + [self.answerSorts addObject:sort]; + return self; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - - if (self.reviewSorts.count > 0){ - [params addObject:[self sortParams:self.reviewSorts withKey:@"Sort_Reviews"]]; - } - - if (self.questionSorts.count > 0){ - [params addObject:[self sortParams:self.questionSorts withKey:@"Sort_Questions"]]; - } - - if (self.answerSorts.count > 0){ - [params addObject:[self sortParams:self.answerSorts withKey:@"Sort_Answers"]]; - } - - return params; +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + if (self.reviewSorts.count > 0) { + [params + addObject:[self sortParams:self.reviewSorts withKey:@"Sort_Reviews"]]; + } + + if (self.questionSorts.count > 0) { + [params addObject:[self sortParams:self.questionSorts + withKey:@"Sort_Questions"]]; + } + + if (self.answerSorts.count > 0) { + [params + addObject:[self sortParams:self.answerSorts withKey:@"Sort_Answers"]]; + } + + return params; } @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.h b/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.h index e52e012a..a59bfd73 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.h +++ b/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.h @@ -5,45 +5,58 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import #import "BVConversationsRequest.h" -#import "BVSort.h" -#import "BVSortOptionReviews.h" -#import "BVReviewFilterType.h" #import "BVFilterOperator.h" #import "BVResponse.h" +#import "BVReviewFilterType.h" #import "BVReviewIncludeType.h" +#import "BVSort.h" +#import "BVSortOptionReviews.h" +#import @class BVFilter; -@interface BVBaseReviewsRequest<__covariant BVResponseType: id> : BVConversationsRequest +@interface BVBaseReviewsRequest < __covariant BVResponseType : id +> : BVConversationsRequest -- (nonnull instancetype) __unavailable init; -- (nonnull instancetype)initWithID:(NSString * _Nonnull)ID limit:(int)limit offset:(int)offset; + - (nonnull instancetype)__unavailable init; +- (nonnull instancetype)initWithID:(nonnull NSString *)ID + limit:(int)limit + offset:(int)offset; -@property (nonatomic, assign, readonly) int limit; -@property (nonatomic, assign, readonly) int offset; -@property (nonatomic, strong, readonly) NSString* _Nullable ID; -@property (nonatomic, strong, readonly) NSString* _Nullable search; -@property (nonatomic, strong, readonly) NSMutableArray* _Nonnull sorts; -@property (nonatomic, strong, readonly) NSMutableArray* _Nonnull filters; -@property (nonatomic, strong, readonly) NSMutableArray * _Nonnull includes; +@property(nonatomic, assign, readonly) int limit; +@property(nonatomic, assign, readonly) int offset; +@property(nullable, nonatomic, strong, readonly) NSString *ID; +@property(nullable, nonatomic, strong, readonly) NSString *search; +@property(nonnull, nonatomic, strong, readonly) NSMutableArray *sorts; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *filters; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *includes; -- (void)sendReviewResultsAnalytics:(NSArray * _Nonnull)reviews; -- (void)sendReviewsAnalytics:(BVReviewsResponse* _Nonnull)reviewsResponse; -- (nonnull instancetype)search:(NSString * _Nonnull)search; +- (void)sendReviewResultsAnalytics:(nonnull NSArray *)reviews; +- (void)sendReviewsAnalytics:(nonnull BVReviewsResponse *)reviewsResponse; +- (nonnull instancetype)search:(nonnull NSString *)search; - (nonnull instancetype)addInclude:(BVReviewIncludeType)include; -- (nonnull instancetype)addSort:(BVSortOptionProducts)option order:(BVSortOrder)order __deprecated_msg("use sortReviews instead"); +- (nonnull instancetype)addSort:(BVSortOptionProducts)option + order:(BVSortOrder)order + __deprecated_msg("use sortReviews instead"); -- (nonnull instancetype)addReviewSort:(BVSortOptionReviews)option order:(BVSortOrder)order; +- (nonnull instancetype)addReviewSort:(BVSortOptionReviews)option + order:(BVSortOrder)order; -- (nonnull instancetype)addFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; -- (nonnull instancetype)addFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values; +- (nonnull instancetype)addFilter:(BVReviewFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value; +- (nonnull instancetype)addFilter:(BVReviewFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; -- (void)load:(void (^ _Nonnull)(BVResponseType _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; +- (void)load:(nonnull void (^)(BVResponseType __nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; -- (nonnull BVResponseType)createResponse:(NSDictionary * _Nonnull)raw; +- (nonnull BVResponseType)createResponse:(nonnull NSDictionary *)raw; @end diff --git a/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.m b/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.m index dd50e24e..5420697b 100644 --- a/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.m +++ b/Pod/BVConversations/Display/Requests/BVBaseReviewsRequest.m @@ -1,3 +1,4 @@ + // // BVBaseReviewsRequest.m // Bazaarvoice SDK @@ -7,172 +8,201 @@ #import "BVBaseReviewsRequest.h" #import "BVAnalyticsManager.h" -#import "BVSort.h" +#import "BVCommaUtil.h" #import "BVCore.h" #import "BVFilter.h" -#import "BVCommaUtil.h" +#import "BVSort.h" @implementation BVBaseReviewsRequest -- (nonnull instancetype)initWithID:(NSString * _Nonnull)ID limit:(int)limit offset:(int)offset { - if (self = [super init]) { - [self initDefaultProps:ID]; - _limit = limit; - _offset = offset; - } - return self; +- (nonnull instancetype)initWithID:(nonnull NSString *)ID + limit:(int)limit + offset:(int)offset { + if (self = [super init]) { + [self initDefaultProps:ID]; + _limit = limit; + _offset = offset; + } + return self; } -- (void)initDefaultProps:(NSString *)ID{ - - _ID = [BVCommaUtil escape:ID]; - - _filters = [NSMutableArray array]; - _sorts = [NSMutableArray array]; - _includes = [NSMutableArray array]; - - // filter the request to the given productId - BVFilter* filter = [[BVFilter alloc] initWithString:@"ProductId" filterOperator:BVFilterOperatorEqualTo values:@[_ID]]; - [self.filters addObject:filter]; -} +- (void)initDefaultProps:(NSString *)ID { + _ID = [BVCommaUtil escape:ID]; + _filters = [NSMutableArray array]; + _sorts = [NSMutableArray array]; + _includes = [NSMutableArray array]; -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - - [params addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Limit" value: [NSString stringWithFormat:@"%i", self.limit]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Offset" value: [NSString stringWithFormat:@"%i", self.offset]]]; - - for (BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; - } - - for (NSString*include in self.includes){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:include]]; - } - - if ([self.sorts count] > 0) { - NSMutableArray* sortsAsStrings = [NSMutableArray array]; - for (BVSort* sort in self.sorts) { - [sortsAsStrings addObject:[sort toString]]; - } - NSString* allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" value:allTogetherNow]]; + // filter the request to the given productId + BVFilter *filter = [[BVFilter alloc] initWithString:@"ProductId" + filterOperator:BVFilterOperatorEqualTo + values:@[ _ID ]]; + [self.filters addObject:filter]; +} + +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Limit" + value:[NSString stringWithFormat:@"%i", self.limit]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Offset" + value:[NSString + stringWithFormat:@"%i", self.offset]]]; + + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + for (NSString *include in self.includes) { + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:include]]; + } + + if ([self.sorts count] > 0) { + NSMutableArray *sortsAsStrings = [NSMutableArray array]; + for (BVSort *sort in self.sorts) { + [sortsAsStrings addObject:[sort toString]]; } + NSString *allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" + value:allTogetherNow]]; + } - - return params; + return params; } -- (NSString * _Nonnull)endpoint { - return @"reviews.json"; +- (nonnull NSString *)endpoint { + return @"reviews.json"; } -- (nonnull instancetype)search:(NSString * _Nonnull)search { - // this invalidates sort options - _search = search; - return self; +- (nonnull instancetype)search:(nonnull NSString *)search { + // this invalidates sort options + _search = search; + return self; } -- (nonnull instancetype)addInclude:(BVReviewIncludeType)include{ - - [self.includes addObject:[BVReviewIncludeTypeUtil toString:include]]; - - if (include == BVReviewIncludeTypeProducts){ - [self addCustomDisplayParameter:@"Stats" withValue:@"Reviews"]; // Always include stats when requesting products with reviews - } - - return self; +- (nonnull instancetype)addInclude:(BVReviewIncludeType)include { + [self.includes addObject:[BVReviewIncludeTypeUtil toString:include]]; + + if (include == BVReviewIncludeTypeProducts) { + [self addCustomDisplayParameter:@"Stats" + withValue:@"Reviews"]; // Always include stats + // when requesting products + // with reviews + } + + return self; } -- (nonnull instancetype)addSort:(BVSortOptionProducts)option order:(BVSortOrder)order { - LOG_DEPRECATED_MESSAGE(@"addSort") - BVSort* sort = [[BVSort alloc] initWithOption:option order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addSort:(BVSortOptionProducts)option + order:(BVSortOrder)order { + LOG_DEPRECATED_MESSAGE(@"addSort") + BVSort *sort = [[BVSort alloc] initWithOption:option order:order]; + [self.sorts addObject:sort]; + return self; } -- (nonnull instancetype)addReviewSort:(BVSortOptionReviews)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionReviewUtil toString:option] order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addReviewSort:(BVSortOptionReviews)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionReviewUtil toString:option] + order:order]; + [self.sorts addObject:sort]; + return self; } -- (nonnull instancetype)addFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value { - [self addFilter:type filterOperator:filterOperator values:@[value]]; - return self; +- (nonnull instancetype)addFilter:(BVReviewFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + [self addFilter:type filterOperator:filterOperator values:@[ value ]]; + return self; } -- (nonnull instancetype)addFilter:(BVReviewFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values { - BVFilter* filter = [[BVFilter alloc] initWithString:[BVReviewFilterTypeUtil toString:type] filterOperator:filterOperator values:values]; - [self.filters addObject:filter]; - return self; +- (nonnull instancetype)addFilter:(BVReviewFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVReviewFilterTypeUtil toString:type] + filterOperator:filterOperator + values:values]; + [self.filters addObject:filter]; + return self; } -- (void)load:(void (^ _Nonnull)(id _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure { - if (self.limit < 1 || self.limit > 100) { - // invalid request - [self sendError:[super limitError:self.limit] failureCallback:failure]; - } - else { - [self loadReviews:self completion:success failure:failure]; - } +- (void)load:(nonnull void (^)(id __nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.limit < 1 || self.limit > 100) { + // invalid request + [self sendError:[super limitError:self.limit] failureCallback:failure]; + } else { + [self loadReviews:self completion:success failure:failure]; + } } -- (void)loadReviews:(BVConversationsRequest *)request completion:(void (^)(id _Nonnull))completion failure:(void (^)(NSArray * _Nonnull))failure { - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - id reviewResponse = [self createResponse: response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(reviewResponse); - }); - [self sendReviewsAnalytics:reviewResponse]; - } failure:failure]; +- (void)loadReviews:(BVConversationsRequest *)request + completion:(void (^)(id nonnull))completion + failure:(void (^)(NSArray *nonnull))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + id reviewResponse = [self createResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(reviewResponse); + }); + [self sendReviewsAnalytics:reviewResponse]; + } + failure:failure]; } --(id)createResponse:(NSDictionary *)raw { - NSAssert(true, @"Should be implemented in subclasses"); - return nil; +- (id)createResponse:(NSDictionary *)raw { + NSAssert(true, @"Should be implemented in subclasses"); + return nil; } -- (void)sendReviewsAnalytics:(BVReviewsResponse*)reviewsResponse { - - [self sendReviewResultsAnalytics:reviewsResponse.results]; +- (void)sendReviewsAnalytics:(BVReviewsResponse *)reviewsResponse { + [self sendReviewResultsAnalytics:reviewsResponse.results]; } -- (void)sendReviewResultsAnalytics:(NSArray*)reviews{ - - for (BVReview* review in reviews) { - - // Record Review Impression - NSString *brandName = review.product.brand ? review.product.brand.name : nil; - BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] initWithProductId:review.productId - withContentId:review.identifier - withCategoryId:review.product.categoryId - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeReview - withBrand:brandName withAdditionalParams:nil]; - - [BVPixel trackEvent:reviewImpression]; - - } - // send pageview for product - BVReview* review = reviews.firstObject; - if (review != nil) { - NSNumber* count = @([reviews count]); - - NSDictionary *addParams = @{@"numReviews":count}; - NSString *brandName = review.product.brand ? review.product.brand.name : nil; - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:review.productId - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:brandName - withCategoryId:review.product.categoryId - withRootCategoryId:nil - withAdditionalParams:addParams]; - - [BVPixel trackEvent:pageView]; - } +- (void)sendReviewResultsAnalytics:(NSArray *)reviews { + for (BVReview *review in reviews) { + // Record Review Impression + NSString *brandName = + review.product.brand ? review.product.brand.name : nil; + BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] + initWithProductId:review.productId + withContentId:review.identifier + withCategoryId:review.product.categoryId + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeReview + withBrand:brandName + withAdditionalParams:nil]; + + [BVPixel trackEvent:reviewImpression]; + } + // send pageview for product + BVReview *review = reviews.firstObject; + if (review != nil) { + NSNumber *count = @([reviews count]); + + NSDictionary *addParams = @{@"numReviews" : count}; + NSString *brandName = + review.product.brand ? review.product.brand.name : nil; + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:review.productId + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:brandName + withCategoryId:review.product.categoryId + withRootCategoryId:nil + withAdditionalParams:addParams]; + + [BVPixel trackEvent:pageView]; + } } @end diff --git a/Pod/BVConversations/Display/Requests/BVBulkProductRequest.h b/Pod/BVConversations/Display/Requests/BVBulkProductRequest.h index a60af8be..fb0b6d74 100644 --- a/Pod/BVConversations/Display/Requests/BVBulkProductRequest.h +++ b/Pod/BVConversations/Display/Requests/BVBulkProductRequest.h @@ -5,14 +5,19 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import #import "BVBaseProductRequest.h" #import "BVProductFilterType.h" +#import @interface BVBulkProductRequest : BVBaseSortableProductRequest -- (nonnull instancetype)addProductSort:(BVSortOptionProducts)option order:(BVSortOrder)order; -- (nonnull instancetype)addFilter:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; -- (nonnull instancetype)addFilter:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values; +- (nonnull instancetype)addProductSort:(BVSortOptionProducts)option + order:(BVSortOrder)order; +- (nonnull instancetype)addFilter:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value; +- (nonnull instancetype)addFilter:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; @end diff --git a/Pod/BVConversations/Display/Requests/BVBulkProductRequest.m b/Pod/BVConversations/Display/Requests/BVBulkProductRequest.m index 945b95dd..1997da3d 100644 --- a/Pod/BVConversations/Display/Requests/BVBulkProductRequest.m +++ b/Pod/BVConversations/Display/Requests/BVBulkProductRequest.m @@ -7,70 +7,85 @@ #import "BVBulkProductRequest.h" -@interface BVBulkProductRequest() +@interface BVBulkProductRequest () -@property NSMutableArray* _Nonnull sorts; -@property (nonatomic, strong)NSMutableArray* _Nonnull filters; +@property(nonnull) NSMutableArray *sorts; +@property(nonnull, nonatomic, strong) NSMutableArray *filters; @end @implementation BVBulkProductRequest +- (instancetype)init { + if (self = [super init]) { + _sorts = [NSMutableArray new]; + _filters = [NSMutableArray new]; + } --(instancetype)init { - if (self = [super init]) { - _sorts = [NSMutableArray new]; - _filters = [NSMutableArray new]; - } - - return self; + return self; } -- (nonnull instancetype)addProductSort:(BVSortOptionProducts)option order:(BVSortOrder)order { - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionProductsUtil toString:option] order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addProductSort:(BVSortOptionProducts)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionProductsUtil toString:option] + order:order]; + [self.sorts addObject:sort]; + return self; } +- (void)load:(ProductSearchRequestCompletionHandler)success + failure:(ConversationsFailureHandler)failure { + [self loadContent:self + completion:^(NSDictionary *__nonnull response) { + BVBulkProductResponse *productsResponse = + [[BVBulkProductResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + success(productsResponse); + }); --(void)load:(ProductSearchRequestCompletionHandler)success failure:(ConversationsFailureHandler)failure { - [self loadContent:self completion:^(NSDictionary * _Nonnull response) { - BVBulkProductResponse* productsResponse = [[BVBulkProductResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - success(productsResponse); - }); - - } failure:failure]; + } + failure:failure]; } --(NSMutableArray * _Nonnull)createParams { - NSMutableArray* params = [super createParams]; - if ([self.sorts count] > 0) { - NSMutableArray* sortsAsStrings = [NSMutableArray array]; - for (BVSort* sort in self.sorts) { - [sortsAsStrings addObject:[sort toString]]; - } - NSString* allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" value:allTogetherNow]]; - } - - for(BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + if ([self.sorts count] > 0) { + NSMutableArray *sortsAsStrings = [NSMutableArray array]; + for (BVSort *sort in self.sorts) { + [sortsAsStrings addObject:[sort toString]]; } - - return params; + NSString *allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" + value:allTogetherNow]]; + } + + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + return params; } -- (nonnull instancetype)addFilter:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value { - [self addFilter:type filterOperator:filterOperator values:@[value]]; - return self; +- (nonnull instancetype)addFilter:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + [self addFilter:type filterOperator:filterOperator values:@[ value ]]; + return self; } -- (nonnull instancetype)addFilter:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values { - BVFilter* filter = [[BVFilter alloc] initWithString:[BVProductFilterTypeUtil toString:type] filterOperator:filterOperator values:values]; - [self.filters addObject:filter]; - return self; +- (nonnull instancetype)addFilter:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVProductFilterTypeUtil toString:type] + filterOperator:filterOperator + values:values]; + [self.filters addObject:filter]; + return self; } @end diff --git a/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.h b/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.h index b3626df2..7e2b923a 100644 --- a/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.h +++ b/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.h @@ -5,33 +5,41 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import +#import "BVBulkRatingsFilterType.h" #import "BVBulkRatingsResponse.h" #import "BVConversationsRequest.h" -#import "BVBulkRatingsFilterType.h" -#import "PDPInclude.h" #import "BVFilterOperator.h" +#import "PDPInclude.h" +#import typedef NS_ENUM(NSInteger, BulkRatingsStatsType) { - BulkRatingsStatsTypeReviews, - BulkRatingsStatsTypeNativeReviews, - BulkRatingsStatsTypeAll + BulkRatingsStatsTypeReviews, + BulkRatingsStatsTypeNativeReviews, + BulkRatingsStatsTypeAll }; -typedef void (^BulkRatingsSuccessHandler)(BVBulkRatingsResponse* _Nonnull response); +typedef void (^BulkRatingsSuccessHandler)( + BVBulkRatingsResponse *__nonnull response); /* - You can request many products ratings (average rating, number of reviews) with this request. + You can request many products ratings (average rating, number of reviews) with + this request. You can request up to 50 product ratings with a single request. */ @interface BVBulkRatingsRequest : BVConversationsRequest -- (nonnull instancetype)initWithProductIds:(NSArray * _Nonnull)productIds statistics:(enum BulkRatingsStatsType)statistics; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype) +initWithProductIds:(nonnull NSArray *)productIds + statistics:(enum BulkRatingsStatsType)statistics; +- (nonnull instancetype)__unavailable init; -- (NSString * _Nonnull)endpoint; -- (void)load:(void (^ _Nonnull)(BVBulkRatingsResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; -- (nonnull instancetype)addFilter:(BVBulkRatingsFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values; -- (NSMutableArray * _Nonnull)createParams; +- (nonnull NSString *)endpoint; +- (void)load: + (nonnull void (^)(BVBulkRatingsResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; +- (nonnull instancetype)addFilter:(BVBulkRatingsFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; +- (nonnull NSMutableArray *)createParams; @end diff --git a/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.m b/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.m index 25796d2a..36ff0eca 100644 --- a/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.m +++ b/Pod/BVConversations/Display/Requests/BVBulkRatingsRequest.m @@ -6,91 +6,109 @@ // #import "BVBulkRatingsRequest.h" -#import "BVFilter.h" #import "BVCommaUtil.h" +#import "BVFilter.h" #import "BVLogger.h" -@interface BVBulkRatingsRequest() +@interface BVBulkRatingsRequest () -@property NSArray* _Nonnull productIds; +@property(nonnull) NSArray *productIds; @property BulkRatingsStatsType statistics; -@property NSMutableArray* _Nonnull filters; +@property(nonnull) NSMutableArray *filters; @end @implementation BVBulkRatingsRequest -- (NSString * _Nonnull)endpoint { - return @"statistics.json"; +- (nonnull NSString *)endpoint { + return @"statistics.json"; } -- (nonnull instancetype)initWithProductIds:(NSArray * _Nonnull)productIds statistics:(enum BulkRatingsStatsType)statistics { - self = [super init]; - if(self){ - - self.productIds = [BVCommaUtil escapeMultiple:productIds]; - self.statistics = statistics; - - self.filters = [NSMutableArray array]; - BVFilter* filter = [[BVFilter alloc] initWithString:@"ProductId" filterOperator:BVFilterOperatorEqualTo values:productIds]; - [self.filters addObject:filter]; - - } - return self; +- (nonnull instancetype) +initWithProductIds:(nonnull NSArray *)productIds + statistics:(enum BulkRatingsStatsType)statistics { + self = [super init]; + if (self) { + self.productIds = [BVCommaUtil escapeMultiple:productIds]; + self.statistics = statistics; + + self.filters = [NSMutableArray array]; + BVFilter *filter = [[BVFilter alloc] initWithString:@"ProductId" + filterOperator:BVFilterOperatorEqualTo + values:productIds]; + [self.filters addObject:filter]; + } + return self; } -- (void)load:(void (^ _Nonnull)(BVBulkRatingsResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure { - - if([self.productIds count] > 100) { - // invalid request - [self sendError:[self tooManyProductsError:self.productIds] failureCallback:failure]; - } - else { - [self loadBulkRatings:self completion:success failure:failure]; - } - +- (void)load: + (nonnull void (^)(BVBulkRatingsResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + if ([self.productIds count] > 100) { + // invalid request + [self sendError:[self tooManyProductsError:self.productIds] + failureCallback:failure]; + } else { + [self loadBulkRatings:self completion:success failure:failure]; + } } -- (void)loadBulkRatings:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVBulkRatingsResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - - BVBulkRatingsResponse* bulkRatingsResponse = [[BVBulkRatingsResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(bulkRatingsResponse); - }); - - } failure:failure]; - +- (void) +loadBulkRatings:(nonnull BVConversationsRequest *)request + completion: + (nonnull void (^)(BVBulkRatingsResponse *__nonnull response))completion + failure: + (nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + + BVBulkRatingsResponse *bulkRatingsResponse = + [[BVBulkRatingsResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(bulkRatingsResponse); + }); + + } + failure:failure]; } -- (nonnull instancetype)addFilter:(BVBulkRatingsFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values { - BVFilter* filter = [[BVFilter alloc] initWithString:[BVBulkRatingsFilterTypeUtil toString:type] filterOperator:filterOperator values:values]; - [self.filters addObject:filter]; - return self; +- (nonnull instancetype)addFilter:(BVBulkRatingsFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + BVFilter *filter = [[BVFilter alloc] + initWithString:[BVBulkRatingsFilterTypeUtil toString:type] + filterOperator:filterOperator + values:values]; + [self.filters addObject:filter]; + return self; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - - [params addObject:[BVStringKeyValuePair pairWithKey:@"Stats" value:[self statsToString:self.statistics]]]; - - for(BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; - } - - return params; - +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Stats" + value:[self statsToString:self.statistics]]]; + + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + return params; } --(NSString* _Nonnull)statsToString:(BulkRatingsStatsType)stats { - switch (stats) { - case BulkRatingsStatsTypeReviews: return @"Reviews"; - case BulkRatingsStatsTypeNativeReviews: return @"NativeReviews"; - case BulkRatingsStatsTypeAll: return @"Reviews,NativeReviews"; - } +- (nonnull NSString *)statsToString:(BulkRatingsStatsType)stats { + switch (stats) { + case BulkRatingsStatsTypeReviews: + return @"Reviews"; + case BulkRatingsStatsTypeNativeReviews: + return @"NativeReviews"; + case BulkRatingsStatsTypeAll: + return @"Reviews,NativeReviews"; + } } @end diff --git a/Pod/BVConversations/Display/Requests/BVCommentsRequest.h b/Pod/BVConversations/Display/Requests/BVCommentsRequest.h index f88b029e..fbe6f323 100644 --- a/Pod/BVConversations/Display/Requests/BVCommentsRequest.h +++ b/Pod/BVConversations/Display/Requests/BVCommentsRequest.h @@ -1,3 +1,5 @@ + + // // BVCommentsRequest.h // BVSDK @@ -5,38 +7,49 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import "BVConversationsRequest.h" +#import "BVCommentFilterType.h" +#import "BVCommentIncludeType.h" #import "BVCommentsResponse.h" -#import "BVSort.h" +#import "BVConversationsRequest.h" #import "BVFilter.h" +#import "BVSort.h" #import "BVSortOptionsComments.h" -#import "BVCommentFilterType.h" -#import "BVCommentIncludeType.h" @interface BVCommentsRequest : BVConversationsRequest -@property (readonly) NSString* _Nonnull reviewId; -@property (nonatomic, assign, readonly) UInt16 limit; -@property (nonatomic, assign, readonly) UInt16 offset; -@property (nonatomic, strong, readonly) NSMutableArray* _Nonnull sorts; -@property (nonatomic, strong, readonly) NSMutableArray* _Nonnull filters; -@property (nonatomic, strong, readonly) NSMutableArray * _Nonnull includes; -@property (readonly) NSString* _Nonnull commentId; +@property(nonnull, readonly) NSString *reviewId; +@property(nonatomic, assign, readonly) UInt16 limit; +@property(nonatomic, assign, readonly) UInt16 offset; +@property(nonnull, nonatomic, strong, readonly) NSMutableArray *sorts; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *filters; +@property(nonnull, nonatomic, strong, readonly) + NSMutableArray *includes; +@property(nonnull, readonly) NSString *commentId; + +- (nonnull instancetype)initWithReviewId:(nonnull NSString *)reviewId + limit:(UInt16)limit + offset:(UInt16)offset; +- (nonnull instancetype)initWithCommentId:(nonnull NSString *)commentId; -- (nonnull instancetype)initWithReviewId:(NSString * _Nonnull)reviewId limit:(UInt16)limit offset:(UInt16)offset; -- (nonnull instancetype)initWithCommentId:(NSString * _Nonnull)commentId; - -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; -- (nonnull instancetype)addCommentSort:(BVSortOptionComments)option order:(BVSortOrder)order; +- (nonnull instancetype)addCommentSort:(BVSortOptionComments)option + order:(BVSortOrder)order; -- (nonnull instancetype)addFilter:(BVCommentFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; +- (nonnull instancetype)addFilter:(BVCommentFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value; -- (nonnull instancetype)addFilter:(BVCommentFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values; +- (nonnull instancetype)addFilter:(BVCommentFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; - (nonnull instancetype)addInclude:(BVCommentIncludeType)include; - /// Make an asynch http request to fethre the Author's profile data. See the BVAuthorResponse model for availble fields. -- (void)load:(void (^ _Nonnull)(BVCommentsResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; - +/// Make an asynch http request to fethre the Author's profile data. See the +/// BVAuthorResponse model for availble fields. +- (void)load:(nonnull void (^)(BVCommentsResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; + @end diff --git a/Pod/BVConversations/Display/Requests/BVCommentsRequest.m b/Pod/BVConversations/Display/Requests/BVCommentsRequest.m index 9b3b1d02..58c27eb3 100644 --- a/Pod/BVConversations/Display/Requests/BVCommentsRequest.m +++ b/Pod/BVConversations/Display/Requests/BVCommentsRequest.m @@ -1,3 +1,5 @@ + + // // BVCommentsRequest.m // BVSDK @@ -11,171 +13,191 @@ @implementation BVCommentsRequest -- (nonnull instancetype)initWithReviewId:(NSString * _Nonnull)reviewId limit:(UInt16)limit offset:(UInt16)offset{ - - self = [super init]; - if(self){ - - _reviewId = reviewId; - _limit = limit; - _offset = offset; - _filters = [NSMutableArray array]; - _sorts = [NSMutableArray array]; - _includes = [NSMutableArray array]; - } - - return self; +- (nonnull instancetype)initWithReviewId:(nonnull NSString *)reviewId + limit:(UInt16)limit + offset:(UInt16)offset { + self = [super init]; + if (self) { + _reviewId = reviewId; + _limit = limit; + _offset = offset; + _filters = [NSMutableArray array]; + _sorts = [NSMutableArray array]; + _includes = [NSMutableArray array]; + } + + return self; } - -- (nonnull instancetype)initWithCommentId:(NSString * _Nonnull)commentId{ - - self = [super init]; - if(self){ - - _commentId = commentId; - _filters = [NSMutableArray array]; - _sorts = [NSMutableArray array]; - _includes = [NSMutableArray array]; - } - - return self; -} - -- (void)load:(void (^ _Nonnull)(BVCommentsResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure{ - - if (self.reviewId && (self.limit < 1 || self.limit > 100)) { - // invalid request - [self sendError:[super limitError:self.limit] failureCallback:failure]; - } - else { - [self loadComments:self completion:success failure:failure]; - } - + +- (nonnull instancetype)initWithCommentId:(nonnull NSString *)commentId { + self = [super init]; + if (self) { + _commentId = commentId; + _filters = [NSMutableArray array]; + _sorts = [NSMutableArray array]; + _includes = [NSMutableArray array]; + } + + return self; } - -- (void)loadComments:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVCommentsResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - - BVCommentsResponse *commentsResponse = [[BVCommentsResponse alloc] initWithApiResponse:response]; - - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(commentsResponse); - }); - - if (commentsResponse && commentsResponse.results){ - [self sendCommentImpressionAnalytics:commentsResponse.results]; - } - - } failure:failure]; + +- (void)load:(nonnull void (^)(BVCommentsResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.reviewId && (self.limit < 1 || self.limit > 100)) { + // invalid request + [self sendError:[super limitError:self.limit] failureCallback:failure]; + } else { + [self loadComments:self completion:success failure:failure]; + } } -- (NSString * _Nonnull)endpoint { - return @"reviewcomments.json"; +- (void) +loadComments:(nonnull BVConversationsRequest *)request + completion: + (nonnull void (^)(BVCommentsResponse *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + + BVCommentsResponse *commentsResponse = + [[BVCommentsResponse alloc] initWithApiResponse:response]; + + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(commentsResponse); + }); + + if (commentsResponse && commentsResponse.results) { + [self sendCommentImpressionAnalytics:commentsResponse.results]; + } + + } + failure:failure]; } -- (void)sendCommentImpressionAnalytics:(NSArray*)comments { - - for (BVComment* comment in comments) { - - // Record Review Impression - BVImpressionEvent *commentImpression = [[BVImpressionEvent alloc] initWithProductId:comment.reviewId - withContentId:comment.commentId - withCategoryId:nil - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeComment - withBrand:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:commentImpression]; - } - - +- (nonnull NSString *)endpoint { + return @"reviewcomments.json"; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - - // There are two ways to make a request: 1) with a review Id to get a bunch of comments or 2) just get single review. - if (self.reviewId) { - - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[NSString stringWithFormat:@"reviewId:%@", self.reviewId]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Limit" value: [NSString stringWithFormat:@"%i", self.limit]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Offset" value: [NSString stringWithFormat:@"%i", self.offset]]]; - - } else if (self.commentId) { - BVFilter *commentIdFilter = [[BVFilter alloc] initWithType:BVProductFilterTypeId filterOperator:BVFilterOperatorEqualTo value:self.commentId]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[commentIdFilter toParameterString]]]; - - } else { - - NSAssert(NO, @"You must supply a valid comment or review ID in the supplied initiaizers."); - - } - - for(BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; - } - - if (self.sorts.count > 0){ - [params addObject:[self sortParams:self.sorts withKey:@"Sort"]]; - } - - if (self.includes.count > 0){ - [params addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:[self includesToParams:self.includes]]]; - } - - return params; +- (void)sendCommentImpressionAnalytics:(NSArray *)comments { + for (BVComment *comment in comments) { + // Record Review Impression + BVImpressionEvent *commentImpression = [[BVImpressionEvent alloc] + initWithProductId:comment.reviewId + withContentId:comment.commentId + withCategoryId:nil + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeComment + withBrand:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:commentImpression]; + } } +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + // There are two ways to make a request: 1) with a review Id to get a bunch + // of comments or 2) just get single review. + if (self.reviewId) { + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[NSString stringWithFormat:@"reviewId:%@", + self.reviewId]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Limit" + value:[NSString + stringWithFormat:@"%i", self.limit]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Offset" + value:[NSString stringWithFormat:@"%i", + self.offset]]]; + + } else if (self.commentId) { + BVFilter *commentIdFilter = + [[BVFilter alloc] initWithType:BVProductFilterTypeId + filterOperator:BVFilterOperatorEqualTo + value:self.commentId]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[commentIdFilter toParameterString]]]; + + } else { + NSAssert(NO, @"You must supply a valid comment or review ID in the " + @"supplied initiaizers."); + } + + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + if (self.sorts.count > 0) { + [params addObject:[self sortParams:self.sorts withKey:@"Sort"]]; + } + + if (self.includes.count > 0) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Include" + value:[self includesToParams:self.includes]]]; + } + + return params; +} -- (nonnull instancetype)addCommentSort:(BVSortOptionComments)option order:(BVSortOrder)order{ - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionsCommentUtil toString:option] order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addCommentSort:(BVSortOptionComments)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionsCommentUtil toString:option] + order:order]; + [self.sorts addObject:sort]; + return self; } -- (nonnull instancetype)addFilter:(BVCommentFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value{ - [self addFilter:type filterOperator:filterOperator values:@[value]]; - return self; +- (nonnull instancetype)addFilter:(BVCommentFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + [self addFilter:type filterOperator:filterOperator values:@[ value ]]; + return self; } -- (nonnull instancetype)addInclude:(BVCommentIncludeType)include{ - - [self.includes addObject:[BVCommentIncludeTypeUtil toString:include]]; - return self; - +- (nonnull instancetype)addInclude:(BVCommentIncludeType)include { + [self.includes addObject:[BVCommentIncludeTypeUtil toString:include]]; + return self; } -- (nonnull instancetype)addFilter:(BVCommentFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values { - BVFilter* filter = [[BVFilter alloc] initWithString:[BVCommentFilterTypeUtil toString:type] filterOperator:filterOperator values:values]; - [self.filters addObject:filter]; - return self; +- (nonnull instancetype)addFilter:(BVCommentFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVCommentFilterTypeUtil toString:type] + filterOperator:filterOperator + values:values]; + [self.filters addObject:filter]; + return self; } --(BVStringKeyValuePair* _Nonnull)sortParams:(NSArray* _Nonnull)sorts withKey:(NSString*)paramKey { - - NSMutableArray* strings = [NSMutableArray array]; - - for(BVSort* sort in sorts) { - [strings addObject:[sort toString]]; - } - - NSString* combined = [strings componentsJoinedByString:@","]; - - return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; - +- (nonnull BVStringKeyValuePair *)sortParams:(nonnull NSArray *)sorts + withKey:(NSString *)paramKey { + NSMutableArray *strings = [NSMutableArray array]; + + for (BVSort *sort in sorts) { + [strings addObject:[sort toString]]; + } + + NSString *combined = [strings componentsJoinedByString:@","]; + + return [BVStringKeyValuePair pairWithKey:paramKey value:combined]; } --(NSString* _Nonnull)includesToParams:(NSArray* _Nonnull)includes { - - - NSArray* sortedArray = [includes sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - +- (nonnull NSString *)includesToParams:(nonnull NSArray *)includes { + NSArray *sortedArray = [includes + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; } @end diff --git a/Pod/BVConversations/Display/Requests/BVConversationsRequest.h b/Pod/BVConversations/Display/Requests/BVConversationsRequest.h index 3dec57f7..9e8449aa 100644 --- a/Pod/BVConversations/Display/Requests/BVConversationsRequest.h +++ b/Pod/BVConversations/Display/Requests/BVConversationsRequest.h @@ -7,36 +7,47 @@ #import -#import "BVStringKeyValuePair.h" -#import "BVReviewsResponse.h" -#import "BVProductsResponse.h" #import "BVAuthorResponse.h" +#import "BVProductsResponse.h" +#import "BVReviewsResponse.h" +#import "BVStringKeyValuePair.h" -typedef void (^ConversationsFailureHandler)(NSArray* _Nonnull errors); - +typedef void (^ConversationsFailureHandler)( + NSArray *__nonnull errors); /// Internal class - used only within BVSDK @interface BVConversationsRequest : NSObject --(NSMutableArray* _Nonnull)createParams; --(NSString* _Nonnull)endpoint; -+(NSString* _Nonnull)commonEndpoint; +- (nonnull NSMutableArray *)createParams; +- (nonnull NSString *)endpoint; ++ (nonnull NSString *)commonEndpoint; --(nonnull instancetype)addAdditionalField:(nonnull NSString*)fieldName value:(nonnull NSString*)value __deprecated_msg("use addCustomDisplayParameter instead."); +- (nonnull instancetype)addAdditionalField:(nonnull NSString *)fieldName + value:(nonnull NSString *)value + __deprecated_msg("use addCustomDisplayParameter instead."); /** This method adds extra user provided query parameters to a submission request, and will be urlencoded. */ --(nonnull instancetype)addCustomDisplayParameter:(nonnull NSString*)parameter withValue:(nonnull NSString*)value; +- (nonnull instancetype)addCustomDisplayParameter:(nonnull NSString *)parameter + withValue:(nonnull NSString *)value; + +- (void) +loadContent:(nonnull BVConversationsRequest *)request + completion:(nonnull void (^)(NSDictionary *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure; + +- (void)sendError:(nonnull NSError *)error + failureCallback:(nonnull ConversationsFailureHandler)failure; -- (void)loadContent:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(NSDictionary* _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray* _Nonnull errors))failure; --(void)sendError:(nonnull NSError*)error failureCallback:(nonnull ConversationsFailureHandler)failure; --(void)sendErrors:(nonnull NSArray*)errors failureCallback:(nonnull ConversationsFailureHandler)failure; +- (void)sendErrors:(nonnull NSArray *)errors + failureCallback:(nonnull ConversationsFailureHandler)failure; -- (NSError * _Nonnull)limitError:(NSInteger)limit; -- (NSError * _Nonnull)tooManyProductsError:(NSArray * _Nonnull)productIds; +- (nonnull NSError *)limitError:(NSInteger)limit; +- (nonnull NSError *)tooManyProductsError: + (nonnull NSArray *)productIds; -- (NSString * _Nonnull)getPassKey; +- (nonnull NSString *)getPassKey; @end diff --git a/Pod/BVConversations/Display/Requests/BVConversationsRequest.m b/Pod/BVConversations/Display/Requests/BVConversationsRequest.m index b50a2620..1a917a2b 100644 --- a/Pod/BVConversations/Display/Requests/BVConversationsRequest.m +++ b/Pod/BVConversations/Display/Requests/BVConversationsRequest.m @@ -6,181 +6,232 @@ // #import "BVConversationsRequest.h" -#import "BVSDKManager.h" #import "BVAnalyticsManager.h" -#import "BVDiagnosticHelpers.h" #import "BVConversationsErrorResponse.h" -#import "BVStoreReviewsResponse.h" +#import "BVDiagnosticHelpers.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVStoreReviewsResponse.h" -@interface BVConversationsRequest() -@property (strong, nonatomic) NSMutableArray* customQueryParameters; +@interface BVConversationsRequest () +@property(strong, nonatomic) + NSMutableArray *customQueryParameters; @end @implementation BVConversationsRequest - --(NSMutableArray* _Nonnull)createParams { - - NSMutableArray* params = [NSMutableArray array]; - - [params addObject:[BVStringKeyValuePair pairWithKey:@"apiversion" value:@"5.4"]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"passkey" value:[self getPassKey]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"_appId" value:[NSBundle mainBundle].bundleIdentifier]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"_appVersion" value:[BVDiagnosticHelpers releaseVersionNumber]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"_buildNumber" value:[BVDiagnosticHelpers buildVersionNumber]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"_bvIosSdkVersion" value:BV_SDK_VERSION]]; - - if (_customQueryParameters){ - [params addObjectsFromArray:_customQueryParameters]; - } - - return params; - +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [NSMutableArray array]; + + [params + addObject:[BVStringKeyValuePair pairWithKey:@"apiversion" value:@"5.4"]]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"passkey" + value:[self getPassKey]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"_appId" + value:[NSBundle mainBundle].bundleIdentifier]]; + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"_appVersion" + value:[BVDiagnosticHelpers releaseVersionNumber]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"_buildNumber" + value:[BVDiagnosticHelpers buildVersionNumber]]]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"_bvIosSdkVersion" + value:BV_SDK_VERSION]]; + + if (_customQueryParameters) { + [params addObjectsFromArray:_customQueryParameters]; + } + + return params; } --(nonnull instancetype)addAdditionalField:(nonnull NSString*)fieldName value:(nonnull NSString*)value{ - return [self addCustomDisplayParameter:fieldName withValue:value]; +- (nonnull instancetype)addAdditionalField:(nonnull NSString *)fieldName + value:(nonnull NSString *)value { + return [self addCustomDisplayParameter:fieldName withValue:value]; } --(nonnull instancetype)addCustomDisplayParameter:(NSString *)parameter withValue:(NSString *)value { - if (parameter && value){ - if (!self.customQueryParameters){ - self.customQueryParameters = [NSMutableArray array]; - } - - [_customQueryParameters addObject:[BVStringKeyValuePair pairWithKey:parameter value:value]]; - } else { - NSAssert(NO, @"illegal use of non-null parameters"); +- (nonnull instancetype)addCustomDisplayParameter:(NSString *)parameter + withValue:(NSString *)value { + if (parameter && value) { + if (!self.customQueryParameters) { + self.customQueryParameters = [NSMutableArray array]; } - - return self; -} + [_customQueryParameters + addObject:[BVStringKeyValuePair pairWithKey:parameter value:value]]; + } else { + NSAssert(NO, @"illegal use of non-null parameters"); + } --(NSString* _Nonnull)endpoint { - NSAssert(NO, @"endpoint must be overridden by subclass"); - return nil; + return self; } -+(NSString* _Nonnull)commonEndpoint { - return [BVSDKManager sharedManager].configuration.staging ? @"https://stg.api.bazaarvoice.com/data/" : @"https://api.bazaarvoice.com/data/"; +- (nonnull NSString *)endpoint { + NSAssert(NO, @"endpoint must be overridden by subclass"); + return nil; } --(NSArray* _Nonnull)getQueryItems:(NSArray*)params { - - NSMutableArray* queryItems = [NSMutableArray array]; - - for(BVStringKeyValuePair* param in params) { - if(param.value != nil){ - NSURLQueryItem* queryItem = [[NSURLQueryItem alloc] initWithName:param.key value:param.value]; - [queryItems addObject:queryItem]; - } - } - - return queryItems; - ++ (nonnull NSString *)commonEndpoint { + return [BVSDKManager sharedManager].configuration.staging + ? @"https://stg.api.bazaarvoice.com/data/" + : @"https://api.bazaarvoice.com/data/"; } -- (void)loadContent:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(NSDictionary* _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray* _Nonnull errors))failure { - - NSString* url = [NSString stringWithFormat:@"%@%@", [BVConversationsRequest commonEndpoint], [request endpoint]]; - NSURLComponents* urlComponents = [NSURLComponents componentsWithString:url]; - NSArray* parameters = [request createParams]; - urlComponents.queryItems = [self getQueryItems:parameters]; - NSURLRequest* urlRequest = [NSURLRequest requestWithURL:urlComponents.URL]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", urlRequest.URL]]; - - NSURLSession* session = [NSURLSession sharedSession]; - NSURLSessionDataTask* task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - [self processData:data response:response error:error completion:completion failure:failure]; - - }]; - - // start the request - [task resume]; +- (nonnull NSArray *)getQueryItems: + (NSArray *)params { + NSMutableArray *queryItems = [NSMutableArray array]; + + for (BVStringKeyValuePair *param in params) { + if (param.value != nil) { + NSURLQueryItem *queryItem = + [[NSURLQueryItem alloc] initWithName:param.key value:param.value]; + [queryItems addObject:queryItem]; + } + } + return queryItems; } --(void)processData:(NSData* _Nullable)data response:(NSURLResponse* _Nullable)response error:(NSError* _Nullable)error completion:(void (^ _Nonnull)(NSDictionary* _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray* _Nonnull errors))failure { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; - NSInteger statusCode = [httpResponse statusCode]; - - if(statusCode == 200) { - @try { - NSError* err; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err]; - if (json != nil){ - - BVConversationsErrorResponse* errorResponse = [[BVConversationsErrorResponse alloc] initWithApiResponse:json]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - - if (errorResponse != nil) { - [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; - } - else { - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(json); - }); - } - } - else if (err != nil) { - [self sendError:err failureCallback:failure]; - } - else { - NSError* parsingError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_PARSING_FAILED userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; - [self sendError:parsingError failureCallback:failure]; - } - - } - @catch (NSException *exception) { - NSError* err = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; - [self sendError:err failureCallback:failure]; +- (void) +loadContent:(nonnull BVConversationsRequest *)request + completion:(nonnull void (^)(NSDictionary *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + NSString *url = [NSString + stringWithFormat:@"%@%@", [BVConversationsRequest commonEndpoint], + [request endpoint]]; + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:url]; + NSArray *parameters = [request createParams]; + urlComponents.queryItems = [self getQueryItems:parameters]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:urlComponents.URL]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"GET: %@", urlRequest.URL]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = + [session dataTaskWithRequest:urlRequest + completionHandler:^(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable error) { + + [self processData:data + response:response + error:error + completion:completion + failure:failure]; + + }]; + + // start the request + [task resume]; +} + +- (void) +processData:(nullable NSData *)data + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + completion:(nonnull void (^)(NSDictionary *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + NSInteger statusCode = [httpResponse statusCode]; + + if (statusCode == 200) { + @try { + NSError *err; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&err]; + if (json != nil) { + BVConversationsErrorResponse *errorResponse = + [[BVConversationsErrorResponse alloc] initWithApiResponse:json]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + + if (errorResponse != nil) { + [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; + } else { + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(json); + }); } + } else if (err != nil) { + [self sendError:err failureCallback:failure]; + } else { + NSError *parsingError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_PARSING_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + [self sendError:parsingError failureCallback:failure]; + } + + } @catch (NSException *exception) { + NSError *err = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + [self sendError:err failureCallback:failure]; } - else { - NSString* message = [NSString stringWithFormat:@"HTTP response status code: %li with error: %@", (long)statusCode, error.localizedDescription]; - NSError* enhancedError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey: message}]; - [self sendError:enhancedError failureCallback:failure]; - } + } else { + NSString *message = [NSString + stringWithFormat:@"HTTP response status code: %li with error: %@", + (long)statusCode, error.localizedDescription]; + NSError *enhancedError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{NSLocalizedDescriptionKey : message}]; + [self sendError:enhancedError failureCallback:failure]; + } } - -- (NSError * _Nonnull)limitError:(NSInteger)limit { - - NSString* message = [NSString stringWithFormat:@"Invalid `limit` value: Parameter 'limit' has invalid value: %li - must be between 1 and 100.", (long)limit]; - return [NSError errorWithDomain:BVErrDomain code:BV_ERROR_INVALID_LIMIT userInfo:@{NSLocalizedDescriptionKey: message}]; - +- (nonnull NSError *)limitError:(NSInteger)limit { + NSString *message = [NSString + stringWithFormat:@"Invalid `limit` value: Parameter 'limit' has " + @"invalid value: %li - must be between 1 and 100.", + (long)limit]; + return [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_INVALID_LIMIT + userInfo:@{NSLocalizedDescriptionKey : message}]; } -- (NSError * _Nonnull)tooManyProductsError:(NSArray * _Nonnull)productIds { - - NSString* message = [NSString stringWithFormat:@"Too many productIds requested: %lu. Must be between 1 and 100.", (unsigned long)[productIds count]]; - return [NSError errorWithDomain:BVErrDomain code:BV_ERROR_TOO_MANY_PRODUCTS userInfo:@{NSLocalizedDescriptionKey: message}]; - +- (nonnull NSError *)tooManyProductsError: + (nonnull NSArray *)productIds { + NSString *message = [NSString + stringWithFormat: + @"Too many productIds requested: %lu. Must be between 1 and 100.", + (unsigned long)[productIds count]]; + return [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_TOO_MANY_PRODUCTS + userInfo:@{NSLocalizedDescriptionKey : message}]; } --(void)sendError:(nonnull NSError*)error failureCallback:(ConversationsFailureHandler) failure { - [self sendErrors:@[error] failureCallback:failure]; +- (void)sendError:(nonnull NSError *)error + failureCallback:(ConversationsFailureHandler)failure { + [self sendErrors:@[ error ] failureCallback:failure]; } --(void)sendErrors:(nonnull NSArray*)errors failureCallback:(ConversationsFailureHandler) failure { - for (NSError* error in errors) { - [[BVLogger sharedLogger] printError:error]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - failure(errors); - }); +- (void)sendErrors:(nonnull NSArray *)errors + failureCallback:(ConversationsFailureHandler)failure { + for (NSError *error in errors) { + [[BVLogger sharedLogger] printError:error]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + failure(errors); + }); } -- (NSString * _Nonnull)getPassKey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversations; +- (nonnull NSString *)getPassKey { + return [BVSDKManager sharedManager].configuration.apiKeyConversations; } - @end diff --git a/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.h b/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.h index a1d892c1..17723163 100644 --- a/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.h +++ b/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.h @@ -5,25 +5,30 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "PDPContentType.h" #import "BVBaseProductRequest.h" +#import "PDPContentType.h" +#import -typedef void (^ProductRequestCompletionHandler)(BVProductsResponse* _Nonnull response); +typedef void (^ProductRequestCompletionHandler)( + BVProductsResponse *__nonnull response); /* - You can retrieve all information needed for a Product Display Page with this request. - Optionally, you can include `Reviews` or `QuestionsAndAnswers` as well as statistics on + You can retrieve all information needed for a Product Display Page with this + request. + Optionally, you can include `Reviews` or `QuestionsAndAnswers` as well as + statistics on reviews and questions and answers. - Optionally, you can filter and sort the questions using the `addSort*` and `addFilter*` methods. + Optionally, you can filter and sort the questions using the `addSort*` and + `addFilter*` methods. */ @interface BVProductDisplayPageRequest : BVBaseSortableProductRequest +/// Initialize the request for the product ID you are displaying on your product +/// page. +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId; +- (nonnull instancetype)__unavailable init; -/// Initialize the request for the product ID you are displaying on your product page. -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId; -- (nonnull instancetype) __unavailable init; - -- (void)load:(ProductRequestCompletionHandler _Nonnull)success failure:(ConversationsFailureHandler _Nonnull)failure; +- (void)load:(nonnull ProductRequestCompletionHandler)success + failure:(nonnull ConversationsFailureHandler)failure; @end diff --git a/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.m b/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.m index 08389e3f..8d3ca383 100644 --- a/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.m +++ b/Pod/BVConversations/Display/Requests/BVProductDisplayPageRequest.m @@ -6,104 +6,112 @@ // #import "BVProductDisplayPageRequest.h" +#import "BVAnalyticsManager.h" #import "BVCommaUtil.h" -#import "BVProductFilterType.h" #import "BVFilterOperator.h" -#import "BVAnalyticsManager.h" #import "BVPixel.h" +#import "BVProductFilterType.h" -@interface BVProductDisplayPageRequest() +@interface BVProductDisplayPageRequest () -@property NSString* _Nonnull productId; +@property(nonnull) NSString *productId; @end @implementation BVProductDisplayPageRequest -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId { - self = [super init]; - if(self){ - self.productId = [BVCommaUtil escape:productId]; - } - return self; +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId { + self = [super init]; + if (self) { + self.productId = [BVCommaUtil escape:productId]; + } + return self; } -- (void)load:(ProductRequestCompletionHandler _Nonnull)success failure:(ConversationsFailureHandler _Nonnull)failure { - [self loadProducts:self completion:success failure:failure]; +- (void)load:(nonnull ProductRequestCompletionHandler)success + failure:(nonnull ConversationsFailureHandler)failure { + [self loadProducts:self completion:success failure:failure]; } -- (void)loadProducts:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVProductsResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - BVProductsResponse* productsResponse = [[BVProductsResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(productsResponse); - }); - [self sendProductsAnalytics:productsResponse]; - - } failure:failure]; - +- (void) +loadProducts:(nonnull BVConversationsRequest *)request + completion: + (nonnull void (^)(BVProductsResponse *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + BVProductsResponse *productsResponse = + [[BVProductsResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(productsResponse); + }); + [self sendProductsAnalytics:productsResponse]; + + } + failure:failure]; } -- (void)sendProductsAnalytics:(BVProductsResponse*)productsResponse { - - BVProduct* product = productsResponse.result; - if (product) { - - // send impressions for included content, reviews and questions - - - for(BVReview* review in product.includedReviews) { - NSString *brandName = review.product.brand ? review.product.brand.name : nil; - BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] initWithProductId:review.productId - withContentId:review.identifier - withCategoryId:review.product.categoryId - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeReview - withBrand:brandName withAdditionalParams:nil]; - - [BVPixel trackEvent:reviewImpression]; - - } - - for(BVQuestion* question in product.includedQuestions) { - // Record Question Impression - BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] initWithProductId:question.productId - withContentId:question.identifier - withCategoryId:question.categoryId - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContentType:BVPixelImpressionContentTypeQuestion - withBrand:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:questionImpression]; - } - - - // send pageview for product - NSString *brandName = product.brand != nil ? product.brand.name : nil; - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:product.identifier - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:brandName - withCategoryId:product.categoryId - withRootCategoryId:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:pageView]; +- (void)sendProductsAnalytics:(BVProductsResponse *)productsResponse { + BVProduct *product = productsResponse.result; + if (product) { + // send impressions for included content, reviews and questions + + for (BVReview *review in product.includedReviews) { + NSString *brandName = + review.product.brand ? review.product.brand.name : nil; + BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] + initWithProductId:review.productId + withContentId:review.identifier + withCategoryId:review.product.categoryId + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeReview + withBrand:brandName + withAdditionalParams:nil]; + + [BVPixel trackEvent:reviewImpression]; } - + + for (BVQuestion *question in product.includedQuestions) { + // Record Question Impression + BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] + initWithProductId:question.productId + withContentId:question.identifier + withCategoryId:question.categoryId + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContentType:BVPixelImpressionContentTypeQuestion + withBrand:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:questionImpression]; + } + + // send pageview for product + NSString *brandName = product.brand != nil ? product.brand.name : nil; + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:product.identifier + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:brandName + withCategoryId:product.categoryId + withRootCategoryId:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:pageView]; + } } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - - BVFilter* filter = [[BVFilter alloc] initWithString:[BVProductFilterTypeUtil toString:BVProductFilterTypeId] filterOperator:BVFilterOperatorEqualTo values:@[self.productId]]; - NSString* filterValue = [filter toParameterString]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:filterValue]]; - - return params; +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + BVFilter *filter = [[BVFilter alloc] + initWithString:[BVProductFilterTypeUtil toString:BVProductFilterTypeId] + filterOperator:BVFilterOperatorEqualTo + values:@[ self.productId ]]; + NSString *filterValue = [filter toParameterString]; + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:filterValue]]; + + return params; } @end diff --git a/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.h b/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.h index 26159083..4d7dee19 100644 --- a/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.h +++ b/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.h @@ -5,12 +5,12 @@ // Copyright 2017 Bazaarvoice Inc. All rights reserved. // -#import #import "BVBaseProductRequest.h" +#import @interface BVProductTextSearchRequest : BVBaseProductsRequest -- (nonnull instancetype)initWithSearchText:(NSString * _Nullable)searchText; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)initWithSearchText:(nullable NSString *)searchText; +- (nonnull instancetype)__unavailable init; @end diff --git a/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.m b/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.m index 577f8d22..b520b734 100644 --- a/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.m +++ b/Pod/BVConversations/Display/Requests/BVProductTextSearchRequest.m @@ -8,30 +8,30 @@ #import "BVProductTextSearchRequest.h" #import "BVBulkProductResponse.h" -@interface BVProductTextSearchRequest() +@interface BVProductTextSearchRequest () -@property NSString* searchText; +@property NSString *searchText; @end @implementation BVProductTextSearchRequest --(instancetype)initWithSearchText:(NSString *)searchText { - if (self = [super init]) { - _searchText = searchText; - } - - return self; +- (instancetype)initWithSearchText:(NSString *)searchText { + if (self = [super init]) { + _searchText = searchText; + } + + return self; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - if (_searchText) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"search" value:_searchText]]; - } - - return params; +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + if (_searchText) { + [params addObject:[BVStringKeyValuePair pairWithKey:@"search" + value:_searchText]]; + } + + return params; } @end diff --git a/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.h b/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.h index a300f78c..63ac794c 100644 --- a/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.h +++ b/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.h @@ -5,38 +5,52 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVConversationsRequest.h" -#import "BVQuestionFilterType.h" #import "BVFilterOperator.h" +#import "BVQuestionFilterType.h" +#import "BVQuestionsAndAnswersResponse.h" #import "BVSort.h" -#import "BVSortOptionQuestions.h" #import "BVSortOptionAnswers.h" -#import "BVQuestionsAndAnswersResponse.h" +#import "BVSortOptionQuestions.h" +#import -typedef void (^QuestionsAndAnswersSuccessHandler)(BVQuestionsAndAnswersResponse* _Nonnull response); +typedef void (^QuestionsAndAnswersSuccessHandler)( + BVQuestionsAndAnswersResponse *__nonnull response); /* - You can get multiple questions and their associated answers with this request object. - Optionally, you can filter, sort, or search reviews using the `addSort*` and `addFilter*` and `search` methods. + You can get multiple questions and their associated answers with this request + object. + Optionally, you can filter, sort, or search reviews using the `addSort*` and + `addFilter*` and `search` methods. */ @interface BVQuestionsAndAnswersRequest : BVConversationsRequest -@property (readonly) NSString* _Nonnull productId; - -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId limit:(int)limit offset:(int)offset; -- (nonnull instancetype) __unavailable init; - -- (nonnull instancetype)addSort:(BVSortOptionProducts)option order:(BVSortOrder)order __deprecated_msg("use sortQuestions and sortAnswers instead"); - -- (nonnull instancetype)addQuestionSort:(BVSortOptionQuestions)option order:(BVSortOrder)order; - -- (nonnull instancetype)addFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value; -- (nonnull instancetype)addFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values; -- (nonnull instancetype)search:(NSString * _Nonnull)search; - -- (void)load:(void (^ _Nonnull)(BVQuestionsAndAnswersResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; -- (NSString * _Nonnull)endpoint; -- (NSMutableArray * _Nonnull)createParams; +@property(nonnull, readonly) NSString *productId; + +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId + limit:(int)limit + offset:(int)offset; +- (nonnull instancetype)__unavailable init; + +- (nonnull instancetype)addSort:(BVSortOptionProducts)option + order:(BVSortOrder)order + __deprecated_msg("use sortQuestions and sortAnswers instead"); + +- (nonnull instancetype)addQuestionSort:(BVSortOptionQuestions)option + order:(BVSortOrder)order; + +- (nonnull instancetype)addFilter:(BVQuestionFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value; +- (nonnull instancetype)addFilter:(BVQuestionFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; +- (nonnull instancetype)search:(nonnull NSString *)search; + +- (void)load:(nonnull void (^)( + BVQuestionsAndAnswersResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; +- (nonnull NSString *)endpoint; +- (nonnull NSMutableArray *)createParams; @end diff --git a/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.m b/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.m index 44220a0c..1a5d4b98 100644 --- a/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.m +++ b/Pod/BVConversations/Display/Requests/BVQuestionsAndAnswersRequest.m @@ -6,154 +6,183 @@ // #import "BVQuestionsAndAnswersRequest.h" -#import "BVFilter.h" #import "BVCommaUtil.h" #import "BVCore.h" +#import "BVFilter.h" -@interface BVQuestionsAndAnswersRequest() +@interface BVQuestionsAndAnswersRequest () @property int limit; @property int offset; -@property NSString* _Nullable search; -@property NSMutableArray* _Nonnull filters; -@property NSMutableArray* _Nonnull sorts; +@property(nullable) NSString *search; +@property(nonnull) NSMutableArray *filters; +@property(nonnull) NSMutableArray *sorts; @end @implementation BVQuestionsAndAnswersRequest -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId limit:(int)limit offset:(int)offset { - self = [super init]; - if(self){ - _productId = [BVCommaUtil escape:productId]; - self.limit = (int)limit; - self.offset = (int)offset; - - self.filters = [NSMutableArray array]; - self.sorts = [NSMutableArray array]; - - // filter the request to the given productId - BVFilter* filter = [[BVFilter alloc] initWithString:@"ProductId" filterOperator:BVFilterOperatorEqualTo values:@[self.productId]]; - [self.filters addObject:filter]; - } - return self; +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId + limit:(int)limit + offset:(int)offset { + self = [super init]; + if (self) { + _productId = [BVCommaUtil escape:productId]; + self.limit = (int)limit; + self.offset = (int)offset; + + self.filters = [NSMutableArray array]; + self.sorts = [NSMutableArray array]; + + // filter the request to the given productId + BVFilter *filter = [[BVFilter alloc] initWithString:@"ProductId" + filterOperator:BVFilterOperatorEqualTo + values:@[ self.productId ]]; + [self.filters addObject:filter]; + } + return self; } -- (nonnull instancetype)addSort:(BVSortOptionProducts)option order:(BVSortOrder)order { - LOG_DEPRECATED_MESSAGE(@"addSort") - BVSort* sort = [[BVSort alloc] initWithOption:option order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addSort:(BVSortOptionProducts)option + order:(BVSortOrder)order { + LOG_DEPRECATED_MESSAGE(@"addSort") + BVSort *sort = [[BVSort alloc] initWithOption:option order:order]; + [self.sorts addObject:sort]; + return self; } -- (nonnull instancetype)addQuestionSort:(BVSortOptionQuestions)option order:(BVSortOrder)order{ - BVSort* sort = [[BVSort alloc] initWithOptionString:[BVSortOptionQuestionsUtil toString:option] order:order]; - [self.sorts addObject:sort]; - return self; +- (nonnull instancetype)addQuestionSort:(BVSortOptionQuestions)option + order:(BVSortOrder)order { + BVSort *sort = [[BVSort alloc] + initWithOptionString:[BVSortOptionQuestionsUtil toString:option] + order:order]; + [self.sorts addObject:sort]; + return self; } -- (nonnull instancetype)addFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString * _Nonnull)value { - [self addFilter:type filterOperator:filterOperator values:@[value]]; - return self; +- (nonnull instancetype)addFilter:(BVQuestionFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + [self addFilter:type filterOperator:filterOperator values:@[ value ]]; + return self; } -- (nonnull instancetype)addFilter:(BVQuestionFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray * _Nonnull)values { - BVFilter* filter = [[BVFilter alloc] initWithString:[BVQuestionFilterTypeUtil toString:type] filterOperator:filterOperator values:values]; - [self.filters addObject:filter]; - return self; +- (nonnull instancetype)addFilter:(BVQuestionFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + BVFilter *filter = + [[BVFilter alloc] initWithString:[BVQuestionFilterTypeUtil toString:type] + filterOperator:filterOperator + values:values]; + [self.filters addObject:filter]; + return self; } -- (nonnull instancetype)search:(NSString * _Nonnull)search { - self.search = search; - return self; +- (nonnull instancetype)search:(nonnull NSString *)search { + self.search = search; + return self; } -- (void)load:(void (^ _Nonnull)(BVQuestionsAndAnswersResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure { - // validate request - if (self.limit < 1 || self.limit > 20) { - [self sendError:[super limitError:self.limit] failureCallback:failure]; - } - else { - [self loadQuestions:self completion:success failure:failure]; - } +- (void)load:(nonnull void (^)( + BVQuestionsAndAnswersResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + // validate request + if (self.limit < 1 || self.limit > 20) { + [self sendError:[super limitError:self.limit] failureCallback:failure]; + } else { + [self loadQuestions:self completion:success failure:failure]; + } } -- (void)loadQuestions:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVQuestionsAndAnswersResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - BVQuestionsAndAnswersResponse* questionsAndAnswersResponse = [[BVQuestionsAndAnswersResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(questionsAndAnswersResponse); - }); - [self sendQuestionsAnalytics:questionsAndAnswersResponse]; - } failure:failure]; - +- (void) +loadQuestions:(nonnull BVConversationsRequest *)request + completion:(nonnull void (^)( + BVQuestionsAndAnswersResponse *__nonnull response))completion + failure:(nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + BVQuestionsAndAnswersResponse *questionsAndAnswersResponse = + [[BVQuestionsAndAnswersResponse alloc] + initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(questionsAndAnswersResponse); + }); + [self sendQuestionsAnalytics:questionsAndAnswersResponse]; + } + failure:failure]; } -- (void)sendQuestionsAnalytics:(BVQuestionsAndAnswersResponse*)questionsResponse { - - for (BVQuestion* question in questionsResponse.results) { - - // Record Question Impression - BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] initWithProductId:question.productId - withContentId:question.identifier - withCategoryId:question.categoryId - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContentType:BVPixelImpressionContentTypeQuestion - withBrand:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:questionImpression]; - - } - - // send pageview for product - BVQuestion* question = questionsResponse.results.firstObject; - if (question != nil) { - - NSNumber* count = @([questionsResponse.results count]); - NSDictionary *addParams = @{@"numQuestions":count}; - - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:question.productId - withBVPixelProductType:BVPixelProductTypeConversationsQuestionAnswer - withBrand:nil - withCategoryId:question.categoryId - withRootCategoryId:nil - withAdditionalParams:addParams]; - - [BVPixel trackEvent:pageView]; - } - +- (void)sendQuestionsAnalytics: + (BVQuestionsAndAnswersResponse *)questionsResponse { + for (BVQuestion *question in questionsResponse.results) { + // Record Question Impression + BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] + initWithProductId:question.productId + withContentId:question.identifier + withCategoryId:question.categoryId + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContentType:BVPixelImpressionContentTypeQuestion + withBrand:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:questionImpression]; + } + + // send pageview for product + BVQuestion *question = questionsResponse.results.firstObject; + if (question != nil) { + NSNumber *count = @([questionsResponse.results count]); + NSDictionary *addParams = @{@"numQuestions" : count}; + + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:question.productId + withBVPixelProductType:BVPixelProductTypeConversationsQuestionAnswer + withBrand:nil + withCategoryId:question.categoryId + withRootCategoryId:nil + withAdditionalParams:addParams]; + + [BVPixel trackEvent:pageView]; + } } -- (NSString * _Nonnull)endpoint { - return @"questions.json"; +- (nonnull NSString *)endpoint { + return @"questions.json"; } -- (NSMutableArray * _Nonnull)createParams { - - NSMutableArray* params = [super createParams]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:@"Answers"]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Limit" value: [NSString stringWithFormat:@"%i", self.limit]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Offset" value: [NSString stringWithFormat:@"%i", self.offset]]]; - - for(BVFilter* filter in self.filters) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:[filter toParameterString]]]; +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Include" value:@"Answers"]]; + [params + addObject:[BVStringKeyValuePair pairWithKey:@"Search" value:self.search]]; + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Limit" + value:[NSString stringWithFormat:@"%i", self.limit]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Offset" + value:[NSString + stringWithFormat:@"%i", self.offset]]]; + + for (BVFilter *filter in self.filters) { + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Filter" + value:[filter toParameterString]]]; + } + + if ([self.sorts count] > 0) { + NSMutableArray *sortsAsStrings = [NSMutableArray array]; + for (BVSort *sort in self.sorts) { + [sortsAsStrings addObject:[sort toString]]; } - - if ([self.sorts count] > 0) { - NSMutableArray* sortsAsStrings = [NSMutableArray array]; - for (BVSort* sort in self.sorts) { - [sortsAsStrings addObject:[sort toString]]; - } - NSString* allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" value:allTogetherNow]]; - } - - return params; - + NSString *allTogetherNow = [sortsAsStrings componentsJoinedByString:@","]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"Sort" + value:allTogetherNow]]; + } + + return params; } @end diff --git a/Pod/BVConversations/Display/Requests/BVReviewsRequest.h b/Pod/BVConversations/Display/Requests/BVReviewsRequest.h index 77efbef7..fdd91de8 100644 --- a/Pod/BVConversations/Display/Requests/BVReviewsRequest.h +++ b/Pod/BVConversations/Display/Requests/BVReviewsRequest.h @@ -5,25 +5,29 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVReviewFilterType.h" +#import "BVBaseReviewsRequest.h" #import "BVFilterOperator.h" -#import "BVSortOptionReviews.h" -#import "BVSort.h" +#import "BVReviewFilterType.h" #import "BVReviewsResponse.h" -#import "BVBaseReviewsRequest.h" +#import "BVSort.h" +#import "BVSortOptionReviews.h" +#import -typedef void (^ReviewRequestCompletionHandler)(BVReviewsResponse* _Nonnull response); +typedef void (^ReviewRequestCompletionHandler)( + BVReviewsResponse *__nonnull response); /* You can get multiple reviews and with this request object. - Optionally, you can filter, sort, or search reviews using the `addSort*` and `addFilter*` and `search` methods. + Optionally, you can filter, sort, or search reviews using the `addSort*` and + `addFilter*` and `search` methods. */ -@interface BVReviewsRequest : BVBaseReviewsRequest +@interface BVReviewsRequest : BVBaseReviewsRequest -@property (readonly) NSString* _Nonnull productId; +@property(nonnull, readonly) NSString *productId; -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId limit:(int)limit offset:(int)offset; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId + limit:(int)limit + offset:(int)offset; +- (nonnull instancetype)__unavailable init; @end diff --git a/Pod/BVConversations/Display/Requests/BVReviewsRequest.m b/Pod/BVConversations/Display/Requests/BVReviewsRequest.m index 6b82526b..35da3ba1 100644 --- a/Pod/BVConversations/Display/Requests/BVReviewsRequest.m +++ b/Pod/BVConversations/Display/Requests/BVReviewsRequest.m @@ -5,24 +5,26 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import "BVCore.h" -#import "BVCommaUtil.h" #import "BVReviewsRequest.h" +#import "BVCommaUtil.h" +#import "BVCore.h" #import "BVFilter.h" #import "BVSort.h" @implementation BVReviewsRequest -- (nonnull instancetype)initWithProductId:(NSString * _Nonnull)productId limit:(int)limit offset:(int)offset { - return self = [super initWithID:productId limit:limit offset:offset]; +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId + limit:(int)limit + offset:(int)offset { + return self = [super initWithID:productId limit:limit offset:offset]; } --(NSString*)productId { - return self.ID; +- (NSString *)productId { + return self.ID; } --(id)createResponse:(NSDictionary *)raw { - return [[BVReviewsResponse alloc]initWithApiResponse:raw]; +- (id)createResponse:(NSDictionary *)raw { + return [[BVReviewsResponse alloc] initWithApiResponse:raw]; } @end diff --git a/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.h b/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.h index ce826638..0f21d6a5 100644 --- a/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.h +++ b/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.h @@ -6,36 +6,44 @@ // // -#import -#import "BVConversationsRequest.h" #import "BVBulkStoresResponse.h" -#import "BVSortOptionReviews.h" +#import "BVConversationsRequest.h" #import "BVSort.h" +#import "BVSortOptionReviews.h" #import "BVStoreIncludeContentType.h" +#import /** - Use the BVBulkStoreItemsRequest object for making requests to either fetch stores by limit and offset, or by specific store Id(s). + Use the BVBulkStoreItemsRequest object for making requests to either fetch + stores by limit and offset, or by specific store Id(s). Please use the designated initializers for each use case: - - • init:(int)limit offset:(int)offset - Use this initializer when you want to load multiple stores in batches. - • (nonnull instancetype)initWithStoreIds:(NSArray * _Nonnull)storeIds - Use this initializer when you know the store Id or Ids you want to fetch. + + • init:(int)limit offset:(int)offset - Use this initializer when you want to + load multiple stores in batches. + • (nonnull instancetype)initWithStoreIds:(NSArray * nonnull)storeIds - Use + this initializer when you know the store Id or Ids you want to fetch. */ @interface BVBulkStoreItemsRequest : BVConversationsRequest -/// Initialize fetching of stores when requesting many store objects at once and/or paging +/// Initialize fetching of stores when requesting many store objects at once +/// and/or paging - (nonnull instancetype)init:(int)limit offset:(int)offset; /// Initialize to return a specific set of store Ids -- (nonnull instancetype)initWithStoreIds:(NSArray * _Nonnull)storeIds; +- (nonnull instancetype)initWithStoreIds:(nonnull NSArray *)storeIds; -- (nonnull instancetype) __unavailable init; // Use the designated initializers only. +- (nonnull instancetype)__unavailable + init; // Use the designated initializers only. -/// When you apply PDPContentTypeReviews to the content type, you will get review statistics in the BVStore object(s) -- (nonnull instancetype)includeStatistics:(BVStoreIncludeContentType)contentType; +/// When you apply PDPContentTypeReviews to the content type, you will get +/// review statistics in the BVStore object(s) +- (nonnull instancetype)includeStatistics: + (BVStoreIncludeContentType)contentType; /// Make the asynchronous call from the request object. -- (void)load:(void (^ _Nonnull)(BVBulkStoresResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure; +- (void)load:(nonnull void (^)(BVBulkStoresResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure; -- (NSString * _Nonnull)endpoint; -- (NSMutableArray * _Nonnull)createParams; +- (nonnull NSString *)endpoint; +- (nonnull NSMutableArray *)createParams; @end diff --git a/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.m b/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.m index 0b94021a..5c1cf089 100644 --- a/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.m +++ b/Pod/BVConversations/Display/Requests/Stores/BVBulkStoreItemsRequest.m @@ -7,143 +7,149 @@ // #import "BVBulkStoreItemsRequest.h" -#import "BVSDKManager.h" #import "BVFilter.h" -#import "PDPInclude.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "PDPInclude.h" -@interface BVBulkStoreItemsRequest() - - @property int limit; - @property int offset; - @property NSMutableArray* _Nonnull storeContentTypeStatistics; - @property NSArray* _Nonnull filterStoreIds; +@interface BVBulkStoreItemsRequest () - @end +@property int limit; +@property int offset; +@property(nonnull) NSMutableArray *storeContentTypeStatistics; +@property(nonnull) NSArray *filterStoreIds; -@implementation BVBulkStoreItemsRequest +@end +@implementation BVBulkStoreItemsRequest -- (nonnull instancetype)init:(int)limit offset:(int)offset{ - - self = [super init]; - if(self){ - - self.limit = limit; - self.offset = offset; - [self initDefaultProps]; - - } - return self; - +- (nonnull instancetype)init:(int)limit offset:(int)offset { + self = [super init]; + if (self) { + self.limit = limit; + self.offset = offset; + [self initDefaultProps]; + } + return self; } -- (nonnull instancetype)initWithStoreIds:(NSArray * _Nonnull)storeIds{ - - self = [super init]; - if(self){ - - self.limit = (int)[storeIds count]; - self.offset = 0; - [self initDefaultProps]; - _filterStoreIds = storeIds; - - } - return self; +- (nonnull instancetype)initWithStoreIds:(nonnull NSArray *)storeIds { + self = [super init]; + if (self) { + self.limit = (int)[storeIds count]; + self.offset = 0; + [self initDefaultProps]; + _filterStoreIds = storeIds; + } + return self; } -- (void)initDefaultProps{ - _filterStoreIds = [NSArray array]; - self.storeContentTypeStatistics = [NSMutableArray array]; +- (void)initDefaultProps { + _filterStoreIds = [NSArray array]; + self.storeContentTypeStatistics = [NSMutableArray array]; } - -- (void)load:(void (^ _Nonnull)(BVBulkStoresResponse * _Nonnull response))success failure:(ConversationsFailureHandler _Nonnull)failure{ - [self loadStores:self completion:success failure:failure]; -} - -- (void)loadStores:(BVConversationsRequest * _Nonnull)request completion:(void (^ _Nonnull)(BVBulkStoresResponse * _Nonnull response))completion failure:(void (^ _Nonnull)(NSArray * _Nonnull errors))failure { - - [self loadContent:request completion:^(NSDictionary * _Nonnull response) { - BVBulkStoresResponse* storesResponse = [[BVBulkStoresResponse alloc] initWithApiResponse:response]; - // invoke success callback on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - completion(storesResponse); - }); - - if ([storesResponse.results count] == 1){ - [self sendStoreAnalytics:storesResponse.results[0]]; - } - - } failure:failure]; - +- (void)load:(nonnull void (^)(BVBulkStoresResponse *__nonnull response))success + failure:(nonnull ConversationsFailureHandler)failure { + [self loadStores:self completion:success failure:failure]; } -- (void)sendStoreAnalytics:(BVStore*)store { - - if (store) { - // send pageview for product - - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:store.identifier - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:nil - withCategoryId:store.categoryId - withRootCategoryId:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:pageView]; - - } +- (void)loadStores:(nonnull BVConversationsRequest *)request + completion:(nonnull void (^)(BVBulkStoresResponse *__nonnull response)) + completion + failure: + (nonnull void (^)(NSArray *__nonnull errors))failure { + [self loadContent:request + completion:^(NSDictionary *__nonnull response) { + BVBulkStoresResponse *storesResponse = + [[BVBulkStoresResponse alloc] initWithApiResponse:response]; + // invoke success callback on main thread + dispatch_async(dispatch_get_main_queue(), ^{ + completion(storesResponse); + }); + + if ([storesResponse.results count] == 1) { + [self sendStoreAnalytics:storesResponse.results[0]]; + } + + } + failure:failure]; } -- (NSString * _Nonnull)endpoint{ - return @"products.json"; +- (void)sendStoreAnalytics:(BVStore *)store { + if (store) { + // send pageview for product + + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:store.identifier + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:nil + withCategoryId:store.categoryId + withRootCategoryId:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:pageView]; + } } - - -- (NSMutableArray * _Nonnull)createParams{ - - NSMutableArray* params = [super createParams]; - - [params addObject:[BVStringKeyValuePair pairWithKey:@"Limit" value: [NSString stringWithFormat:@"%i", self.limit]]]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Offset" value: [NSString stringWithFormat:@"%i", self.offset]]]; - - if (_storeContentTypeStatistics.count) { - [params addObject:[BVStringKeyValuePair pairWithKey:@"Stats" value:[self statisticsToParams:self.storeContentTypeStatistics]]]; - } - - if ([self.filterStoreIds count] > 0){ - BVFilter* filter = [[BVFilter alloc] initWithString:[BVProductFilterTypeUtil toString:BVProductFilterTypeId] filterOperator:BVFilterOperatorEqualTo values:self.filterStoreIds]; - NSString* filterValue = [filter toParameterString]; - [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" value:filterValue]]; - } - - return params; + +- (nonnull NSString *)endpoint { + return @"products.json"; } - - -- (nonnull instancetype)includeStatistics:(BVStoreIncludeContentType)contentType { - [self.storeContentTypeStatistics addObject:@(contentType)]; - return self; + +- (nonnull NSMutableArray *)createParams { + NSMutableArray *params = [super createParams]; + + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Limit" + value:[NSString stringWithFormat:@"%i", self.limit]]]; + [params addObject:[BVStringKeyValuePair + pairWithKey:@"Offset" + value:[NSString + stringWithFormat:@"%i", self.offset]]]; + + if (_storeContentTypeStatistics.count) { + [params + addObject:[BVStringKeyValuePair + pairWithKey:@"Stats" + value:[self statisticsToParams: + self.storeContentTypeStatistics]]]; + } + + if ([self.filterStoreIds count] > 0) { + BVFilter *filter = [[BVFilter alloc] + initWithString:[BVProductFilterTypeUtil toString:BVProductFilterTypeId] + filterOperator:BVFilterOperatorEqualTo + values:self.filterStoreIds]; + NSString *filterValue = [filter toParameterString]; + [params addObject:[BVStringKeyValuePair pairWithKey:@"Filter" + value:filterValue]]; + } + + return params; } - --(NSString* _Nonnull)statisticsToParams:(NSArray* _Nonnull)statistics { - - NSMutableArray* strings = [NSMutableArray array]; - - for(NSNumber* stat in statistics) { - [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - +- (nonnull instancetype)includeStatistics: + (BVStoreIncludeContentType)contentType { + [self.storeContentTypeStatistics addObject:@(contentType)]; + return self; } +- (nonnull NSString *)statisticsToParams: + (nonnull NSArray *)statistics { + NSMutableArray *strings = [NSMutableArray array]; + + for (NSNumber *stat in statistics) { + [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + + return [sortedArray componentsJoinedByString:@","]; +} -- (NSString *)getPassKey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; +- (NSString *)getPassKey { + return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; } @end diff --git a/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.h b/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.h index d6e0a13e..a9a9df35 100644 --- a/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.h +++ b/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.h @@ -5,30 +5,36 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVBaseReviewsRequest.h" -#import "BVReviewFilterType.h" #import "BVFilterOperator.h" -#import "BVSort.h" -#import "BVStoreReviewsResponse.h" +#import "BVReviewFilterType.h" #import "BVReviewsRequest.h" -#import "BVStoreIncludeContentType.h" +#import "BVSort.h" #import "BVSortOptionReviews.h" +#import "BVStoreIncludeContentType.h" +#import "BVStoreReviewsResponse.h" +#import -typedef void (^StoreReviewRequestCompletionHandler)(BVStoreReviewsResponse* _Nonnull response); +typedef void (^StoreReviewRequestCompletionHandler)( + BVStoreReviewsResponse *__nonnull response); /* You can get multiple reviews and with this request object. - Optionally, you can filter, sort, or search reviews using the `addSort*` and `addFilter*` and `search` methods. + Optionally, you can filter, sort, or search reviews using the `addSort*` and + `addFilter*` and `search` methods. */ -@interface BVStoreReviewsRequest : BVBaseReviewsRequest +@interface BVStoreReviewsRequest + : BVBaseReviewsRequest -@property (readonly) NSString* _Nonnull storeId; +@property(nonnull, readonly) NSString *storeId; -- (nonnull instancetype)initWithStoreId:(NSString * _Nonnull)storeId limit:(int)limit offset:(int)offset; +- (nonnull instancetype)initWithStoreId:(nonnull NSString *)storeId + limit:(int)limit + offset:(int)offset; -- (nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; -- (nonnull instancetype)includeStatistics:(BVStoreIncludeContentType)contentType; +- (nonnull instancetype)includeStatistics: + (BVStoreIncludeContentType)contentType; @end diff --git a/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.m b/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.m index 02248a06..57a69d75 100644 --- a/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.m +++ b/Pod/BVConversations/Display/Requests/Stores/BVStoreReviewsRequest.m @@ -6,57 +6,59 @@ // #import "BVStoreReviewsRequest.h" -#import "BVFilter.h" -#import "BVSort.h" #import "BVCommaUtil.h" +#import "BVFilter.h" #import "BVLogger.h" -#import "PDPInclude.h" -#import "BVSDKManager.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVSort.h" +#import "PDPInclude.h" -@interface BVStoreReviewsRequest() +@interface BVStoreReviewsRequest () -@property NSMutableArray* _Nonnull storeContentTypeStatistics; -@property NSMutableArray* _Nonnull reviewSorts; +@property(nonnull) NSMutableArray *storeContentTypeStatistics; +@property(nonnull) NSMutableArray *reviewSorts; @end @implementation BVStoreReviewsRequest -- (nonnull instancetype)initWithStoreId:(NSString * _Nonnull)storeId limit:(int)limit offset:(int)offset { - return self = [super initWithID:storeId limit:limit offset:offset]; +- (nonnull instancetype)initWithStoreId:(nonnull NSString *)storeId + limit:(int)limit + offset:(int)offset { + return self = [super initWithID:storeId limit:limit offset:offset]; } - -- (nonnull instancetype)includeStatistics:(BVStoreIncludeContentType)contentType { - [self.storeContentTypeStatistics addObject:@(contentType)]; - return self; + +- (nonnull instancetype)includeStatistics: + (BVStoreIncludeContentType)contentType { + [self.storeContentTypeStatistics addObject:@(contentType)]; + return self; } +- (nonnull NSString *)statisticsToParams: + (nonnull NSArray *)statistics { + NSMutableArray *strings = [NSMutableArray array]; + + for (NSNumber *stat in statistics) { + [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; + } + + NSArray *sortedArray = [strings + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; --(NSString* _Nonnull)statisticsToParams:(NSArray* _Nonnull)statistics { - - NSMutableArray* strings = [NSMutableArray array]; - - for(NSNumber* stat in statistics) { - [strings addObject:[PDPContentTypeUtil toString:[stat intValue]]]; - } - - NSArray* sortedArray = [strings sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - - return [sortedArray componentsJoinedByString:@","]; - + return [sortedArray componentsJoinedByString:@","]; } -- (NSString * _Nonnull)getPassKey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; +- (nonnull NSString *)getPassKey { + return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; } --(NSString*)storeId { - return self.ID; +- (NSString *)storeId { + return self.ID; } --(id)createResponse:(NSDictionary *)raw { - return [[BVStoreReviewsResponse alloc]initWithApiResponse:raw]; +- (id)createResponse:(NSDictionary *)raw { + return [[BVStoreReviewsResponse alloc] initWithApiResponse:raw]; } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.h b/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.h index af08ef78..44f5c83e 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.h @@ -11,11 +11,11 @@ Filter a `BVBulkRatingsRequest` based on content locale. */ typedef NS_ENUM(NSInteger, BVBulkRatingsFilterType) { - BVBulkRatingsFilterTypeContentLocale + BVBulkRatingsFilterTypeContentLocale }; @interface BVBulkRatingsFilterTypeUtil : NSObject -+(NSString* _Nonnull)toString:(BVBulkRatingsFilterType)filterOperator; ++ (nonnull NSString *)toString:(BVBulkRatingsFilterType)filterOperator; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.m b/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.m index ebb4e399..a6f88f7c 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVBulkRatingsFilterType.m @@ -9,14 +9,11 @@ @implementation BVBulkRatingsFilterTypeUtil -+(NSString* _Nonnull)toString:(BVBulkRatingsFilterType)filterOperator { - - switch (filterOperator) { - - case BVBulkRatingsFilterTypeContentLocale: return @"ContentLocale"; - - } - ++ (nonnull NSString *)toString:(BVBulkRatingsFilterType)filterOperator { + switch (filterOperator) { + case BVBulkRatingsFilterTypeContentLocale: + return @"ContentLocale"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.h b/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.h index bfd2e959..9e3051f7 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.h @@ -9,31 +9,31 @@ /** The allowable filter types for `BVCommentsRequest` requests. - API Refernce: https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-display#filter-options + API Refernce: + https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-display#filter-options */ typedef NS_ENUM(NSInteger, BVCommentFilterType) { - BVCommentFilterTypeId, - BVCommentFilterTypeAuthorId, - BVCommentFilterTypeCampaignId, - BVCommentFilterTypeCategoryAncestorId, - BVCommentFilterTypeContentLocale, - BVCommentFilterTypeIsFeatured, - BVCommentFilterTypeLastModeratedTime, - BVCommentFilterTypeLastModificationTime, - BVCommentFilterTypeModeratorCode, - BVCommentFilterTypeProductId, - BVCommentFilterTypeReviewId, - BVCommentFilterTypeSubmissionId, - BVCommentFilterTypeSubmissionTime, - BVCommentFilterTypeTotalFeedbackCount, - BVCommentFilterTypeTotalNegativeFeedbackCount, - BVCommentFilterTypeTotalPositiveFeedbackCount, - BVCommentFilterTypeUserLocation + BVCommentFilterTypeId, + BVCommentFilterTypeAuthorId, + BVCommentFilterTypeCampaignId, + BVCommentFilterTypeCategoryAncestorId, + BVCommentFilterTypeContentLocale, + BVCommentFilterTypeIsFeatured, + BVCommentFilterTypeLastModeratedTime, + BVCommentFilterTypeLastModificationTime, + BVCommentFilterTypeModeratorCode, + BVCommentFilterTypeProductId, + BVCommentFilterTypeReviewId, + BVCommentFilterTypeSubmissionId, + BVCommentFilterTypeSubmissionTime, + BVCommentFilterTypeTotalFeedbackCount, + BVCommentFilterTypeTotalNegativeFeedbackCount, + BVCommentFilterTypeTotalPositiveFeedbackCount, + BVCommentFilterTypeUserLocation }; - @interface BVCommentFilterTypeUtil : NSObject -+(NSString* _Nonnull)toString:(BVCommentFilterType)commentFilterOperator; ++ (nonnull NSString *)toString:(BVCommentFilterType)commentFilterOperator; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.m b/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.m index f85c4280..26230f2c 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVCommentFilterType.m @@ -9,28 +9,44 @@ @implementation BVCommentFilterTypeUtil -+(NSString* _Nonnull)toString:(BVCommentFilterType)commentFilterOperator{ - - switch (commentFilterOperator) { - case BVCommentFilterTypeId: return @"Id"; - case BVCommentFilterTypeAuthorId: return @"AuthorId"; - case BVCommentFilterTypeCampaignId: return @"CampaignId"; - case BVCommentFilterTypeCategoryAncestorId: return @"CategoryAncestorId"; - case BVCommentFilterTypeContentLocale: return @"ContentLocale"; - case BVCommentFilterTypeIsFeatured: return @"IsFeatured"; - case BVCommentFilterTypeLastModeratedTime: return @"LastModeratedTime"; - case BVCommentFilterTypeLastModificationTime: return @"LastModificationTime"; - case BVCommentFilterTypeModeratorCode: return @"ModeratorCode"; - case BVCommentFilterTypeProductId: return @"ProductId"; - case BVCommentFilterTypeReviewId: return @"ReviewId"; - case BVCommentFilterTypeSubmissionId: return @"SubmissionId"; - case BVCommentFilterTypeSubmissionTime: return @"SubmissionTime"; - case BVCommentFilterTypeTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVCommentFilterTypeTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVCommentFilterTypeTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVCommentFilterTypeUserLocation: return @"UserLocation"; - } ++ (nonnull NSString *)toString:(BVCommentFilterType)commentFilterOperator { + switch (commentFilterOperator) { + case BVCommentFilterTypeId: + return @"Id"; + case BVCommentFilterTypeAuthorId: + return @"AuthorId"; + case BVCommentFilterTypeCampaignId: + return @"CampaignId"; + case BVCommentFilterTypeCategoryAncestorId: + return @"CategoryAncestorId"; + case BVCommentFilterTypeContentLocale: + return @"ContentLocale"; + case BVCommentFilterTypeIsFeatured: + return @"IsFeatured"; + case BVCommentFilterTypeLastModeratedTime: + return @"LastModeratedTime"; + case BVCommentFilterTypeLastModificationTime: + return @"LastModificationTime"; + case BVCommentFilterTypeModeratorCode: + return @"ModeratorCode"; + case BVCommentFilterTypeProductId: + return @"ProductId"; + case BVCommentFilterTypeReviewId: + return @"ReviewId"; + case BVCommentFilterTypeSubmissionId: + return @"SubmissionId"; + case BVCommentFilterTypeSubmissionTime: + return @"SubmissionTime"; + case BVCommentFilterTypeTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVCommentFilterTypeTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVCommentFilterTypeTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVCommentFilterTypeUserLocation: + return @"UserLocation"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.h b/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.h index b5a59805..fdee3be2 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.h @@ -5,16 +5,22 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVProductFilterType.h" #import "BVFilterOperator.h" +#import "BVProductFilterType.h" +#import /// Internal class - used only within BVSDK @interface BVFilter : NSObject --(id _Nonnull)initWithType:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray* _Nonnull)values; --(id _Nonnull)initWithType:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString* _Nonnull)value; --(id _Nonnull)initWithString:(NSString* _Nonnull)str filterOperator:(BVFilterOperator)filterOperator values:(NSArray* _Nonnull)values; --(NSString* _Nonnull)toParameterString; +- (nonnull id)initWithType:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; +- (nonnull id)initWithType:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value; +- (nonnull id)initWithString:(nonnull NSString *)str + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values; +- (nonnull NSString *)toParameterString; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.m b/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.m index faec2139..5c184b96 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVFilter.m @@ -8,53 +8,60 @@ #import "BVFilter.h" #import "BVCommaUtil.h" -@interface BVFilter() +@interface BVFilter () -@property NSString* _Nonnull type; +@property(nonnull) NSString *type; @property BVFilterOperator filterOperator; -@property NSArray* _Nonnull values; +@property(nonnull) NSArray *values; @end @implementation BVFilter --(id _Nonnull)initWithType:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator values:(NSArray* _Nonnull)values { - self = [super init]; - if(self){ - self.type = [BVProductFilterTypeUtil toString:type]; - self.filterOperator = filterOperator; - self.values = [BVCommaUtil escapeMultiple:values]; - } - return self; +- (nonnull id)initWithType:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + self = [super init]; + if (self) { + self.type = [BVProductFilterTypeUtil toString:type]; + self.filterOperator = filterOperator; + self.values = [BVCommaUtil escapeMultiple:values]; + } + return self; } --(id _Nonnull)initWithType:(BVProductFilterType)type filterOperator:(BVFilterOperator)filterOperator value:(NSString* _Nonnull)value { - self = [super init]; - if(self){ - self.type = [BVProductFilterTypeUtil toString:type]; - self.filterOperator = filterOperator; - self.values = [BVCommaUtil escapeMultiple:@[value]]; - } - return self; +- (nonnull id)initWithType:(BVProductFilterType)type + filterOperator:(BVFilterOperator)filterOperator + value:(nonnull NSString *)value { + self = [super init]; + if (self) { + self.type = [BVProductFilterTypeUtil toString:type]; + self.filterOperator = filterOperator; + self.values = [BVCommaUtil escapeMultiple:@[ value ]]; + } + return self; } --(id _Nonnull)initWithString:(NSString* _Nonnull)str filterOperator:(BVFilterOperator)filterOperator values:(NSArray* _Nonnull)values { - self = [super init]; - if(self){ - self.type = str; - self.filterOperator = filterOperator; - self.values = [BVCommaUtil escapeMultiple:values]; - } - return self; +- (nonnull id)initWithString:(nonnull NSString *)str + filterOperator:(BVFilterOperator)filterOperator + values:(nonnull NSArray *)values { + self = [super init]; + if (self) { + self.type = str; + self.filterOperator = filterOperator; + self.values = [BVCommaUtil escapeMultiple:values]; + } + return self; } --(NSString*)toParameterString { - NSString* start = self.type; - NSString* middle = [BVFilterOperatorUtil toString:self.filterOperator]; - NSArray* sortedArray = [self.values sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; - NSString* end = [sortedArray componentsJoinedByString:@","]; - - return [NSString stringWithFormat:@"%@:%@:%@", start, middle, end]; +- (NSString *)toParameterString { + NSString *start = self.type; + NSString *middle = [BVFilterOperatorUtil toString:self.filterOperator]; + NSArray *sortedArray = [self.values + sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; + NSString *end = [sortedArray componentsJoinedByString:@","]; + + return [NSString stringWithFormat:@"%@:%@:%@", start, middle, end]; } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.h b/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.h index 5f74337b..54f2d616 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.h @@ -8,20 +8,21 @@ #import /* - BVFilterOperator describes the operator used on filters added to request objects. - For example: to search reviews that have ratings greater than 3, you would use the BVFilterOperatorGreaterThanOrEqualTo operator. + BVFilterOperator describes the operator used on filters added to request + objects. For example: to search reviews that have ratings greater than 3, you + would use the BVFilterOperatorGreaterThanOrEqualTo operator. */ typedef NS_ENUM(NSInteger, BVFilterOperator) { - BVFilterOperatorGreaterThan, - BVFilterOperatorGreaterThanOrEqualTo, - BVFilterOperatorLessThan, - BVFilterOperatorLessThanOrEqualTo, - BVFilterOperatorEqualTo, - BVFilterOperatorNotEqualTo, + BVFilterOperatorGreaterThan, + BVFilterOperatorGreaterThanOrEqualTo, + BVFilterOperatorLessThan, + BVFilterOperatorLessThanOrEqualTo, + BVFilterOperatorEqualTo, + BVFilterOperatorNotEqualTo, }; @interface BVFilterOperatorUtil : NSObject -+(NSString* _Nonnull)toString:(BVFilterOperator)filterOperator; ++ (nonnull NSString *)toString:(BVFilterOperator)filterOperator; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.m b/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.m index e3de24ec..8ce31b4e 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVFilterOperator.m @@ -9,23 +9,22 @@ @implementation BVFilterOperatorUtil -+(NSString* _Nonnull)toString:(BVFilterOperator)filterOperator { - - switch (filterOperator) { - case BVFilterOperatorGreaterThan: - return @"gt"; - case BVFilterOperatorGreaterThanOrEqualTo: - return @"gte"; - case BVFilterOperatorLessThan: - return @"lt"; - case BVFilterOperatorLessThanOrEqualTo: - return @"lte"; - case BVFilterOperatorEqualTo: - return @"eq"; - case BVFilterOperatorNotEqualTo: - return @"neq"; - } - ++ (nonnull NSString *)toString:(BVFilterOperator)filterOperator { + + switch (filterOperator) { + case BVFilterOperatorGreaterThan: + return @"gt"; + case BVFilterOperatorGreaterThanOrEqualTo: + return @"gte"; + case BVFilterOperatorLessThan: + return @"lt"; + case BVFilterOperatorLessThanOrEqualTo: + return @"lte"; + case BVFilterOperatorEqualTo: + return @"eq"; + case BVFilterOperatorNotEqualTo: + return @"neq"; + } } -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.h b/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.h index 9c168c92..2aaaf93a 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.h @@ -11,26 +11,26 @@ The allowable filter types for `BVProductDisplayPageRequest` requests. */ typedef NS_ENUM(NSInteger, BVProductFilterType) { - BVProductFilterTypeId, - BVProductFilterTypeAverageOverallRating, - BVProductFilterTypeCategoryAncestorId, - BVProductFilterTypeCategoryId, - BVProductFilterTypeIsActive, - BVProductFilterTypeIsDisabled, - BVProductFilterTypeLastAnswerTime, - BVProductFilterTypeLastQuestionTime, - BVProductFilterTypeLastReviewTime, - BVProductFilterTypeLastStoryTime, - BVProductFilterTypeName, - BVProductFilterTypeRatingsOnlyReviewCount, - BVProductFilterTypeTotalAnswerCount, - BVProductFilterTypeTotalQuestionCount, - BVProductFilterTypeTotalReviewCount, - BVProductFilterTypeTotalStoryCount + BVProductFilterTypeId, + BVProductFilterTypeAverageOverallRating, + BVProductFilterTypeCategoryAncestorId, + BVProductFilterTypeCategoryId, + BVProductFilterTypeIsActive, + BVProductFilterTypeIsDisabled, + BVProductFilterTypeLastAnswerTime, + BVProductFilterTypeLastQuestionTime, + BVProductFilterTypeLastReviewTime, + BVProductFilterTypeLastStoryTime, + BVProductFilterTypeName, + BVProductFilterTypeRatingsOnlyReviewCount, + BVProductFilterTypeTotalAnswerCount, + BVProductFilterTypeTotalQuestionCount, + BVProductFilterTypeTotalReviewCount, + BVProductFilterTypeTotalStoryCount }; @interface BVProductFilterTypeUtil : NSObject -+(NSString* _Nonnull)toString:(BVProductFilterType)filterOperator; ++ (nonnull NSString *)toString:(BVProductFilterType)filterOperator; -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.m b/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.m index 0ed3846f..d7b6c052 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVProductFilterType.m @@ -9,29 +9,43 @@ @implementation BVProductFilterTypeUtil -+(NSString* _Nonnull)toString:(BVProductFilterType)filterOperator { - - switch (filterOperator) { - - case BVProductFilterTypeId: return @"Id"; - case BVProductFilterTypeAverageOverallRating: return @"AverageOverallRating"; - case BVProductFilterTypeCategoryAncestorId: return @"CategoryAncestorId"; - case BVProductFilterTypeCategoryId: return @"CategoryId"; - case BVProductFilterTypeIsActive: return @"IsActive"; - case BVProductFilterTypeIsDisabled: return @"IsDisabled"; - case BVProductFilterTypeLastAnswerTime: return @"LastAnswerTime"; - case BVProductFilterTypeLastQuestionTime: return @"LastQuestionTime"; - case BVProductFilterTypeLastReviewTime: return @"LastReviewTime"; - case BVProductFilterTypeLastStoryTime: return @"LastStoryTime"; - case BVProductFilterTypeName: return @"Name"; - case BVProductFilterTypeRatingsOnlyReviewCount: return @"RatingsOnlyReviewCount"; - case BVProductFilterTypeTotalAnswerCount: return @"TotalAnswerCount"; - case BVProductFilterTypeTotalQuestionCount: return @"TotalQuestionCount"; - case BVProductFilterTypeTotalReviewCount: return @"TotalReviewCount"; - case BVProductFilterTypeTotalStoryCount: return @"TotalStoryCount"; - - } - ++ (nonnull NSString *)toString:(BVProductFilterType)filterOperator { + + switch (filterOperator) { + + case BVProductFilterTypeId: + return @"Id"; + case BVProductFilterTypeAverageOverallRating: + return @"AverageOverallRating"; + case BVProductFilterTypeCategoryAncestorId: + return @"CategoryAncestorId"; + case BVProductFilterTypeCategoryId: + return @"CategoryId"; + case BVProductFilterTypeIsActive: + return @"IsActive"; + case BVProductFilterTypeIsDisabled: + return @"IsDisabled"; + case BVProductFilterTypeLastAnswerTime: + return @"LastAnswerTime"; + case BVProductFilterTypeLastQuestionTime: + return @"LastQuestionTime"; + case BVProductFilterTypeLastReviewTime: + return @"LastReviewTime"; + case BVProductFilterTypeLastStoryTime: + return @"LastStoryTime"; + case BVProductFilterTypeName: + return @"Name"; + case BVProductFilterTypeRatingsOnlyReviewCount: + return @"RatingsOnlyReviewCount"; + case BVProductFilterTypeTotalAnswerCount: + return @"TotalAnswerCount"; + case BVProductFilterTypeTotalQuestionCount: + return @"TotalQuestionCount"; + case BVProductFilterTypeTotalReviewCount: + return @"TotalReviewCount"; + case BVProductFilterTypeTotalStoryCount: + return @"TotalStoryCount"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.h b/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.h index bf3ab211..89c87e4c 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.h @@ -11,37 +11,37 @@ The allowable filter types for `BVQuestionsAndAnswersRequest` requests. */ typedef NS_ENUM(NSInteger, BVQuestionFilterType) { - BVQuestionFilterTypeId, - BVQuestionFilterTypeAuthorId, - BVQuestionFilterTypeCampaignId, - BVQuestionFilterTypeCategoryAncestorId, - BVQuestionFilterTypeCategoryId, - BVQuestionFilterTypeContentLocale, - BVQuestionFilterTypeHasAnswers, - BVQuestionFilterTypeHasBestAnswer, - BVQuestionFilterTypeHasBrandAnswers, - BVQuestionFilterTypeHasPhotos, - BVQuestionFilterTypeHasStaffAnswers, - BVQuestionFilterTypeHasTags, - BVQuestionFilterTypeHasVideos, - BVQuestionFilterTypeIsFeatured, - BVQuestionFilterTypeIsSubjectActive, - BVQuestionFilterTypeLastApprovedAnswerSubmissionTime, - BVQuestionFilterTypeLastModeratedTime, - BVQuestionFilterTypeLastModificationTime, - BVQuestionFilterTypeModeratorCode, - BVQuestionFilterTypeSubmissionId, - BVQuestionFilterTypeSubmissionTime, - BVQuestionFilterTypeSummary, - BVQuestionFilterTypeTotalAnswerCount, - BVQuestionFilterTypeTotalFeedbackCount, - BVQuestionFilterTypeTotalNegativeFeedbackCount, - BVQuestionFilterTypeTotalPositiveFeedbackCount, - BVQuestionFilterTypeUserLocation + BVQuestionFilterTypeId, + BVQuestionFilterTypeAuthorId, + BVQuestionFilterTypeCampaignId, + BVQuestionFilterTypeCategoryAncestorId, + BVQuestionFilterTypeCategoryId, + BVQuestionFilterTypeContentLocale, + BVQuestionFilterTypeHasAnswers, + BVQuestionFilterTypeHasBestAnswer, + BVQuestionFilterTypeHasBrandAnswers, + BVQuestionFilterTypeHasPhotos, + BVQuestionFilterTypeHasStaffAnswers, + BVQuestionFilterTypeHasTags, + BVQuestionFilterTypeHasVideos, + BVQuestionFilterTypeIsFeatured, + BVQuestionFilterTypeIsSubjectActive, + BVQuestionFilterTypeLastApprovedAnswerSubmissionTime, + BVQuestionFilterTypeLastModeratedTime, + BVQuestionFilterTypeLastModificationTime, + BVQuestionFilterTypeModeratorCode, + BVQuestionFilterTypeSubmissionId, + BVQuestionFilterTypeSubmissionTime, + BVQuestionFilterTypeSummary, + BVQuestionFilterTypeTotalAnswerCount, + BVQuestionFilterTypeTotalFeedbackCount, + BVQuestionFilterTypeTotalNegativeFeedbackCount, + BVQuestionFilterTypeTotalPositiveFeedbackCount, + BVQuestionFilterTypeUserLocation }; @interface BVQuestionFilterTypeUtil : NSObject -+(NSString* _Nonnull)toString:(BVQuestionFilterType)filterOperator; ++ (nonnull NSString *)toString:(BVQuestionFilterType)filterOperator; -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.m b/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.m index 9b166943..bdad47b4 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVQuestionFilterType.m @@ -9,41 +9,65 @@ @implementation BVQuestionFilterTypeUtil -+(NSString* _Nonnull)toString:(BVQuestionFilterType)filterOperator { - - switch (filterOperator) { - - case BVQuestionFilterTypeId: return @"Id"; - case BVQuestionFilterTypeAuthorId: return @"AuthorId"; - case BVQuestionFilterTypeCampaignId: return @"CampaignId"; - case BVQuestionFilterTypeCategoryAncestorId: return @"CategoryAncestorId"; - case BVQuestionFilterTypeCategoryId: return @"CategoryId"; - case BVQuestionFilterTypeContentLocale: return @"ContentLocale"; - case BVQuestionFilterTypeHasAnswers: return @"HasAnswers"; - case BVQuestionFilterTypeHasBestAnswer: return @"HasBestAnswer"; - case BVQuestionFilterTypeHasBrandAnswers: return @"HasBrandAnswers"; - case BVQuestionFilterTypeHasPhotos: return @"HasPhotos"; - case BVQuestionFilterTypeHasStaffAnswers: return @"HasStaffAnswers"; - case BVQuestionFilterTypeHasTags: return @"HasTags"; - case BVQuestionFilterTypeHasVideos: return @"HasVideos"; - case BVQuestionFilterTypeIsFeatured: return @"IsFeatured"; - case BVQuestionFilterTypeIsSubjectActive: return @"IsSubjectActive"; - case BVQuestionFilterTypeLastApprovedAnswerSubmissionTime: return @"LastApprovedAnswerSubmissionTime"; - case BVQuestionFilterTypeLastModeratedTime: return @"LastModeratedTime"; - case BVQuestionFilterTypeLastModificationTime: return @"LastModificationTime"; - case BVQuestionFilterTypeModeratorCode: return @"ModeratorCode"; - case BVQuestionFilterTypeSubmissionId: return @"SubmissionId"; - case BVQuestionFilterTypeSubmissionTime: return @"SubmissionTime"; - case BVQuestionFilterTypeSummary: return @"Summary"; - case BVQuestionFilterTypeTotalAnswerCount: return @"TotalAnswerCount"; - case BVQuestionFilterTypeTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVQuestionFilterTypeTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVQuestionFilterTypeTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVQuestionFilterTypeUserLocation: return @"UserLocation"; - - } - -} ++ (nonnull NSString *)toString:(BVQuestionFilterType)filterOperator { + + switch (filterOperator) { + case BVQuestionFilterTypeId: + return @"Id"; + case BVQuestionFilterTypeAuthorId: + return @"AuthorId"; + case BVQuestionFilterTypeCampaignId: + return @"CampaignId"; + case BVQuestionFilterTypeCategoryAncestorId: + return @"CategoryAncestorId"; + case BVQuestionFilterTypeCategoryId: + return @"CategoryId"; + case BVQuestionFilterTypeContentLocale: + return @"ContentLocale"; + case BVQuestionFilterTypeHasAnswers: + return @"HasAnswers"; + case BVQuestionFilterTypeHasBestAnswer: + return @"HasBestAnswer"; + case BVQuestionFilterTypeHasBrandAnswers: + return @"HasBrandAnswers"; + case BVQuestionFilterTypeHasPhotos: + return @"HasPhotos"; + case BVQuestionFilterTypeHasStaffAnswers: + return @"HasStaffAnswers"; + case BVQuestionFilterTypeHasTags: + return @"HasTags"; + case BVQuestionFilterTypeHasVideos: + return @"HasVideos"; + case BVQuestionFilterTypeIsFeatured: + return @"IsFeatured"; + case BVQuestionFilterTypeIsSubjectActive: + return @"IsSubjectActive"; + case BVQuestionFilterTypeLastApprovedAnswerSubmissionTime: + return @"LastApprovedAnswerSubmissionTime"; + case BVQuestionFilterTypeLastModeratedTime: + return @"LastModeratedTime"; + case BVQuestionFilterTypeLastModificationTime: + return @"LastModificationTime"; + case BVQuestionFilterTypeModeratorCode: + return @"ModeratorCode"; + case BVQuestionFilterTypeSubmissionId: + return @"SubmissionId"; + case BVQuestionFilterTypeSubmissionTime: + return @"SubmissionTime"; + case BVQuestionFilterTypeSummary: + return @"Summary"; + case BVQuestionFilterTypeTotalAnswerCount: + return @"TotalAnswerCount"; + case BVQuestionFilterTypeTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVQuestionFilterTypeTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVQuestionFilterTypeTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVQuestionFilterTypeUserLocation: + return @"UserLocation"; + } +} @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.h b/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.h index 6e3bcb9a..6fce0b11 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.h @@ -11,35 +11,35 @@ The allowable filter types for `BVReviewsRequest` requests. */ typedef NS_ENUM(NSInteger, BVReviewFilterType) { - BVReviewFilterTypeId, - BVReviewFilterTypeAuthorId, - BVReviewFilterTypeCampaignId, - BVReviewFilterTypeCategoryAncestorId, - BVReviewFilterTypeContentLocale, - BVReviewFilterTypeHasComments, - BVReviewFilterTypeHasPhotos, - BVReviewFilterTypeHasTags, - BVReviewFilterTypeHasVideos, - BVReviewFilterTypeIsFeatured, - BVReviewFilterTypeIsRatingsOnly, - BVReviewFilterTypeIsRecommended, - BVReviewFilterTypeIsSubjectActive, - BVReviewFilterTypeIsSyndicated, - BVReviewFilterTypeLastModeratedTime, - BVReviewFilterTypeLastModificationTime, - BVReviewFilterTypeModeratorCode, - BVReviewFilterTypeRating, - BVReviewFilterTypeSubmissionId, - BVReviewFilterTypeSubmissionTime, - BVReviewFilterTypeTotalCommentCount, - BVReviewFilterTypeTotalFeedbackCount, - BVReviewFilterTypeTotalNegativeFeedbackCount, - BVReviewFilterTypeTotalPositiveFeedbackCount, - BVReviewFilterTypeUserLocation + BVReviewFilterTypeId, + BVReviewFilterTypeAuthorId, + BVReviewFilterTypeCampaignId, + BVReviewFilterTypeCategoryAncestorId, + BVReviewFilterTypeContentLocale, + BVReviewFilterTypeHasComments, + BVReviewFilterTypeHasPhotos, + BVReviewFilterTypeHasTags, + BVReviewFilterTypeHasVideos, + BVReviewFilterTypeIsFeatured, + BVReviewFilterTypeIsRatingsOnly, + BVReviewFilterTypeIsRecommended, + BVReviewFilterTypeIsSubjectActive, + BVReviewFilterTypeIsSyndicated, + BVReviewFilterTypeLastModeratedTime, + BVReviewFilterTypeLastModificationTime, + BVReviewFilterTypeModeratorCode, + BVReviewFilterTypeRating, + BVReviewFilterTypeSubmissionId, + BVReviewFilterTypeSubmissionTime, + BVReviewFilterTypeTotalCommentCount, + BVReviewFilterTypeTotalFeedbackCount, + BVReviewFilterTypeTotalNegativeFeedbackCount, + BVReviewFilterTypeTotalPositiveFeedbackCount, + BVReviewFilterTypeUserLocation }; @interface BVReviewFilterTypeUtil : NSObject -+(NSString* _Nonnull)toString:(BVReviewFilterType)filterOperator; ++ (nonnull NSString *)toString:(BVReviewFilterType)filterOperator; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.m b/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.m index 2d50ad91..48e83b37 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVReviewFilterType.m @@ -9,39 +9,61 @@ @implementation BVReviewFilterTypeUtil -+(NSString* _Nonnull)toString:(BVReviewFilterType)filterOperator { - - switch (filterOperator) { - - case BVReviewFilterTypeId: return @"Id"; - case BVReviewFilterTypeAuthorId: return @"AuthorId"; - case BVReviewFilterTypeCampaignId: return @"CampaignId"; - case BVReviewFilterTypeCategoryAncestorId: return @"CategoryAncestorId"; - case BVReviewFilterTypeContentLocale: return @"ContentLocale"; - case BVReviewFilterTypeHasComments: return @"HasComments"; - case BVReviewFilterTypeHasPhotos: return @"HasPhotos"; - case BVReviewFilterTypeHasTags: return @"HasTags"; - case BVReviewFilterTypeHasVideos: return @"HasVideos"; - case BVReviewFilterTypeIsFeatured: return @"IsFeatured"; - case BVReviewFilterTypeIsRatingsOnly: return @"IsRatingsOnly"; - case BVReviewFilterTypeIsRecommended: return @"IsRecommended"; - case BVReviewFilterTypeIsSubjectActive: return @"IsSubjectActive"; - case BVReviewFilterTypeIsSyndicated: return @"IsSyndicated"; - case BVReviewFilterTypeLastModeratedTime: return @"LastModeratedTime"; - case BVReviewFilterTypeLastModificationTime: return @"LastModificationTime"; - case BVReviewFilterTypeModeratorCode: return @"ModeratorCode"; - case BVReviewFilterTypeRating: return @"Rating"; - case BVReviewFilterTypeSubmissionId: return @"SubmissionId"; - case BVReviewFilterTypeSubmissionTime: return @"SubmissionTime"; - case BVReviewFilterTypeTotalCommentCount: return @"TotalCommentCount"; - case BVReviewFilterTypeTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVReviewFilterTypeTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVReviewFilterTypeTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVReviewFilterTypeUserLocation: return @"UserLocation"; ++ (nonnull NSString *)toString:(BVReviewFilterType)filterOperator { - } - -} + switch (filterOperator) { + case BVReviewFilterTypeId: + return @"Id"; + case BVReviewFilterTypeAuthorId: + return @"AuthorId"; + case BVReviewFilterTypeCampaignId: + return @"CampaignId"; + case BVReviewFilterTypeCategoryAncestorId: + return @"CategoryAncestorId"; + case BVReviewFilterTypeContentLocale: + return @"ContentLocale"; + case BVReviewFilterTypeHasComments: + return @"HasComments"; + case BVReviewFilterTypeHasPhotos: + return @"HasPhotos"; + case BVReviewFilterTypeHasTags: + return @"HasTags"; + case BVReviewFilterTypeHasVideos: + return @"HasVideos"; + case BVReviewFilterTypeIsFeatured: + return @"IsFeatured"; + case BVReviewFilterTypeIsRatingsOnly: + return @"IsRatingsOnly"; + case BVReviewFilterTypeIsRecommended: + return @"IsRecommended"; + case BVReviewFilterTypeIsSubjectActive: + return @"IsSubjectActive"; + case BVReviewFilterTypeIsSyndicated: + return @"IsSyndicated"; + case BVReviewFilterTypeLastModeratedTime: + return @"LastModeratedTime"; + case BVReviewFilterTypeLastModificationTime: + return @"LastModificationTime"; + case BVReviewFilterTypeModeratorCode: + return @"ModeratorCode"; + case BVReviewFilterTypeRating: + return @"Rating"; + case BVReviewFilterTypeSubmissionId: + return @"SubmissionId"; + case BVReviewFilterTypeSubmissionTime: + return @"SubmissionTime"; + case BVReviewFilterTypeTotalCommentCount: + return @"TotalCommentCount"; + case BVReviewFilterTypeTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVReviewFilterTypeTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVReviewFilterTypeTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVReviewFilterTypeUserLocation: + return @"UserLocation"; + } +} @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSort.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSort.h index 73d94793..ba47870b 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSort.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSort.h @@ -5,21 +5,23 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVSortOptionProducts.h" +#import /* Sort ordering can be `ascending` or `descending`. */ typedef NS_ENUM(NSInteger, BVSortOrder) { - BVSortOrderAscending, - BVSortOrderDescending + BVSortOrderAscending, + BVSortOrderDescending }; @interface BVSort : NSObject --(id _Nonnull)initWithOption:(BVSortOptionProducts)option order:(BVSortOrder)order; --(id _Nonnull)initWithOptionString:(NSString* _Nonnull)optionString order:(BVSortOrder)order; --(NSString* _Nonnull)toString; +- (nonnull id)initWithOption:(BVSortOptionProducts)option + order:(BVSortOrder)order; +- (nonnull id)initWithOptionString:(nonnull NSString *)optionString + order:(BVSortOrder)order; +- (nonnull NSString *)toString; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSort.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSort.m index 5f8b5afa..f5a0cbbb 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSort.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSort.m @@ -7,39 +7,42 @@ #import "BVSort.h" -@interface BVSort() +@interface BVSort () -@property NSString* _Nonnull option; +@property(nonnull) NSString *option; @property BVSortOrder order; @end @implementation BVSort --(id _Nonnull)initWithOption:(BVSortOptionProducts)option order:(BVSortOrder)order { - self = [super init]; - if(self){ - self.option = [BVSortOptionProductsUtil toString:option]; - self.order = order; - } - return self; +- (nonnull id)initWithOption:(BVSortOptionProducts)option + order:(BVSortOrder)order { + self = [super init]; + if (self) { + self.option = [BVSortOptionProductsUtil toString:option]; + self.order = order; + } + return self; } --(id _Nonnull)initWithOptionString:(NSString* _Nonnull)optionString order:(BVSortOrder)order { - self = [super init]; - if(self){ - self.option = optionString; - self.order = order; - } - return self; +- (nonnull id)initWithOptionString:(nonnull NSString *)optionString + order:(BVSortOrder)order { + self = [super init]; + if (self) { + self.option = optionString; + self.order = order; + } + return self; } --(NSString* _Nonnull)toString { - return [NSString stringWithFormat:@"%@:%@", self.option, [self orderingString:self.order]]; +- (nonnull NSString *)toString { + return [NSString + stringWithFormat:@"%@:%@", self.option, [self orderingString:self.order]]; } --(NSString* _Nonnull)orderingString:(BVSortOrder)order { - return self.order == BVSortOrderAscending ? @"asc" : @"desc"; +- (nonnull NSString *)orderingString:(BVSortOrder)order { + return self.order == BVSortOrderAscending ? @"asc" : @"desc"; } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.h index 1d7dd2f9..93c1124f 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.h @@ -8,30 +8,31 @@ #import /* - The allowable sorting types for answers included in a `BVQuestionsAndAnswersRequest` request. + The allowable sorting types for answers included in a + `BVQuestionsAndAnswersRequest` request. */ typedef NS_ENUM(NSInteger, BVSortOptionAnswers) { - BVSortOptionAnswersId, - BVSortOptionAnswersAuthorId, - BVSortOptionAnswersCampaignId, - BVSortOptionAnswersContentLocale, - BVSortOptionAnswersHasPhotos, - BVSortOptionAnswersIsBestAnswer, - BVSortOptionAnswersIsFeatured, - BVSortOptionAnswersLastModeratedTime, - BVSortOptionAnswersLastModificationTime, - BVSortOptionAnswersProductId, - BVSortOptionAnswersQuestionId, - BVSortOptionAnswersSubmissionId, - BVSortOptionAnswersSubmissionTime, - BVSortOptionAnswersTotalFeedbackCount, - BVSortOptionAnswersTotalNegativeFeedbackCount, - BVSortOptionAnswersTotalPositiveFeedbackCount, - BVSortOptionAnswersUserLocation + BVSortOptionAnswersId, + BVSortOptionAnswersAuthorId, + BVSortOptionAnswersCampaignId, + BVSortOptionAnswersContentLocale, + BVSortOptionAnswersHasPhotos, + BVSortOptionAnswersIsBestAnswer, + BVSortOptionAnswersIsFeatured, + BVSortOptionAnswersLastModeratedTime, + BVSortOptionAnswersLastModificationTime, + BVSortOptionAnswersProductId, + BVSortOptionAnswersQuestionId, + BVSortOptionAnswersSubmissionId, + BVSortOptionAnswersSubmissionTime, + BVSortOptionAnswersTotalFeedbackCount, + BVSortOptionAnswersTotalNegativeFeedbackCount, + BVSortOptionAnswersTotalPositiveFeedbackCount, + BVSortOptionAnswersUserLocation }; @interface BVSortOptionAnswersUtil : NSObject -+(NSString* _Nonnull)toString:(BVSortOptionAnswers)BVSortOptionAnswers; ++ (nonnull NSString *)toString:(BVSortOptionAnswers)BVSortOptionAnswers; -@end \ No newline at end of file +@end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.m index ca6bd283..7ff133bf 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionAnswers.m @@ -9,30 +9,45 @@ @implementation BVSortOptionAnswersUtil -+(NSString* _Nonnull)toString:(BVSortOptionAnswers)BVSortOptionAnswers { - - switch (BVSortOptionAnswers) { - - case BVSortOptionAnswersId: return @"Id"; - case BVSortOptionAnswersAuthorId: return @"AuthorId"; - case BVSortOptionAnswersCampaignId: return @"CampaignId"; - case BVSortOptionAnswersContentLocale: return @"ContentLocale"; - case BVSortOptionAnswersHasPhotos: return @"HasPhotos"; - case BVSortOptionAnswersIsBestAnswer: return @"IsBestAnswer"; - case BVSortOptionAnswersIsFeatured: return @"IsFeatured"; - case BVSortOptionAnswersLastModeratedTime: return @"LastModeratedTime"; - case BVSortOptionAnswersLastModificationTime: return @"LastModificationTime"; - case BVSortOptionAnswersProductId: return @"ProductId"; - case BVSortOptionAnswersQuestionId: return @"QuestionId"; - case BVSortOptionAnswersSubmissionId: return @"SubmissionId"; - case BVSortOptionAnswersSubmissionTime: return @"SubmissionTime"; - case BVSortOptionAnswersTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVSortOptionAnswersTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVSortOptionAnswersTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVSortOptionAnswersUserLocation: return @"UserLocation"; - - } - ++ (nonnull NSString *)toString:(BVSortOptionAnswers)BVSortOptionAnswers { + + switch (BVSortOptionAnswers) { + + case BVSortOptionAnswersId: + return @"Id"; + case BVSortOptionAnswersAuthorId: + return @"AuthorId"; + case BVSortOptionAnswersCampaignId: + return @"CampaignId"; + case BVSortOptionAnswersContentLocale: + return @"ContentLocale"; + case BVSortOptionAnswersHasPhotos: + return @"HasPhotos"; + case BVSortOptionAnswersIsBestAnswer: + return @"IsBestAnswer"; + case BVSortOptionAnswersIsFeatured: + return @"IsFeatured"; + case BVSortOptionAnswersLastModeratedTime: + return @"LastModeratedTime"; + case BVSortOptionAnswersLastModificationTime: + return @"LastModificationTime"; + case BVSortOptionAnswersProductId: + return @"ProductId"; + case BVSortOptionAnswersQuestionId: + return @"QuestionId"; + case BVSortOptionAnswersSubmissionId: + return @"SubmissionId"; + case BVSortOptionAnswersSubmissionTime: + return @"SubmissionTime"; + case BVSortOptionAnswersTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVSortOptionAnswersTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVSortOptionAnswersTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVSortOptionAnswersUserLocation: + return @"UserLocation"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.h index 7c494a2a..55848270 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.h @@ -8,30 +8,31 @@ #import /* - The allowable sort types for `BVReviewsRequest` and `BVQuestionsAndAnswersRequests` requests. + The allowable sort types for `BVReviewsRequest` and + `BVQuestionsAndAnswersRequests` requests. */ typedef NS_ENUM(NSInteger, BVSortOptionProducts) { - BVSortOptionProductsId, - BVSortOptionProductsAverageOverallRating, - BVSortOptionProductsRating, - BVSortOptionProductsCategoryId, - BVSortOptionProductsIsActive, - BVSortOptionProductsIsDisabled, - BVSortOptionProductsLastAnswerTime, - BVSortOptionProductsLastQuestionTime, - BVSortOptionProductsLastReviewTime, - BVSortOptionProductsLastStoryTime, - BVSortOptionProductsName, - BVSortOptionProductsRatingsOnlyReviewCount, - BVSortOptionProductsTotalAnswerCount, - BVSortOptionProductsTotalQuestionCount, - BVSortOptionProductsTotalReviewCount, - BVSortOptionProductsTotalStoryCount, - BVSortOptionProductsHelpfulness + BVSortOptionProductsId, + BVSortOptionProductsAverageOverallRating, + BVSortOptionProductsRating, + BVSortOptionProductsCategoryId, + BVSortOptionProductsIsActive, + BVSortOptionProductsIsDisabled, + BVSortOptionProductsLastAnswerTime, + BVSortOptionProductsLastQuestionTime, + BVSortOptionProductsLastReviewTime, + BVSortOptionProductsLastStoryTime, + BVSortOptionProductsName, + BVSortOptionProductsRatingsOnlyReviewCount, + BVSortOptionProductsTotalAnswerCount, + BVSortOptionProductsTotalQuestionCount, + BVSortOptionProductsTotalReviewCount, + BVSortOptionProductsTotalStoryCount, + BVSortOptionProductsHelpfulness }; @interface BVSortOptionProductsUtil : NSObject -+(NSString* _Nonnull)toString:(BVSortOptionProducts)BVSortOptionProducts; ++ (nonnull NSString *)toString:(BVSortOptionProducts)BVSortOptionProducts; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.m index a9c72d5c..b01dc696 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionProducts.m @@ -9,30 +9,45 @@ @implementation BVSortOptionProductsUtil -+(NSString* _Nonnull)toString:(BVSortOptionProducts)BVSortOptionProducts { - - switch (BVSortOptionProducts) { - - case BVSortOptionProductsId: return @"Id"; - case BVSortOptionProductsAverageOverallRating: return @"AverageOverallRating"; - case BVSortOptionProductsRating: return @"Rating"; - case BVSortOptionProductsCategoryId: return @"CategoryId"; - case BVSortOptionProductsIsActive: return @"IsActive"; - case BVSortOptionProductsIsDisabled: return @"IsDisabled"; - case BVSortOptionProductsLastAnswerTime: return @"LastAnswerTime"; - case BVSortOptionProductsLastQuestionTime: return @"LastQuestionTime"; - case BVSortOptionProductsLastReviewTime: return @"LastReviewTime"; - case BVSortOptionProductsLastStoryTime: return @"LastStoryTime"; - case BVSortOptionProductsName: return @"Name"; - case BVSortOptionProductsRatingsOnlyReviewCount: return @"RatingsOnlyReviewCount"; - case BVSortOptionProductsTotalAnswerCount: return @"TotalAnswerCount"; - case BVSortOptionProductsTotalQuestionCount: return @"TotalQuestionCount"; - case BVSortOptionProductsTotalReviewCount: return @"TotalReviewCount"; - case BVSortOptionProductsTotalStoryCount: return @"TotalStoryCount"; - case BVSortOptionProductsHelpfulness: return @"Helpfulness"; - - } - ++ (nonnull NSString *)toString:(BVSortOptionProducts)BVSortOptionProducts { + + switch (BVSortOptionProducts) { + + case BVSortOptionProductsId: + return @"Id"; + case BVSortOptionProductsAverageOverallRating: + return @"AverageOverallRating"; + case BVSortOptionProductsRating: + return @"Rating"; + case BVSortOptionProductsCategoryId: + return @"CategoryId"; + case BVSortOptionProductsIsActive: + return @"IsActive"; + case BVSortOptionProductsIsDisabled: + return @"IsDisabled"; + case BVSortOptionProductsLastAnswerTime: + return @"LastAnswerTime"; + case BVSortOptionProductsLastQuestionTime: + return @"LastQuestionTime"; + case BVSortOptionProductsLastReviewTime: + return @"LastReviewTime"; + case BVSortOptionProductsLastStoryTime: + return @"LastStoryTime"; + case BVSortOptionProductsName: + return @"Name"; + case BVSortOptionProductsRatingsOnlyReviewCount: + return @"RatingsOnlyReviewCount"; + case BVSortOptionProductsTotalAnswerCount: + return @"TotalAnswerCount"; + case BVSortOptionProductsTotalQuestionCount: + return @"TotalQuestionCount"; + case BVSortOptionProductsTotalReviewCount: + return @"TotalReviewCount"; + case BVSortOptionProductsTotalStoryCount: + return @"TotalStoryCount"; + case BVSortOptionProductsHelpfulness: + return @"Helpfulness"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.h index a4a877da..5b1300a6 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.h @@ -11,34 +11,34 @@ The allowable sort types for `BVQuestionsAndAnswersRequests` requests. */ typedef NS_ENUM(NSInteger, BVSortOptionQuestions) { - BVSortOptionQuestionsId, - BVSortOptionQuestionsAuthorId, - BVSortOptionQuestionsCampaignId, - BVSortOptionQuestionsContentLocale, - BVSortOptionQuestionsHasAnswers, - BVSortOptionQuestionsHasBestAnswer, - BVSortOptionQuestionsHasPhotos, - BVSortOptionQuestionsHasStaffAnswers, - BVSortOptionQuestionsIsFeatured, - BVSortOptionQuestionsIsSubjectActive, - BVSortOptionQuestionsLastApprovedAnswerSubmissionTime, - BVSortOptionQuestionsLastModeratorCode, - BVSortOptionQuestionsLastModeratedTime, - BVSortOptionQuestionsLastModificationTime, - BVSortOptionQuestionsProductId, - BVSortOptionQuestionsSubmissionId, - BVSortOptionQuestionsSubmissionTime, - BVSortOptionQuestionsSummary, - BVSortOptionQuestionsTotalAnswerCount, - BVSortOptionQuestionsTotalFeedbackCount, - BVSortOptionQuestionsTotalNegativeFeedbackCount, - BVSortOptionQuestionsTotalPositiveFeedbackCount, - BVSortOptionQuestionsUserLocation, - BVSortOptionQuestionsHasVideos, // PRR Only + BVSortOptionQuestionsId, + BVSortOptionQuestionsAuthorId, + BVSortOptionQuestionsCampaignId, + BVSortOptionQuestionsContentLocale, + BVSortOptionQuestionsHasAnswers, + BVSortOptionQuestionsHasBestAnswer, + BVSortOptionQuestionsHasPhotos, + BVSortOptionQuestionsHasStaffAnswers, + BVSortOptionQuestionsIsFeatured, + BVSortOptionQuestionsIsSubjectActive, + BVSortOptionQuestionsLastApprovedAnswerSubmissionTime, + BVSortOptionQuestionsLastModeratorCode, + BVSortOptionQuestionsLastModeratedTime, + BVSortOptionQuestionsLastModificationTime, + BVSortOptionQuestionsProductId, + BVSortOptionQuestionsSubmissionId, + BVSortOptionQuestionsSubmissionTime, + BVSortOptionQuestionsSummary, + BVSortOptionQuestionsTotalAnswerCount, + BVSortOptionQuestionsTotalFeedbackCount, + BVSortOptionQuestionsTotalNegativeFeedbackCount, + BVSortOptionQuestionsTotalPositiveFeedbackCount, + BVSortOptionQuestionsUserLocation, + BVSortOptionQuestionsHasVideos, // PRR Only }; @interface BVSortOptionQuestionsUtil : NSObject -+(NSString* _Nonnull)toString:(BVSortOptionQuestions)BVSortOptionQuestions; ++ (nonnull NSString *)toString:(BVSortOptionQuestions)BVSortOptionQuestions; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.m index e75f9ed1..29738806 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionQuestions.m @@ -9,38 +9,59 @@ @implementation BVSortOptionQuestionsUtil -+(NSString* _Nonnull)toString:(BVSortOptionQuestions)BVSortOptionQuestions { - - switch (BVSortOptionQuestions) { - - case BVSortOptionQuestionsId: return @"Id"; - case BVSortOptionQuestionsAuthorId: return @"AuthorId"; - case BVSortOptionQuestionsCampaignId: return @"CampaignId"; - case BVSortOptionQuestionsContentLocale: return @"ContentLocale"; - case BVSortOptionQuestionsHasAnswers: return @"HasAnswers"; - case BVSortOptionQuestionsHasBestAnswer: return @"HasBestAnswer"; - case BVSortOptionQuestionsHasPhotos: return @"HasPhotos"; - case BVSortOptionQuestionsHasStaffAnswers: return @"HasStaffAnswers"; - case BVSortOptionQuestionsIsFeatured: return @"IsFeatured"; - case BVSortOptionQuestionsIsSubjectActive: return @"IsSubjectActive"; - case BVSortOptionQuestionsLastApprovedAnswerSubmissionTime: return @"LastApprovedAnswerSubmissionTime"; - case BVSortOptionQuestionsLastModeratedTime: return @"LastModeratedTime"; - case BVSortOptionQuestionsLastModificationTime: return @"LastModificationTime"; - case BVSortOptionQuestionsLastModeratorCode: return @"ModeratorCode"; - case BVSortOptionQuestionsProductId: return @"ProductId"; - case BVSortOptionQuestionsSubmissionId: return @"SubmissionId"; - case BVSortOptionQuestionsSubmissionTime: return @"SubmissionTime"; - case BVSortOptionQuestionsSummary: return @"Summary"; - case BVSortOptionQuestionsTotalAnswerCount: return @"TotalAnswerCount"; - case BVSortOptionQuestionsTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVSortOptionQuestionsTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVSortOptionQuestionsTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVSortOptionQuestionsUserLocation: return @"UserLocation"; - case BVSortOptionQuestionsHasVideos: return @"HasVideos"; - - } - -} ++ (nonnull NSString *)toString:(BVSortOptionQuestions)BVSortOptionQuestions { + + switch (BVSortOptionQuestions) { + case BVSortOptionQuestionsId: + return @"Id"; + case BVSortOptionQuestionsAuthorId: + return @"AuthorId"; + case BVSortOptionQuestionsCampaignId: + return @"CampaignId"; + case BVSortOptionQuestionsContentLocale: + return @"ContentLocale"; + case BVSortOptionQuestionsHasAnswers: + return @"HasAnswers"; + case BVSortOptionQuestionsHasBestAnswer: + return @"HasBestAnswer"; + case BVSortOptionQuestionsHasPhotos: + return @"HasPhotos"; + case BVSortOptionQuestionsHasStaffAnswers: + return @"HasStaffAnswers"; + case BVSortOptionQuestionsIsFeatured: + return @"IsFeatured"; + case BVSortOptionQuestionsIsSubjectActive: + return @"IsSubjectActive"; + case BVSortOptionQuestionsLastApprovedAnswerSubmissionTime: + return @"LastApprovedAnswerSubmissionTime"; + case BVSortOptionQuestionsLastModeratedTime: + return @"LastModeratedTime"; + case BVSortOptionQuestionsLastModificationTime: + return @"LastModificationTime"; + case BVSortOptionQuestionsLastModeratorCode: + return @"ModeratorCode"; + case BVSortOptionQuestionsProductId: + return @"ProductId"; + case BVSortOptionQuestionsSubmissionId: + return @"SubmissionId"; + case BVSortOptionQuestionsSubmissionTime: + return @"SubmissionTime"; + case BVSortOptionQuestionsSummary: + return @"Summary"; + case BVSortOptionQuestionsTotalAnswerCount: + return @"TotalAnswerCount"; + case BVSortOptionQuestionsTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVSortOptionQuestionsTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVSortOptionQuestionsTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVSortOptionQuestionsUserLocation: + return @"UserLocation"; + case BVSortOptionQuestionsHasVideos: + return @"HasVideos"; + } +} @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.h index ec873ec5..86ee14c1 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.h @@ -11,35 +11,35 @@ The allowable sort types for `BVReviewsRequest` requests. */ typedef NS_ENUM(NSInteger, BVSortOptionReviews) { - BVSortOptionReviewsId, - BVSortOptionReviewsAuthorId, - BVSortOptionReviewsCampaignId, - BVSortOptionReviewsContentLocale, - BVSortOptionReviewsHasComments, - BVSortOptionReviewsHasPhotos, - BVSortOptionReviewsHasTags, - BVSortOptionReviewsHasVideos, - BVSortOptionReviewsHelpfulness, - BVSortOptionReviewsIsFeatured, - BVSortOptionReviewsIsRatingsOnly, - BVSortOptionReviewsIsRecommended, - BVSortOptionReviewsIsSubjectActive, - BVSortOptionReviewsIsSyndicated, - BVSortOptionReviewsLastModeratedTime, - BVSortOptionReviewsLastModificationTime, - BVSortOptionReviewsProductId, - BVSortOptionReviewsRating, - BVSortOptionReviewsSubmissionId, - BVSortOptionReviewsSubmissionTime, - BVSortOptionReviewsTotalCommentCount, - BVSortOptionReviewsTotalFeedbackCount, - BVSortOptionReviewsTotalNegativeFeedbackCount, - BVSortOptionReviewsTotalPositiveFeedbackCount, - BVSortOptionReviewsUserLocation + BVSortOptionReviewsId, + BVSortOptionReviewsAuthorId, + BVSortOptionReviewsCampaignId, + BVSortOptionReviewsContentLocale, + BVSortOptionReviewsHasComments, + BVSortOptionReviewsHasPhotos, + BVSortOptionReviewsHasTags, + BVSortOptionReviewsHasVideos, + BVSortOptionReviewsHelpfulness, + BVSortOptionReviewsIsFeatured, + BVSortOptionReviewsIsRatingsOnly, + BVSortOptionReviewsIsRecommended, + BVSortOptionReviewsIsSubjectActive, + BVSortOptionReviewsIsSyndicated, + BVSortOptionReviewsLastModeratedTime, + BVSortOptionReviewsLastModificationTime, + BVSortOptionReviewsProductId, + BVSortOptionReviewsRating, + BVSortOptionReviewsSubmissionId, + BVSortOptionReviewsSubmissionTime, + BVSortOptionReviewsTotalCommentCount, + BVSortOptionReviewsTotalFeedbackCount, + BVSortOptionReviewsTotalNegativeFeedbackCount, + BVSortOptionReviewsTotalPositiveFeedbackCount, + BVSortOptionReviewsUserLocation }; @interface BVSortOptionReviewUtil : NSObject -+(NSString* _Nonnull)toString:(BVSortOptionReviews)BVSortOptionReviews; ++ (nonnull NSString *)toString:(BVSortOptionReviews)BVSortOptionReviews; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.m index 35747309..53189598 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionReviews.m @@ -9,38 +9,61 @@ @implementation BVSortOptionReviewUtil -+(NSString* _Nonnull)toString:(BVSortOptionReviews)BVSortOptionReviews { - - switch (BVSortOptionReviews) { - - case BVSortOptionReviewsId: return @"Id"; - case BVSortOptionReviewsAuthorId: return @"AuthorId"; - case BVSortOptionReviewsCampaignId: return @"CampaignId"; - case BVSortOptionReviewsContentLocale: return @"ContentLocale"; - case BVSortOptionReviewsHasComments: return @"HasComments"; - case BVSortOptionReviewsHasPhotos: return @"HasPhotos"; - case BVSortOptionReviewsHasTags: return @"HasTags"; - case BVSortOptionReviewsHasVideos: return @"HasVideos"; - case BVSortOptionReviewsHelpfulness: return @"Helpfulness"; - case BVSortOptionReviewsIsFeatured: return @"IsFeatured"; - case BVSortOptionReviewsIsRatingsOnly: return @"IsRatingsOnly"; - case BVSortOptionReviewsIsRecommended: return @"IsRecommended"; - case BVSortOptionReviewsIsSubjectActive: return @"IsSubjectActive"; - case BVSortOptionReviewsIsSyndicated: return @"IsSyndicated"; - case BVSortOptionReviewsLastModeratedTime: return @"LastModeratedTime"; - case BVSortOptionReviewsLastModificationTime: return @"LastModificationTime"; - case BVSortOptionReviewsProductId: return @"ProductId"; - case BVSortOptionReviewsRating: return @"Rating"; - case BVSortOptionReviewsSubmissionId: return @"SubmissionId"; - case BVSortOptionReviewsSubmissionTime: return @"SubmissionTime"; - case BVSortOptionReviewsTotalCommentCount: return @"TotalCommentCount"; - case BVSortOptionReviewsTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVSortOptionReviewsTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVSortOptionReviewsTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVSortOptionReviewsUserLocation: return @"UserLocation"; - - } - ++ (nonnull NSString *)toString:(BVSortOptionReviews)BVSortOptionReviews { + + switch (BVSortOptionReviews) { + + case BVSortOptionReviewsId: + return @"Id"; + case BVSortOptionReviewsAuthorId: + return @"AuthorId"; + case BVSortOptionReviewsCampaignId: + return @"CampaignId"; + case BVSortOptionReviewsContentLocale: + return @"ContentLocale"; + case BVSortOptionReviewsHasComments: + return @"HasComments"; + case BVSortOptionReviewsHasPhotos: + return @"HasPhotos"; + case BVSortOptionReviewsHasTags: + return @"HasTags"; + case BVSortOptionReviewsHasVideos: + return @"HasVideos"; + case BVSortOptionReviewsHelpfulness: + return @"Helpfulness"; + case BVSortOptionReviewsIsFeatured: + return @"IsFeatured"; + case BVSortOptionReviewsIsRatingsOnly: + return @"IsRatingsOnly"; + case BVSortOptionReviewsIsRecommended: + return @"IsRecommended"; + case BVSortOptionReviewsIsSubjectActive: + return @"IsSubjectActive"; + case BVSortOptionReviewsIsSyndicated: + return @"IsSyndicated"; + case BVSortOptionReviewsLastModeratedTime: + return @"LastModeratedTime"; + case BVSortOptionReviewsLastModificationTime: + return @"LastModificationTime"; + case BVSortOptionReviewsProductId: + return @"ProductId"; + case BVSortOptionReviewsRating: + return @"Rating"; + case BVSortOptionReviewsSubmissionId: + return @"SubmissionId"; + case BVSortOptionReviewsSubmissionTime: + return @"SubmissionTime"; + case BVSortOptionReviewsTotalCommentCount: + return @"TotalCommentCount"; + case BVSortOptionReviewsTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVSortOptionReviewsTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVSortOptionReviewsTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVSortOptionReviewsUserLocation: + return @"UserLocation"; + } } @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.h b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.h index a7d3bae8..7d3a06b6 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.h +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.h @@ -9,29 +9,29 @@ /** The allowable sort types for `BVCommentsRequest` requests. - API Reference: https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-display#sort-options + API Reference: + https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-display#sort-options */ typedef NS_ENUM(NSInteger, BVSortOptionComments) { - BVSortOptionCommentId, - BVSortOptionCommentAuthorId, - BVSortOptionCommentCampaignId, - BVSortOptionCommentContentLocale, - BVSortOptionCommentsIsFeatured, - BVSortOptionCommentsLastModeratedTime, - BVSortOptionCommentsLastModificationTime, - BVSortOptionCommentsProductId, - BVSortOptionCommentsReviewId, - BVSortOptionCommentsSubmissionId, - BVSortOptionCommentsSubmissionTime, - BVSortOptionCommentsTotalFeedbackCount, - BVSortOptionCommentsTotalNegativeFeedbackCount, - BVSortOptionCommentsTotalPositiveFeedbackCount, - BVSortOptionCommentsUserLocation + BVSortOptionCommentId, + BVSortOptionCommentAuthorId, + BVSortOptionCommentCampaignId, + BVSortOptionCommentContentLocale, + BVSortOptionCommentsIsFeatured, + BVSortOptionCommentsLastModeratedTime, + BVSortOptionCommentsLastModificationTime, + BVSortOptionCommentsProductId, + BVSortOptionCommentsReviewId, + BVSortOptionCommentsSubmissionId, + BVSortOptionCommentsSubmissionTime, + BVSortOptionCommentsTotalFeedbackCount, + BVSortOptionCommentsTotalNegativeFeedbackCount, + BVSortOptionCommentsTotalPositiveFeedbackCount, + BVSortOptionCommentsUserLocation }; - @interface BVSortOptionsCommentUtil : NSObject -+(NSString* _Nonnull)toString:(BVSortOptionComments)commentSortOption; ++ (nonnull NSString *)toString:(BVSortOptionComments)commentSortOption; @end diff --git a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.m b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.m index f5806f9b..b3ebd680 100644 --- a/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.m +++ b/Pod/BVConversations/Display/Sorting & Filtering/BVSortOptionsComments.m @@ -9,25 +9,40 @@ @implementation BVSortOptionsCommentUtil -+(NSString* _Nonnull)toString:(BVSortOptionComments)commentSortOption{ - - switch (commentSortOption) { - case BVSortOptionCommentId: return @"Id"; - case BVSortOptionCommentAuthorId: return @"AuthorId"; - case BVSortOptionCommentCampaignId: return @"CampaignId"; - case BVSortOptionCommentContentLocale: return @"ContentLocale"; - case BVSortOptionCommentsIsFeatured: return @"IsFeatured"; - case BVSortOptionCommentsLastModeratedTime: return @"LastModeratedTime"; - case BVSortOptionCommentsLastModificationTime: return @"LastModificationTime"; - case BVSortOptionCommentsProductId: return @"ProductId"; - case BVSortOptionCommentsReviewId: return @"ReviewId"; - case BVSortOptionCommentsSubmissionId: return @"SubmissionId"; - case BVSortOptionCommentsSubmissionTime: return @"SubmissionTime"; - case BVSortOptionCommentsTotalFeedbackCount: return @"TotalFeedbackCount"; - case BVSortOptionCommentsTotalNegativeFeedbackCount: return @"TotalNegativeFeedbackCount"; - case BVSortOptionCommentsTotalPositiveFeedbackCount: return @"TotalPositiveFeedbackCount"; - case BVSortOptionCommentsUserLocation: return @"UserLocation"; - } ++ (nonnull NSString *)toString:(BVSortOptionComments)commentSortOption { + + switch (commentSortOption) { + case BVSortOptionCommentId: + return @"Id"; + case BVSortOptionCommentAuthorId: + return @"AuthorId"; + case BVSortOptionCommentCampaignId: + return @"CampaignId"; + case BVSortOptionCommentContentLocale: + return @"ContentLocale"; + case BVSortOptionCommentsIsFeatured: + return @"IsFeatured"; + case BVSortOptionCommentsLastModeratedTime: + return @"LastModeratedTime"; + case BVSortOptionCommentsLastModificationTime: + return @"LastModificationTime"; + case BVSortOptionCommentsProductId: + return @"ProductId"; + case BVSortOptionCommentsReviewId: + return @"ReviewId"; + case BVSortOptionCommentsSubmissionId: + return @"SubmissionId"; + case BVSortOptionCommentsSubmissionTime: + return @"SubmissionTime"; + case BVSortOptionCommentsTotalFeedbackCount: + return @"TotalFeedbackCount"; + case BVSortOptionCommentsTotalNegativeFeedbackCount: + return @"TotalNegativeFeedbackCount"; + case BVSortOptionCommentsTotalPositiveFeedbackCount: + return @"TotalPositiveFeedbackCount"; + case BVSortOptionCommentsUserLocation: + return @"UserLocation"; + } } @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.h b/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.h index 0ea12157..7dbe35b2 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.h +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.h @@ -5,48 +5,56 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmissionAction.h" #import "BVAnswerSubmissionResponse.h" -#import "BVConversationsRequest.h" #import "BVBaseUGCSubmission.h" +#import "BVConversationsRequest.h" +#import "BVSubmissionAction.h" +#import - -typedef void (^AnswerSubmissionCompletion)(BVAnswerSubmissionResponse* _Nonnull response); +typedef void (^AnswerSubmissionCompletion)( + BVAnswerSubmissionResponse *__nonnull response); /** Class to use to submit an answer to the Bazaarvoice platform. - - Of the many parameters possible on a BVAnswerSubmission, the ones needed for submission depend on your specific implementation. - - For a description of possible fields see our API documentation at: + + Of the many parameters possible on a BVAnswerSubmission, the ones needed for + submission depend on your specific implementation. + + For a description of possible fields see our API documentation at: https://developer.bazaarvoice.com/docs/read/conversations/answers/submit/5_4 - + @availability 4.1.0 and later */ @interface BVAnswerSubmission : BVBaseUGCSubmission /** Create a new BVAnswerSubmission. - - @param questionId The ID of the question that this answer is associated with. + + @param questionId The ID of the question that this answer is associated + with. @param answerText The answer text that the user input. */ --(nonnull instancetype)initWithQuestionId:(nonnull NSString*)questionId answerText:(nonnull NSString*)answerText; --(nonnull instancetype) __unavailable init; - +- (nonnull instancetype)initWithQuestionId:(nonnull NSString *)questionId + answerText:(nonnull NSString *)answerText; +- (nonnull instancetype)__unavailable init; /** - Submit this answer to the Bazaarvoice platform. If the `action` of this object is set to `BVSubmissionActionPreview` then the submission will NOT actually take place. - - A submission can fail for many reasons, and is dependent on your submission configuration. - - @param success The success block is called when a successful submission occurs. - @param failure The failure block is called when an unsuccessful submission occurs. This could be for a number of reasons: network failures, submission parameters invalid, or server errors occur. + Submit this answer to the Bazaarvoice platform. If the `action` of this object + is set to `BVSubmissionActionPreview` then the submission will NOT actually + take place. + + A submission can fail for many reasons, and is dependent on your submission + configuration. + + @param success The success block is called when a successful submission + occurs. + @param failure The failure block is called when an unsuccessful submission + occurs. This could be for a number of reasons: network failures, submission + parameters invalid, or server errors occur. */ --(void)submit:(nonnull AnswerSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; - +- (void)submit:(nonnull AnswerSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure; -@property (readonly) NSString* _Nonnull questionId; +@property(nonnull, readonly) NSString *questionId; @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.m b/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.m index d2f184d0..5b0729d3 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.m +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmission.m @@ -6,238 +6,280 @@ // #import "BVAnswerSubmission.h" -#import "BVUploadablePhoto.h" #import "BVAnswerSubmissionErrorResponse.h" -#import "BVSDKManager.h" #import "BVCore.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVUploadablePhoto.h" -@interface BVAnswerSubmission() +@interface BVAnswerSubmission () -@property (readwrite) NSString* _Nonnull questionId; -@property NSString* _Nonnull answerText; +@property(nonnull, readwrite) NSString *questionId; +@property(nonnull) NSString *answerText; @property bool failureCalled; @end @implementation BVAnswerSubmission --(nonnull instancetype)initWithQuestionId:(nonnull NSString*)questionId answerText:(nonnull NSString*)answerText { - self = [super init]; - if(self){ - self.questionId = questionId; - self.answerText = answerText; - } - return self; +- (nonnull instancetype)initWithQuestionId:(nonnull NSString *)questionId + answerText:(nonnull NSString *)answerText { + self = [super init]; + if (self) { + self.questionId = questionId; + self.answerText = answerText; + } + return self; } --(void)submit:(nonnull AnswerSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - if (self.action == BVSubmissionActionPreview) { - [[BVLogger sharedLogger] warning:@"Submitting a 'BVAnswerSubmission' with action set to `BVSubmissionActionPreview` will not actially submit the answer! Set to `BVSubmissionActionSubmit` for real submission."]; - [self submitPreview:success failure:failure]; +- (void)submit:(nonnull AnswerSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.action == BVSubmissionActionPreview) { + [[BVLogger sharedLogger] + warning:@"Submitting a 'BVAnswerSubmission' with action set to " + @"`BVSubmissionActionPreview` will not actially submit " + @"the answer! Set to `BVSubmissionActionSubmit` for real " + @"submission."]; + [self submitPreview:success failure:failure]; + } else { + [self submitPreview:^(BVAnswerSubmissionResponse *__nonnull response) { + [self submitForReal:success failure:failure]; } - else { - [self submitPreview:^(BVAnswerSubmissionResponse * _Nonnull response) { - [self submitForReal:success failure:failure]; - } failure:^(NSArray * _Nonnull errors) { - [self sendErrors:errors failureCallback:failure]; + failure:^(NSArray *__nonnull errors) { + [self sendErrors:errors failureCallback:failure]; }]; - } + } } --(void)submitPreview:(AnswerSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - [self submitAnswerWithPhotoUrls:BVSubmissionActionPreview +- (void)submitPreview:(AnswerSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + [self submitAnswerWithPhotoUrls:BVSubmissionActionPreview + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; +} + +- (void)submitForReal:(AnswerSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + if ([self.photos count] == 0) { + [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit photoUrls:@[] photoCaptions:@[] success:success failure:failure]; - -} + return; + } + + // upload photos before submitting Answer + NSMutableArray *photoUrls = [NSMutableArray array]; + NSMutableArray *photoCaptions = [NSMutableArray array]; + + for (BVUploadablePhoto *photo in self.photos) { + [photo uploadForContentType:BVPhotoContentTypeAnswer + success:^(NSString *__nonnull photoUrl) { + + // Queue one event for each photo uploaded. + BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.questionId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNamePhoto + withAdditionalParams:@{@"detail1" : @"Answer"}]; + [BVPixel trackEvent:photoUploadEvent]; + + [photoUrls addObject:photoUrl]; + [photoCaptions addObject:photo.photoCaption]; + + // all photos uploaded! submit answer + if ([photoUrls count] == [self.photos count]) { + [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:photoUrls + photoCaptions:photoCaptions + success:success + failure:failure]; + } + + } + failure:^(NSArray *__nonnull errors) { + + if (!self.failureCalled) { + self.failureCalled = true; // only call failure block once, if + // multiple photos failed. + [self sendErrors:errors failureCallback:failure]; + } --(void)submitForReal:(AnswerSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - if ([self.photos count] == 0) { - [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - return; - } - - // upload photos before submitting Answer - NSMutableArray* photoUrls = [NSMutableArray array]; - NSMutableArray* photoCaptions = [NSMutableArray array]; - - for (BVUploadablePhoto* photo in self.photos) { - - [photo uploadForContentType:BVPhotoContentTypeAnswer success:^(NSString * _Nonnull photoUrl) { - - // Queue one event for each photo uploaded. - BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.questionId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNamePhoto - withAdditionalParams:@{@"detail1":@"Answer"}]; - [BVPixel trackEvent:photoUploadEvent]; - - - [photoUrls addObject:photoUrl]; - [photoCaptions addObject:photo.photoCaption]; - - // all photos uploaded! submit answer - if ([photoUrls count] == [self.photos count]) { - [self submitAnswerWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:photoUrls - photoCaptions:photoCaptions - success:success - failure:failure]; - } - - } failure:^(NSArray * _Nonnull errors) { - - if (!self.failureCalled) { - self.failureCalled = true; // only call failure block once, if multiple photos failed. - [self sendErrors:errors failureCallback:failure]; - } - }]; - - } - + } } --(void)submitAnswerWithPhotoUrls:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions success:(nonnull AnswerSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - - NSDictionary* parameters = [self createSubmissionParameters:action photoUrls:photoUrls photoCaptions:photoCaptions]; - NSData* postBody = [self transformToPostBody:parameters]; - - NSString* urlString = [NSString stringWithFormat:@"%@submitanswer.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *httpError) { - - @try { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. +- (void)submitAnswerWithPhotoUrls:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions + success:(nonnull AnswerSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + NSDictionary *parameters = [self createSubmissionParameters:action + photoUrls:photoUrls + photoCaptions:photoCaptions]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@submitanswer.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - BVAnswerSubmissionErrorResponse* errorResponse = [[BVAnswerSubmissionErrorResponse alloc] initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVAnswerSubmissionErrorResponse *errorResponse = + [[BVAnswerSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Photo upload failed."}]; - [self sendError:statusError failureCallback:failure]; - } - else if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - } - else if(errorResponse){ - // api returned successfully, but has bazaarvoice-specific errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; - } - else { - // success! - - // Fire event now that we've confirmed the answer was successfully uploaded. - BVFeatureUsedEvent *answerQuestionEvent = [[BVFeatureUsedEvent alloc] initWithProductId:_questionId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameAnswerQuestion - withAdditionalParams:nil]; - - [BVPixel trackEvent:answerQuestionEvent]; - - BVAnswerSubmissionResponse* response = [[BVAnswerSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); + // network error was generated + [self sendError:httpError failureCallback:failure]; + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Photo upload failed." + }]; + [self sendError:statusError failureCallback:failure]; + } else if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + } else { + // success! + + // Fire event now that we've confirmed the answer was + // successfully uploaded. + BVFeatureUsedEvent *answerQuestionEvent = [ + [BVFeatureUsedEvent alloc] + initWithProductId:_questionId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameAnswerQuestion + withAdditionalParams:nil]; + + [BVPixel trackEvent:answerQuestionEvent]; + + BVAnswerSubmissionResponse *response = + [[BVAnswerSubmissionResponse alloc] initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); } - - } - @catch (NSException *exception) { - NSError* unknownError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; + + } @catch (NSException *exception) { + NSError *unknownError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; [self sendError:unknownError failureCallback:failure]; - } + } + + }]; - }]; - - // start uploading answer - [postDataTask resume]; - + // start uploading answer + [postDataTask resume]; } --(nonnull NSDictionary*)createSubmissionParameters:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion": @"5.4", - @"answertext": self.answerText, - @"questionid": self.questionId, - }]; - - parameters[@"passkey"] = [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"action"] = [BVSubmissionActionUtil toString:action]; - - parameters[@"campaignid"] = self.campaignId; - parameters[@"locale"] = self.locale; - - parameters[@"hostedauthentication_authenticationemail"] = self.hostedAuthenticationEmail; - parameters[@"hostedauthentication_callbackurl"] = self.hostedAuthenticationCallback; - parameters[@"fp"] = self.fingerPrint; - parameters[@"user"] = self.user; - parameters[@"usernickname"] = self.userNickname; - parameters[@"useremail"] = self.userEmail; - parameters[@"userid"] = self.userId; - parameters[@"userlocation"] = self.userLocation; - - int photoIndex = 0; - for(NSString* url in photoUrls) { - NSString* key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; - parameters[key] = url; - photoIndex += 1; - } - - int captionIndex = 0; - for(NSString* caption in photoCaptions) { - NSString* key = [NSString stringWithFormat:@"photocaption_%i", captionIndex]; - parameters[key] = caption; - captionIndex += 1; - } - - if (self.sendEmailAlertWhenPublished) { - parameters[@"sendemailalertwhenpublished"] = [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; - } - - if (self.agreedToTermsAndConditions) { - parameters[@"agreedtotermsandconditions"] = [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; - } - - for (BVStringKeyValuePair* keyValuePair in self.customFormPairs) { - NSString* key = [keyValuePair key]; - NSString* value = [keyValuePair value]; - parameters[key] = value; - } - - return parameters; - +- (nonnull NSDictionary *) +createSubmissionParameters:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"answertext" : self.answerText, + @"questionid" : self.questionId, + }]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"action"] = [BVSubmissionActionUtil toString:action]; + + parameters[@"campaignid"] = self.campaignId; + parameters[@"locale"] = self.locale; + + parameters[@"hostedauthentication_authenticationemail"] = + self.hostedAuthenticationEmail; + parameters[@"hostedauthentication_callbackurl"] = + self.hostedAuthenticationCallback; + parameters[@"fp"] = self.fingerPrint; + parameters[@"user"] = self.user; + parameters[@"usernickname"] = self.userNickname; + parameters[@"useremail"] = self.userEmail; + parameters[@"userid"] = self.userId; + parameters[@"userlocation"] = self.userLocation; + + int photoIndex = 0; + for (NSString *url in photoUrls) { + NSString *key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; + parameters[key] = url; + photoIndex += 1; + } + + int captionIndex = 0; + for (NSString *caption in photoCaptions) { + NSString *key = + [NSString stringWithFormat:@"photocaption_%i", captionIndex]; + parameters[key] = caption; + captionIndex += 1; + } + + if (self.sendEmailAlertWhenPublished) { + parameters[@"sendemailalertwhenpublished"] = + [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; + } + + if (self.agreedToTermsAndConditions) { + parameters[@"agreedtotermsandconditions"] = + [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; + } + + for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { + NSString *key = [keyValuePair key]; + NSString *value = [keyValuePair value]; + parameters[key] = value; + } + + return parameters; } @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.h b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.h index abf68c25..c8c94a8a 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionErrorResponse.h" #import "BVSubmittedAnswer.h" +#import /// Failed answer submission response. @interface BVAnswerSubmissionErrorResponse : BVSubmissionErrorResponse -@property BVSubmittedAnswer* _Nullable answer; +@property(nullable) BVSubmittedAnswer *answer; @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.m b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.m index 3d57afb5..11ed46a3 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionErrorResponse.m @@ -9,16 +9,19 @@ @implementation BVAnswerSubmissionErrorResponse --(instancetype)initWithApiResponse:(nullable id)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - NSDictionary* apiObject = apiResponse; // [super initWithApiResponse:] checks that this is nonnull and a dictionary - self.answer = [[BVSubmittedAnswer alloc] initWithApiResponse:apiObject[@"Answer"]]; - } - - return self; +- (instancetype)initWithApiResponse:(nullable id)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + + if (self) { + NSDictionary *apiObject = apiResponse; // [super initWithApiResponse:] + // checks that this is nonnull and a + // dictionary + self.answer = + [[BVSubmittedAnswer alloc] initWithApiResponse:apiObject[@"Answer"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.h b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.h index abcbd3c0..95b7e0ff 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmittedAnswer.h" #import "BVSubmissionResponse.h" +#import "BVSubmittedAnswer.h" +#import /// Successful answer submission response. @interface BVAnswerSubmissionResponse : BVSubmissionResponse -@property BVSubmittedAnswer* _Nullable answer; +@property(nullable) BVSubmittedAnswer *answer; @end diff --git a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.m b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.m index 121dae9a..01298efb 100644 --- a/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Answer/BVAnswerSubmissionResponse.m @@ -9,15 +9,16 @@ @implementation BVAnswerSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - self.answer = [[BVSubmittedAnswer alloc] initWithApiResponse:apiResponse[@"Answer"]]; - } - - return self; +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + + if (self) { + self.answer = + [[BVSubmittedAnswer alloc] initWithApiResponse:apiResponse[@"Answer"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.h b/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.h index 541cfa9b..e86edcd1 100644 --- a/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.h +++ b/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.h @@ -10,13 +10,13 @@ /// A successfully submitted answer. @interface BVSubmittedAnswer : NSObject -@property NSString* _Nullable answerText; +@property(nullable) NSString *answerText; @property bool sendEmailAlertWhenAnswered; -@property NSDate* _Nullable submissionTime; -@property NSNumber* _Nullable typicalHoursToPost; -@property NSString* _Nullable submissionId; -@property NSString* _Nullable answerId; +@property(nullable) NSDate *submissionTime; +@property(nullable) NSNumber *typicalHoursToPost; +@property(nullable) NSString *submissionId; +@property(nullable) NSString *answerId; --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.m b/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.m index 5f33dc11..fb1e6243 100644 --- a/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.m +++ b/Pod/BVConversations/Submission/Answer/BVSubmittedAnswer.m @@ -11,29 +11,31 @@ @implementation BVSubmittedAnswer --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.answerText, apiObject[@"AnswerText"]) - SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) - SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) - SET_IF_NOT_NULL(self.answerId, apiObject[@"AnswerId"]) - - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; - - NSNumber* emailAlert = apiObject[@"SendEmailAlertWhenAnswered"]; - if(emailAlert != nil) { - self.sendEmailAlertWhenAnswered = [emailAlert boolValue]; - } +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; + } + + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.answerText, apiObject[@"AnswerText"]) + SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) + SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) + SET_IF_NOT_NULL(self.answerId, apiObject[@"AnswerId"]) + + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; + + NSNumber *emailAlert = apiObject[@"SendEmailAlertWhenAnswered"]; + if (emailAlert != nil) { + self.sendEmailAlertWhenAnswered = [emailAlert boolValue]; } - return self; + } + return self; } @end diff --git a/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.h b/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.h index aba402d5..3cc02fb8 100644 --- a/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.h +++ b/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.h @@ -10,7 +10,7 @@ /// A successfully submitted user authenticated string. @interface BVSubmittedUAS : NSObject -@property (readonly) NSString *_Nullable authenticatedUser; +@property(nullable, readonly) NSString *authenticatedUser; - (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; diff --git a/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.m b/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.m index 4386e1b3..d932f0fe 100644 --- a/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.m +++ b/Pod/BVConversations/Submission/Auth/BVSubmittedUAS.m @@ -9,23 +9,23 @@ #import "BVNullHelper.h" @interface BVSubmittedUAS () -@property (strong, nonatomic, readwrite) NSString *_Nullable authenticatedUser; +@property(nullable, strong, nonatomic, readwrite) NSString *authenticatedUser; @end @implementation BVSubmittedUAS - (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - if ((self = [super init])) { - if (apiResponse == nil || - ![apiResponse isKindOfClass:[NSDictionary class]]) { - return nil; - } + if ((self = [super init])) { + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; + } - NSDictionary *apiObject = apiResponse; + NSDictionary *apiObject = apiResponse; - SET_IF_NOT_NULL(self.authenticatedUser, apiObject[@"User"]) - } - return self; + SET_IF_NOT_NULL(self.authenticatedUser, apiObject[@"User"]) + } + return self; } @end diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmission.h b/Pod/BVConversations/Submission/Auth/BVUASSubmission.h index 06c73b5d..22d32ab1 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmission.h +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmission.h @@ -5,17 +5,18 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import "BVConversationsRequest.h" #import "BVBaseUGCSubmission.h" +#import "BVConversationsRequest.h" +#import @class BVUASSubmissionResponse; typedef void (^UASSubmissionCompletion)( - BVUASSubmissionResponse *_Nonnull response); + BVUASSubmissionResponse *__nonnull response); /** - Class to use to submit an user authentication string request with the bv_authtoken to the Bazaarvoice managed authentication platform. + Class to use to submit an user authentication string request with the + bv_authtoken to the Bazaarvoice managed authentication platform. For a description of the authentication process you can view the docs: https://developer.bazaarvoice.com/conversations-api/tutorials/submission/authentication/bv-mastered#step-4:-user-authentication @@ -30,8 +31,7 @@ typedef void (^UASSubmissionCompletion)( @param bvAuthToken The bvAuthToken representing the hosted authenticating user. */ -- (nonnull instancetype)initWithBvAuthToken: - (nonnull NSString *)bvAuthToken; +- (nonnull instancetype)initWithBvAuthToken:(nonnull NSString *)bvAuthToken; - (nonnull instancetype)__unavailable init; /** @@ -51,7 +51,7 @@ typedef void (^UASSubmissionCompletion)( - (void)submit:(nonnull UASSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; -@property (readonly) NSString *_Nonnull bvAuthToken; +@property(nonnull, readonly) NSString *bvAuthToken; @property BVSubmissionAction action UNAVAILABLE_ATTRIBUTE; @end diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmission.m b/Pod/BVConversations/Submission/Auth/BVUASSubmission.m index ab557f8b..bdec74a8 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmission.m +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmission.m @@ -6,15 +6,15 @@ // #import "BVUASSubmission.h" -#import "BVSDKManager.h" -#import "BVDiagnosticHelpers.h" #import "BVCore.h" +#import "BVDiagnosticHelpers.h" #import "BVSDKConfiguration.h" -#import "BVUASSubmissionResponse.h" +#import "BVSDKManager.h" #import "BVUASSubmissionErrorResponse.h" +#import "BVUASSubmissionResponse.h" @interface BVUASSubmission () -@property (strong, nonatomic, readwrite) NSString *_Nonnull bvAuthToken; +@property(nonnull, strong, nonatomic, readwrite) NSString *bvAuthToken; @end @implementation BVUASSubmission @@ -22,131 +22,128 @@ @implementation BVUASSubmission @dynamic action; - (nonnull instancetype)initWithBvAuthToken:(nonnull NSString *)bvAuthToken { - if ((self = [super init])) { - self.bvAuthToken = bvAuthToken ?: @""; - } - return self; + if ((self = [super init])) { + self.bvAuthToken = bvAuthToken ?: @""; + } + return self; } - (void)submit:(nonnull UASSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - NSDictionary *parameters = [self createUASSubmissionParameters]; - NSData *postBody = [self transformToPostBody:parameters]; - - NSString *urlString = - [NSString stringWithFormat:@"%@authenticateuser.json", - [BVConversationsRequest commonEndpoint]]; - NSURL *url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest *request = - [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", - urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session - dataTaskWithRequest:request - completionHandler:^(NSData *data, NSURLResponse *response, - NSError *httpError) { - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) - response; // This dataTask is used with only HTTP requests. - NSInteger statusCode = httpResponse.statusCode; - NSError *jsonParsingError = nil; - NSDictionary *json = nil; - - @try { - json = - [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&jsonParsingError]; - - } @catch (NSException *exception) { - NSError *unknownError = [NSError - errorWithDomain:BVErrDomain - code:BV_ERROR_UNKNOWN - userInfo:@{ - NSLocalizedDescriptionKey : - @"An unknown parsing error occurred." - }]; - - [self sendError:unknownError failureCallback:failure]; - return; - } - - [[BVLogger sharedLogger] - verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, - (long)statusCode]]; - - if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - return; - } - - if (statusCode >= 300) { - // HTTP status code indicates failure - NSError *statusError = - [NSError errorWithDomain:BVErrDomain - code:BV_ERROR_NETWORK_FAILED - userInfo:@{ - NSLocalizedDescriptionKey : - @"Photo upload failed." - }]; - [self sendError:statusError failureCallback:failure]; - return; - } - - if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - return; - } - - BVUASSubmissionErrorResponse *errorResponse = - [[BVUASSubmissionErrorResponse alloc] - initWithApiResponse:json]; // fails gracefully - - if (errorResponse) { - // api returned successfully, but has bazaarvoice-specific - // errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] - failureCallback:failure]; - return; - } - - // success! - BVUASSubmissionResponse *submissionResponse = - [[BVUASSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(submissionResponse); - }); - - }]; - - // start submission for UAS - [postDataTask resume]; -} + NSDictionary *parameters = [self createUASSubmissionParameters]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@authenticateuser.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. + NSInteger statusCode = httpResponse.statusCode; + NSError *jsonParsingError = nil; + NSDictionary *json = nil; + + @try { + json = [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + + } @catch (NSException *exception) { + NSError *unknownError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + + [self sendError:unknownError failureCallback:failure]; + return; + } + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + + if (httpError) { + // network error was generated + [self sendError:httpError failureCallback:failure]; + return; + } + + if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = [NSError + errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : @"Photo upload failed." + }]; + [self sendError:statusError failureCallback:failure]; + return; + } + + if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + return; + } + + BVUASSubmissionErrorResponse *errorResponse = + [[BVUASSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + return; + } + + // success! + BVUASSubmissionResponse *submissionResponse = + [[BVUASSubmissionResponse alloc] initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(submissionResponse); + }); -- (nonnull NSDictionary *)createUASSubmissionParameters { - NSMutableDictionary *parameters = - [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion" : @"5.4", - @"authtoken" : self.bvAuthToken }]; - parameters[@"passkey"] = - [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"_appId"] = [NSBundle mainBundle].bundleIdentifier; - parameters[@"_appVersion"] = [BVDiagnosticHelpers releaseVersionNumber]; - parameters[@"_buildNumber"] = [BVDiagnosticHelpers buildVersionNumber]; - parameters[@"_bvIosSdkVersion"] = BV_SDK_VERSION; + // start submission for UAS + [postDataTask resume]; +} - return parameters; +- (nonnull NSDictionary *)createUASSubmissionParameters { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"authtoken" : self.bvAuthToken + }]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"_appId"] = [NSBundle mainBundle].bundleIdentifier; + parameters[@"_appVersion"] = [BVDiagnosticHelpers releaseVersionNumber]; + parameters[@"_buildNumber"] = [BVDiagnosticHelpers buildVersionNumber]; + parameters[@"_bvIosSdkVersion"] = BV_SDK_VERSION; + + return parameters; } @end diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.h b/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.h index ec3599ae..ea05d67f 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.h @@ -5,8 +5,8 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionErrorResponse.h" +#import /// Failed UAS submission response @interface BVUASSubmissionErrorResponse : BVSubmissionErrorResponse diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.m b/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.m index 1f84c5d6..172a248a 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmissionErrorResponse.m @@ -10,10 +10,10 @@ @implementation BVUASSubmissionErrorResponse - (instancetype)initWithApiResponse:(nullable id)apiResponse { - if ((self = [super initWithApiResponse:apiResponse])) { - /// Nothing to be done here as of now. - } - return self; + if ((self = [super initWithApiResponse:apiResponse])) { + /// Nothing to be done here as of now. + } + return self; } @end diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.h b/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.h index 4e3db212..44f994e3 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.h @@ -5,12 +5,12 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionResponse.h" +#import @class BVSubmittedUAS; @interface BVUASSubmissionResponse : BVSubmissionResponse -@property (readonly) BVSubmittedUAS *_Nullable userAuthenticationString; +@property(nullable, readonly) BVSubmittedUAS *userAuthenticationString; @end diff --git a/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.m b/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.m index d9d09fab..55636f03 100644 --- a/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Auth/BVUASSubmissionResponse.m @@ -9,23 +9,23 @@ #import "BVSubmittedUAS.h" @interface BVUASSubmissionResponse () -@property (strong, nonatomic, readwrite) - BVSubmittedUAS *_Nullable userAuthenticationString; +@property(nullable, strong, nonatomic, readwrite) + BVSubmittedUAS *userAuthenticationString; @end @implementation BVUASSubmissionResponse - (nullable BVSubmittedUAS *)userAuthenticationString { - return self.userAuthenticationString; + return self.userAuthenticationString; } - (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { - if ((self = [super initWithApiResponse:apiResponse])) { - self.userAuthenticationString = [[BVSubmittedUAS alloc] - initWithApiResponse:apiResponse[@"Authentication"]]; - } + if ((self = [super initWithApiResponse:apiResponse])) { + self.userAuthenticationString = [[BVSubmittedUAS alloc] + initWithApiResponse:apiResponse[@"Authentication"]]; + } - return self; + return self; } @end diff --git a/Pod/BVConversations/Submission/BVBaseUGCSubmission.h b/Pod/BVConversations/Submission/BVBaseUGCSubmission.h index d6e15851..115eb054 100644 --- a/Pod/BVConversations/Submission/BVBaseUGCSubmission.h +++ b/Pod/BVConversations/Submission/BVBaseUGCSubmission.h @@ -10,73 +10,104 @@ #import "BVUploadablePhoto.h" #import "BVUploadableYouTubeVideo.h" -/// Base class used for defining common properties for a UGC content types of the Conversations API: Reviews, Review Comments, Questions and Answers. +/// Base class used for defining common properties for a UGC content types of +/// the Conversations API: Reviews, Review Comments, Questions and Answers. @interface BVBaseUGCSubmission : BVSubmission -/// Set whether or not to to perform a preview on the submission or submit for real. Default is BVSubmissionActionPreview. A preview is like a dry run, but validation of the fields will occur through the API call over the network but no data will be submitted. +/// Set whether or not to to perform a preview on the submission or submit for +/// real. Default is BVSubmissionActionPreview. A preview is like a dry run, but +/// validation of the fields will occur through the API call over the network +/// but no data will be submitted. @property BVSubmissionAction action; -/// Value of the encrypted user. This parameter demonstrates that a user has been authenticated. Note that the UserId parameter does not contain authentication information and should not be used for hosted authentication. See the Authenticate User method for more information. -@property NSString* _Nullable user; +/// Value of the encrypted user. This parameter demonstrates that a user has +/// been authenticated. Note that the UserId parameter does not contain +/// authentication information and should not be used for hosted authentication. +/// See the Authenticate User method for more information. +@property(nullable) NSString *user; /// User's email address -@property NSString* _Nullable userEmail; +@property(nullable) NSString *userEmail; /// User's external ID. Do not use email addresses for this value. -@property NSString* _Nullable userId; +@property(nullable) NSString *userId; /// User location text -@property NSString* _Nullable userLocation; +@property(nullable) NSString *userLocation; /// User nickname display text -@property NSString* _Nullable userNickname; - -/// Boolean indicating whether or not the user agreed to the terms and conditions. Required depending on the client's settings. -@property NSNumber* _Nullable agreedToTermsAndConditions; - -// Boolean indicating whether or not the user wants to be notified when a comment is posted on the content. -@property NSNumber* _Nullable sendEmailAlertWhenCommented; - -/// Boolean indicating whether or not the user wants to be notified when his/her content is published. -@property NSNumber* _Nullable sendEmailAlertWhenPublished; - -/// Locale to display Labels, Configuration, Product Attributes and Category Attributes in. The default value is the locale defined in the display associated with the API key. -@property NSString* _Nullable locale; - -/// Arbitrary text that may be saved alongside content to indicate vehicle by which content was captured, e.g. “post-purchase email”. -@property NSString* _Nullable campaignId; - -/// Email address where the submitter will receive the confirmation email. If you are configured to use hosted email authentication, this parameter is required. See the Authenticate User method for more information on hosted authentication. -@property NSString* _Nullable hostedAuthenticationEmail; - -/// URL of the link contained in the user authentication email. This should point to a landing page where a web application exists to complete the user authentication process. The host for the URL must be one of the domains configured for the client. The link in the email will contain a user authentication token (authtoken) that is used to verify the submitter. If you are configured to use hosted email authentication, this parameter is required. See the hosted authentication tutorial for more information. -@property NSString* _Nullable hostedAuthenticationCallback; +@property(nullable) NSString *userNickname; + +/// Boolean indicating whether or not the user agreed to the terms and +/// conditions. Required depending on the client's settings. +@property(nullable) NSNumber *agreedToTermsAndConditions; + +// Boolean indicating whether or not the user wants to be notified when a +// comment is posted on the content. +@property(nullable) NSNumber *sendEmailAlertWhenCommented; + +/// Boolean indicating whether or not the user wants to be notified when his/her +/// content is published. +@property(nullable) NSNumber *sendEmailAlertWhenPublished; + +/// Locale to display Labels, Configuration, Product Attributes and Category +/// Attributes in. The default value is the locale defined in the display +/// associated with the API key. +@property(nullable) NSString *locale; + +/// Arbitrary text that may be saved alongside content to indicate vehicle by +/// which content was captured, e.g. “post-purchase email”. +@property(nullable) NSString *campaignId; + +/// Email address where the submitter will receive the confirmation email. If +/// you are configured to use hosted email authentication, this parameter is +/// required. See the Authenticate User method for more information on hosted +/// authentication. +@property(nullable) NSString *hostedAuthenticationEmail; + +/// URL of the link contained in the user authentication email. This should +/// point to a landing page where a web application exists to complete the user +/// authentication process. The host for the URL must be one of the domains +/// configured for the client. The link in the email will contain a user +/// authentication token (authtoken) that is used to verify the submitter. If +/// you are configured to use hosted email authentication, this parameter is +/// required. See the hosted authentication tutorial for more information. +@property(nullable) NSString *hostedAuthenticationCallback; /** - Fingerprint of content author's device. See the Authenticity Tutorial for more information. - - Per the Bazaarvoice Authenticity Policy, you must send a device fingerprint attached to each submission. If you fail to send a device fingerprint with your submission, Bazaarvoice may take any action deemed necessary in Bazaarvoice’s sole discretion to protect the integrity of the network. Such actions may include but are not limited to: rejection of your content, halting syndication of your content on the Bazaarvoice network, revocation of your API key, or revocation of your API license. + Fingerprint of content author's device. See the Authenticity Tutorial for more + information. + + Per the Bazaarvoice Authenticity Policy, you must send a device fingerprint + attached to each submission. If you fail to send a device fingerprint with your + submission, Bazaarvoice may take any action deemed necessary in Bazaarvoice’s + sole discretion to protect the integrity of the network. Such actions may + include but are not limited to: rejection of your content, halting syndication + of your content on the Bazaarvoice network, revocation of your API key, or + revocation of your API license. */ -@property NSString* _Nullable fingerPrint; +@property(nullable) NSString *fingerPrint; // An array of BVUploadablePhoto objects to attach to a review submission. -@property NSMutableArray* _Nonnull photos; +@property(nullable) NSMutableArray *photos; // An array of custom Key-Value pairs -@property NSMutableArray* _Nonnull customFormPairs; +@property(nullable) NSMutableArray *customFormPairs; /** Submit a user-provided photo attached to this answer. - + @param image The user-provded image attached to this answer. @param photoCaption The user-provided caption for the photo. */ --(void)addPhoto:(nonnull UIImage*)image withPhotoCaption:(nullable NSString*)photoCaption; +- (void)addPhoto:(nonnull UIImage *)image + withPhotoCaption:(nullable NSString *)photoCaption; /** This method adds extra user provided form parameters to a submission request, and will be urlencoded. */ --(void)addCustomSubmissionParameter:(nonnull NSString*)parameter withValue:(nonnull NSString*)value; +- (void)addCustomSubmissionParameter:(nonnull NSString *)parameter + withValue:(nonnull NSString *)value; @end diff --git a/Pod/BVConversations/Submission/BVBaseUGCSubmission.m b/Pod/BVConversations/Submission/BVBaseUGCSubmission.m index 12146a6a..fe028cb1 100644 --- a/Pod/BVConversations/Submission/BVBaseUGCSubmission.m +++ b/Pod/BVConversations/Submission/BVBaseUGCSubmission.m @@ -12,25 +12,28 @@ @implementation BVBaseUGCSubmission - (nonnull instancetype)init { - - self = [super init]; - if (self){ - self.photos = [NSMutableArray array]; - self.customFormPairs = [NSMutableArray array]; - } - - return self; - + + self = [super init]; + if (self) { + self.photos = [NSMutableArray array]; + self.customFormPairs = [NSMutableArray array]; + } + + return self; } --(void)addPhoto:(nonnull UIImage*)image withPhotoCaption:(nullable NSString*)photoCaption { - BVUploadablePhoto* photo = [[BVUploadablePhoto alloc] initWithPhoto:image photoCaption:photoCaption]; - [self.photos addObject:photo]; +- (void)addPhoto:(nonnull UIImage *)image + withPhotoCaption:(nullable NSString *)photoCaption { + BVUploadablePhoto *photo = + [[BVUploadablePhoto alloc] initWithPhoto:image photoCaption:photoCaption]; + [self.photos addObject:photo]; } --(void)addCustomSubmissionParameter:(nonnull NSString*)parameter withValue:(nonnull NSString *)value { - BVStringKeyValuePair* customFormPair = [BVStringKeyValuePair pairWithKey:parameter value:value]; - [self.customFormPairs addObject:customFormPair]; +- (void)addCustomSubmissionParameter:(nonnull NSString *)parameter + withValue:(nonnull NSString *)value { + BVStringKeyValuePair *customFormPair = + [BVStringKeyValuePair pairWithKey:parameter value:value]; + [self.customFormPairs addObject:customFormPair]; } @end diff --git a/Pod/BVConversations/Submission/BVFieldError.h b/Pod/BVConversations/Submission/BVFieldError.h index 04085995..88ce3422 100644 --- a/Pod/BVConversations/Submission/BVFieldError.h +++ b/Pod/BVConversations/Submission/BVFieldError.h @@ -1,3 +1,5 @@ + + // // BVFieldError.h // Conversations @@ -5,22 +7,24 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionErrorCode.h" +#import -extern NSString* _Nonnull const BVFieldErrorName; -extern NSString* _Nonnull const BVFieldErrorMessage; -extern NSString* _Nonnull const BVFieldErrorCode; +extern NSString *__nonnull const BVFieldErrorName; +extern NSString *__nonnull const BVFieldErrorMessage; +extern NSString *__nonnull const BVFieldErrorCode; // Internal class - used only within BVSDK @interface BVFieldError : NSObject -@property NSString* _Nonnull fieldName; -@property NSString* _Nonnull message; -@property NSString* _Nonnull code; +@property(nonnull) NSString *fieldName; +@property(nonnull) NSString *message; +@property(nonnull) NSString *code; --(nullable instancetype)initWithApiResponse:(nonnull NSDictionary*)apiResponse; --(nonnull NSError*)toNSError; -+(nonnull NSArray*)createListFromFormErrorsDictionary:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse: + (nonnull NSDictionary *)apiResponse; +- (nonnull NSError *)toNSError; ++ (nonnull NSArray *)createListFromFormErrorsDictionary: + (nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Submission/BVFieldError.m b/Pod/BVConversations/Submission/BVFieldError.m index bb3cb680..52baa872 100644 --- a/Pod/BVConversations/Submission/BVFieldError.m +++ b/Pod/BVConversations/Submission/BVFieldError.m @@ -6,57 +6,62 @@ // #import "BVFieldError.h" -#import "BVNullHelper.h" #import "BVCore.h" +#import "BVNullHelper.h" #import "BVSubmissionErrorCode.h" -NSString* _Nonnull const BVFieldErrorName = @"BVFieldErrorName"; -NSString* _Nonnull const BVFieldErrorMessage = @"BVFieldErrorMessage"; -NSString* _Nonnull const BVFieldErrorCode = @"BVFieldErrorCode"; +NSString *__nonnull const BVFieldErrorName = @"BVFieldErrorName"; +NSString *__nonnull const BVFieldErrorMessage = @"BVFieldErrorMessage"; +NSString *__nonnull const BVFieldErrorCode = @"BVFieldErrorCode"; @implementation BVFieldError --(nullable instancetype)initWithApiResponse:(nonnull NSDictionary*)apiResponse { - self = [super init]; - if (self) { - SET_IF_NOT_NULL(self.fieldName, apiResponse[@"Field"]) - SET_IF_NOT_NULL(self.message, apiResponse[@"Message"]) - SET_IF_NOT_NULL(self.code, apiResponse[@"Code"]) - } - return self; +- (nullable instancetype)initWithApiResponse: + (nonnull NSDictionary *)apiResponse { + self = [super init]; + if (self) { + SET_IF_NOT_NULL(self.fieldName, apiResponse[@"Field"]) + SET_IF_NOT_NULL(self.message, apiResponse[@"Message"]) + SET_IF_NOT_NULL(self.code, apiResponse[@"Code"]) + } + return self; } - --(nonnull NSError*)toNSError { - return [NSError errorWithDomain:BVErrDomain code:BV_ERROR_FIELD_INVALID userInfo:@{ - NSLocalizedDescriptionKey: [NSString stringWithFormat:@"%@: %@: %@", self.fieldName, self.code, self.message], - BVFieldErrorName: self.fieldName, - BVFieldErrorMessage: self.message, - BVFieldErrorCode: self.code - }]; +- (nonnull NSError *)toNSError { + return [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_FIELD_INVALID + userInfo:@{ + NSLocalizedDescriptionKey : [NSString + stringWithFormat:@"%@: %@: %@", self.fieldName, + self.code, self.message], + BVFieldErrorName : self.fieldName, + BVFieldErrorMessage : self.message, + BVFieldErrorCode : self.code + }]; } -+(nonnull NSArray*)createListFromFormErrorsDictionary:(nullable id)apiResponse { - - if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { - return @[]; - } - - NSDictionary* apiObject = apiResponse; - - NSMutableArray* results = [NSMutableArray array]; - NSDictionary* rawFieldErrors = apiObject[@"FieldErrors"]; - - for (NSString* key in rawFieldErrors){ - NSDictionary* rawFieldError = rawFieldErrors[key]; - - BVFieldError* fieldError = [[BVFieldError alloc] initWithApiResponse:rawFieldError]; - if (fieldError) { - [results addObject:fieldError]; - } ++ (nonnull NSArray *)createListFromFormErrorsDictionary: + (nullable id)apiResponse { + if (apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { + return @[]; + } + + NSDictionary *apiObject = apiResponse; + + NSMutableArray *results = [NSMutableArray array]; + NSDictionary *rawFieldErrors = apiObject[@"FieldErrors"]; + + for (NSString *key in rawFieldErrors) { + NSDictionary *rawFieldError = rawFieldErrors[key]; + + BVFieldError *fieldError = + [[BVFieldError alloc] initWithApiResponse:rawFieldError]; + if (fieldError) { + [results addObject:fieldError]; } - - return results; + } + + return results; } @end diff --git a/Pod/BVConversations/Submission/BVFormField.h b/Pod/BVConversations/Submission/BVFormField.h index b1af4cb0..e5a469bf 100644 --- a/Pod/BVConversations/Submission/BVFormField.h +++ b/Pod/BVConversations/Submission/BVFormField.h @@ -6,30 +6,34 @@ // // -#import #import "BVFormFieldOptions.h" #import "BVFormInputType.h" +#import /** This model is a single element of a Conversations Submission Form. - The fields elements are defined in the Conversations API Submission documentation: https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element - - The BVFormFields can be obtained for a Review/Question/Answer submission by running a Submission request in .Preview mode. + The fields elements are defined in the Conversations API Submission + documentation: + https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element + + The BVFormFields can be obtained for a Review/Question/Answer submission by + running a Submission request in .Preview mode. */ @interface BVFormField : NSObject -@property NSArray * _Nonnull options; +@property(nonnull) NSArray *options; @property BVFormInputType bvFormInputType; -@property NSString * _Nonnull identifier; -@property NSString * _Nonnull label; -@property NSString * _Nonnull type; -@property NSString * _Nonnull value; -@property NSNumber * _Nonnull required; // Boolean -@property NSNumber * _Nonnull minLength; // Integer -@property NSNumber * _Nonnull maxLength; // Integer -@property NSNumber * _Nonnull isDefault; // Boolean +@property(nonnull) NSString *identifier; +@property(nonnull) NSString *label; +@property(nonnull) NSString *type; +@property(nonnull) NSString *value; +@property(nonnull) NSNumber *required; // Boolean +@property(nonnull) NSNumber *minLength; // Integer +@property(nonnull) NSNumber *maxLength; // Integer +@property(nonnull) NSNumber *isDefault; // Boolean -- (id _Nonnull)initWithFormFieldDictionary:(NSDictionary * _Nonnull)fieldDictionary; +- (nonnull id)initWithFormFieldDictionary: + (nonnull NSDictionary *)fieldDictionary; @end diff --git a/Pod/BVConversations/Submission/BVFormField.m b/Pod/BVConversations/Submission/BVFormField.m index cf3b6880..4bbda4df 100644 --- a/Pod/BVConversations/Submission/BVFormField.m +++ b/Pod/BVConversations/Submission/BVFormField.m @@ -7,61 +7,68 @@ // #import "BVFormField.h" -#import "BVNullHelper.h" #import "BVFormFieldOptions.h" +#import "BVNullHelper.h" @implementation BVFormField -- (id)initWithFormFieldDictionary:(NSDictionary *)fieldDictionary{ - - self = [super init]; - - if (self){ - - _identifier = @""; - _label = @""; - _type = @""; - _value = @""; - _required = [NSNumber numberWithBool:NO]; - _isDefault = [NSNumber numberWithBool:NO]; - _minLength = [NSNumber numberWithInteger:0]; - _maxLength = [NSNumber numberWithInteger:0]; - - SET_IF_NOT_NULL(_identifier, [fieldDictionary objectForKey:@"Id"]); - SET_IF_NOT_NULL(_label, [fieldDictionary objectForKey:@"Label"]); - SET_IF_NOT_NULL(_type, [fieldDictionary objectForKey:@"Type"]); - SET_IF_NOT_NULL(_value, [fieldDictionary objectForKey:@"Value"]); - - _bvFormInputType = [BVFormInputTypeUtil fromString:_type]; - - if (!isObjectNilOrNull([fieldDictionary objectForKey:@"MinLength"])){ - _minLength = [NSNumber numberWithInteger:[[fieldDictionary objectForKey:@"MinLength"] integerValue]]; - } - - if (!isObjectNilOrNull([fieldDictionary objectForKey:@"MaxLength"])){ - _maxLength = [NSNumber numberWithInteger:[[fieldDictionary objectForKey:@"MaxLength"] integerValue]]; - } - - if (!isObjectNilOrNull([fieldDictionary objectForKey:@"Required"])){ - _required = [NSNumber numberWithInteger:[[fieldDictionary objectForKey:@"Required"] boolValue]]; - } - - if (!isObjectNilOrNull([fieldDictionary objectForKey:@"Default"])){ - _isDefault = [NSNumber numberWithInteger:[[fieldDictionary objectForKey:@"Default"] boolValue]]; - } - - NSMutableArray *tmpOptions = [NSMutableArray array]; - for (NSDictionary *optionsDict in [fieldDictionary objectForKey:@"Options"]){ - BVFormFieldOptions *fieldOptions = [[BVFormFieldOptions alloc] initWithOptionsDictionary:optionsDict]; - [tmpOptions addObject:fieldOptions]; - } - - _options = [NSArray arrayWithArray:tmpOptions]; - +- (id)initWithFormFieldDictionary:(NSDictionary *)fieldDictionary { + + self = [super init]; + + if (self) { + + _identifier = @""; + _label = @""; + _type = @""; + _value = @""; + _required = [NSNumber numberWithBool:NO]; + _isDefault = [NSNumber numberWithBool:NO]; + _minLength = [NSNumber numberWithInteger:0]; + _maxLength = [NSNumber numberWithInteger:0]; + + SET_IF_NOT_NULL(_identifier, [fieldDictionary objectForKey:@"Id"]); + SET_IF_NOT_NULL(_label, [fieldDictionary objectForKey:@"Label"]); + SET_IF_NOT_NULL(_type, [fieldDictionary objectForKey:@"Type"]); + SET_IF_NOT_NULL(_value, [fieldDictionary objectForKey:@"Value"]); + + _bvFormInputType = [BVFormInputTypeUtil fromString:_type]; + + if (!isObjectNilOrNull([fieldDictionary objectForKey:@"MinLength"])) { + _minLength = [NSNumber + numberWithInteger:[[fieldDictionary objectForKey:@"MinLength"] + integerValue]]; } - return self; - -} + if (!isObjectNilOrNull([fieldDictionary objectForKey:@"MaxLength"])) { + _maxLength = [NSNumber + numberWithInteger:[[fieldDictionary objectForKey:@"MaxLength"] + integerValue]]; + } + + if (!isObjectNilOrNull([fieldDictionary objectForKey:@"Required"])) { + _required = + [NSNumber numberWithInteger:[[fieldDictionary + objectForKey:@"Required"] boolValue]]; + } + + if (!isObjectNilOrNull([fieldDictionary objectForKey:@"Default"])) { + _isDefault = + [NSNumber numberWithInteger:[[fieldDictionary objectForKey:@"Default"] + boolValue]]; + } + + NSMutableArray *tmpOptions = [NSMutableArray array]; + for (NSDictionary *optionsDict in + [fieldDictionary objectForKey:@"Options"]) { + BVFormFieldOptions *fieldOptions = + [[BVFormFieldOptions alloc] initWithOptionsDictionary:optionsDict]; + [tmpOptions addObject:fieldOptions]; + } + + _options = [NSArray arrayWithArray:tmpOptions]; + } + return self; +} @end diff --git a/Pod/BVConversations/Submission/BVFormFieldOptions.h b/Pod/BVConversations/Submission/BVFormFieldOptions.h index 4dc06955..97e12bbc 100644 --- a/Pod/BVConversations/Submission/BVFormFieldOptions.h +++ b/Pod/BVConversations/Submission/BVFormFieldOptions.h @@ -9,17 +9,22 @@ #import /** - The BVFormFieldOptions are values that have been configured for a parent BVFormField element. An example when to use the Options array, is in forms that contain a UIPicker control. Programmatically a developer would iterate over the array extracting out the values and label to construct a UIPikcer. - - For more information, please see the Options section of Bazaarvioce Submission Form documentation: https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element - + The BVFormFieldOptions are values that have been configured for a parent + BVFormField element. An example when to use the Options array, is in forms that + contain a UIPicker control. Programmatically a developer would iterate over the + array extracting out the values and label to construct a UIPikcer. + + For more information, please see the Options section of Bazaarvioce Submission + Form documentation: + https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element + */ @interface BVFormFieldOptions : NSObject -@property NSString* _Nonnull label; -@property NSString* _Nonnull value; -@property NSNumber* _Nonnull selected; // Boolean +@property(nonnull) NSString *label; +@property(nonnull) NSString *value; +@property(nonnull) NSNumber *selected; // Boolean -- (id _Nonnull)initWithOptionsDictionary:(NSDictionary * _Nonnull)dictionary; +- (nonnull id)initWithOptionsDictionary:(nonnull NSDictionary *)dictionary; @end diff --git a/Pod/BVConversations/Submission/BVFormFieldOptions.m b/Pod/BVConversations/Submission/BVFormFieldOptions.m index 3ab4cf5a..411dd2aa 100644 --- a/Pod/BVConversations/Submission/BVFormFieldOptions.m +++ b/Pod/BVConversations/Submission/BVFormFieldOptions.m @@ -11,29 +11,23 @@ @implementation BVFormFieldOptions +- (id)initWithOptionsDictionary:(NSDictionary *)dictionary { -- (id)initWithOptionsDictionary:(NSDictionary *)dictionary{ - - if (self){ - - _label = @""; - _value = @""; - _selected = [NSNumber numberWithBool:NO]; - - SET_IF_NOT_NULL(_label, [dictionary objectForKey:@"Label"]) - SET_IF_NOT_NULL(_value, [dictionary objectForKey:@"Value"]) - - if ([dictionary objectForKey:@"Selected"] != nil){ - _selected = [NSNumber numberWithBool:[[dictionary objectForKey:@"Selected"] boolValue]]; - } - - } - return self; - -} - + if (self) { + _label = @""; + _value = @""; + _selected = [NSNumber numberWithBool:NO]; + SET_IF_NOT_NULL(_label, [dictionary objectForKey:@"Label"]) + SET_IF_NOT_NULL(_value, [dictionary objectForKey:@"Value"]) + if ([dictionary objectForKey:@"Selected"] != nil) { + _selected = [NSNumber + numberWithBool:[[dictionary objectForKey:@"Selected"] boolValue]]; + } + } + return self; +} @end diff --git a/Pod/BVConversations/Submission/BVFormInputType.h b/Pod/BVConversations/Submission/BVFormInputType.h index cd16abe9..27e1a518 100644 --- a/Pod/BVConversations/Submission/BVFormInputType.h +++ b/Pod/BVConversations/Submission/BVFormInputType.h @@ -11,17 +11,17 @@ Type of form input type. */ typedef NS_ENUM(NSInteger, BVFormInputType) { - BVFormInputTypeBooleanInput, - BVFormInputTypeFileInput, - BVFormInputTypeIntegerInput, - BVFormInputTypeSelectInput, - BVFormInputTypeTextAreaInput, - BVFormInputTypeTextInput, - BVFormInputTypeUnknown + BVFormInputTypeBooleanInput, + BVFormInputTypeFileInput, + BVFormInputTypeIntegerInput, + BVFormInputTypeSelectInput, + BVFormInputTypeTextAreaInput, + BVFormInputTypeTextInput, + BVFormInputTypeUnknown }; @interface BVFormInputTypeUtil : NSObject -+ (BVFormInputType)fromString:(NSString* _Nullable)str; ++ (BVFormInputType)fromString:(nullable NSString *)str; @end diff --git a/Pod/BVConversations/Submission/BVFormInputType.m b/Pod/BVConversations/Submission/BVFormInputType.m index 5467cf78..1fa7bcf8 100644 --- a/Pod/BVConversations/Submission/BVFormInputType.m +++ b/Pod/BVConversations/Submission/BVFormInputType.m @@ -9,28 +9,26 @@ @implementation BVFormInputTypeUtil -+ (BVFormInputType)fromString:(NSString* _Nullable)str{ - - if([str isEqualToString:@"BooleanInput"]) { - return BVFormInputTypeBooleanInput; - } - if([str isEqualToString:@"FileInput"]) { - return BVFormInputTypeFileInput; - } - if([str isEqualToString:@"IntegerInput"]) { - return BVFormInputTypeIntegerInput; - } - if([str isEqualToString:@"SelectInput"]) { - return BVFormInputTypeSelectInput; - } - if([str isEqualToString:@"TextAreaInput"]) { - return BVFormInputTypeTextAreaInput; - } - if([str isEqualToString:@"TextInput"]) { - return BVFormInputTypeTextInput; - } - return BVFormInputTypeUnknown; - ++ (BVFormInputType)fromString:(nullable NSString *)str { + if ([str isEqualToString:@"BooleanInput"]) { + return BVFormInputTypeBooleanInput; + } + if ([str isEqualToString:@"FileInput"]) { + return BVFormInputTypeFileInput; + } + if ([str isEqualToString:@"IntegerInput"]) { + return BVFormInputTypeIntegerInput; + } + if ([str isEqualToString:@"SelectInput"]) { + return BVFormInputTypeSelectInput; + } + if ([str isEqualToString:@"TextAreaInput"]) { + return BVFormInputTypeTextAreaInput; + } + if ([str isEqualToString:@"TextInput"]) { + return BVFormInputTypeTextInput; + } + return BVFormInputTypeUnknown; } @end diff --git a/Pod/BVConversations/Submission/BVSubmission.h b/Pod/BVConversations/Submission/BVSubmission.h index be37c0c9..9b15d4e6 100644 --- a/Pod/BVConversations/Submission/BVSubmission.h +++ b/Pod/BVConversations/Submission/BVSubmission.h @@ -5,13 +5,15 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVConversationsRequest.h" +#import @interface BVSubmission : NSObject --(void)sendError:(nonnull NSError*)error failureCallback:(nonnull ConversationsFailureHandler)failure; --(void)sendErrors:(nonnull NSArray*)errors failureCallback:(nonnull ConversationsFailureHandler)failure; --(nonnull NSData *)transformToPostBody:(nonnull NSDictionary *)parameters; +- (void)sendError:(nonnull NSError *)error + failureCallback:(nonnull ConversationsFailureHandler)failure; +- (void)sendErrors:(nonnull NSArray *)errors + failureCallback:(nonnull ConversationsFailureHandler)failure; +- (nonnull NSData *)transformToPostBody:(nonnull NSDictionary *)parameters; @end diff --git a/Pod/BVConversations/Submission/BVSubmission.m b/Pod/BVConversations/Submission/BVSubmission.m index 7e9b6f8b..224227cd 100644 --- a/Pod/BVConversations/Submission/BVSubmission.m +++ b/Pod/BVConversations/Submission/BVSubmission.m @@ -11,63 +11,66 @@ @implementation BVSubmission --(void)sendError:(nonnull NSError*)error failureCallback:(ConversationsFailureHandler) failure { - [[BVLogger sharedLogger] printError:error]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(@[error]); - }); +- (void)sendError:(nonnull NSError *)error + failureCallback:(ConversationsFailureHandler)failure { + [[BVLogger sharedLogger] printError:error]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(@[ error ]); + }); } --(void)sendErrors:(nonnull NSArray*)errors failureCallback:(ConversationsFailureHandler) failure { - for (NSError* error in errors) { - [[BVLogger sharedLogger] printError:error]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - failure(errors); - }); +- (void)sendErrors:(nonnull NSArray *)errors + failureCallback:(ConversationsFailureHandler)failure { + for (NSError *error in errors) { + [[BVLogger sharedLogger] printError:error]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + failure(errors); + }); } --(nonnull NSData*)transformToPostBody:(nonnull NSDictionary*)parameters { - - NSMutableArray *queryItems = [NSMutableArray array]; - - parameters = [self urlEncodeParameters:parameters]; - - for (NSString *key in parameters) { - [queryItems addObject:[NSURLQueryItem queryItemWithName:key value:parameters[key]]]; - } - - NSURLComponents *components = [NSURLComponents componentsWithString:@"http://bazaarvoice.com"]; - components.queryItems = queryItems; - NSString* query = components.query; - return [query dataUsingEncoding:NSUTF8StringEncoding]; - +- (nonnull NSData *)transformToPostBody:(nonnull NSDictionary *)parameters { + + NSMutableArray *queryItems = [NSMutableArray array]; + + parameters = [self urlEncodeParameters:parameters]; + + for (NSString *key in parameters) { + [queryItems + addObject:[NSURLQueryItem queryItemWithName:key value:parameters[key]]]; + } + + NSURLComponents *components = + [NSURLComponents componentsWithString:@"http://bazaarvoice.com"]; + components.queryItems = queryItems; + NSString *query = components.query; + return [query dataUsingEncoding:NSUTF8StringEncoding]; } static NSString *urlEncode(id object) { - - NSString *string = [NSString stringWithFormat: @"%@", object]; - - NSMutableCharacterSet *chars = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; - [chars removeCharactersInString:@"+&"]; - return [string stringByAddingPercentEncodingWithAllowedCharacters:chars]; - + + NSString *string = [NSString stringWithFormat:@"%@", object]; + + NSMutableCharacterSet *chars = + [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [chars removeCharactersInString:@"+&"]; + return [string stringByAddingPercentEncodingWithAllowedCharacters:chars]; } --(NSDictionary *) urlEncodeParameters:(NSDictionary *)parameters { - NSMutableDictionary *parts = [NSMutableDictionary dictionary]; - - for (id key in parameters) { - id value = [parameters objectForKey: key]; - if([value isKindOfClass:[NSArray class]]) { - for(NSString * valueString in value){ - [parts setObject:urlEncode(valueString) forKey:urlEncode(key)]; - } - } else if([value isKindOfClass:[NSString class]]) { - [parts setObject:urlEncode(value) forKey:urlEncode(key)]; - } +- (NSDictionary *)urlEncodeParameters:(NSDictionary *)parameters { + NSMutableDictionary *parts = [NSMutableDictionary dictionary]; + + for (id key in parameters) { + id value = [parameters objectForKey:key]; + if ([value isKindOfClass:[NSArray class]]) { + for (NSString *valueString in value) { + [parts setObject:urlEncode(valueString) forKey:urlEncode(key)]; + } + } else if ([value isKindOfClass:[NSString class]]) { + [parts setObject:urlEncode(value) forKey:urlEncode(key)]; } - return parts; + } + return parts; } @end diff --git a/Pod/BVConversations/Submission/BVSubmissionAction.h b/Pod/BVConversations/Submission/BVSubmissionAction.h index e9886402..cc49edaa 100644 --- a/Pod/BVConversations/Submission/BVSubmissionAction.h +++ b/Pod/BVConversations/Submission/BVSubmissionAction.h @@ -7,18 +7,20 @@ #import -/* +/* The two allowable submission actions - Submit and Preview. `action == submit` actually tries to submit the content. - `action == preview` doesn't try to submit the content, only validates the content and returns with success or any errors that may occur during validation. + `action == preview` doesn't try to submit the content, only validates the + content and returns with success or any errors that may occur during + validation. */ typedef NS_ENUM(NSInteger, BVSubmissionAction) { - BVSubmissionActionSubmit, - BVSubmissionActionPreview + BVSubmissionActionSubmit, + BVSubmissionActionPreview }; @interface BVSubmissionActionUtil : NSObject -+(NSString* _Nonnull)toString:(BVSubmissionAction)action; ++ (nonnull NSString *)toString:(BVSubmissionAction)action; @end diff --git a/Pod/BVConversations/Submission/BVSubmissionAction.m b/Pod/BVConversations/Submission/BVSubmissionAction.m index c68ac53a..5ae6a606 100644 --- a/Pod/BVConversations/Submission/BVSubmissionAction.m +++ b/Pod/BVConversations/Submission/BVSubmissionAction.m @@ -9,15 +9,15 @@ @implementation BVSubmissionActionUtil -+(NSString* _Nonnull)toString:(BVSubmissionAction)action { - - switch (action) { - - case BVSubmissionActionSubmit: return @"Submit"; - case BVSubmissionActionPreview: return @"Preview"; - - } - ++ (nonnull NSString *)toString:(BVSubmissionAction)action { + + switch (action) { + + case BVSubmissionActionSubmit: + return @"Submit"; + case BVSubmissionActionPreview: + return @"Preview"; + } } @end diff --git a/Pod/BVConversations/Submission/BVSubmissionErrorCode.h b/Pod/BVConversations/Submission/BVSubmissionErrorCode.h index f180afd8..13a75fc0 100644 --- a/Pod/BVConversations/Submission/BVSubmissionErrorCode.h +++ b/Pod/BVConversations/Submission/BVSubmissionErrorCode.h @@ -11,29 +11,29 @@ Type of form error code */ typedef NS_ENUM(NSInteger, BVSubmissionErrorCode) { - BVSubmissionErrorCodeFormDuplicate, - BVSubmissionErrorCodeFormDuplicateNickname, - BVSubmissionErrorCodeFormInvalidEmailAddress, - BVSubmissionErrorCodeFormInvalidIpAddress, - BVSubmissionErrorCodeFormInvalidOption, - BVSubmissionErrorCodeFormPatternMismatch, - BVSubmissionErrorCodeFormProfanity, - BVSubmissionErrorCodeFormRejected, - BVSubmissionErrorCodeFormRequired, - BVSubmissionErrorCodeFormRequiredEither, - BVSubmissionErrorCodeFormRequiredNickname, - BVSubmissionErrorCodeFormRequiresTrue, - BVSubmissionErrorCodeFormRestricted, - BVSubmissionErrorCodeFormStorageProviderFailed, - BVSubmissionErrorCodeFormSubmittedNickname, - BVSubmissionErrorCodeFormTooFew, - BVSubmissionErrorCodeFormTooHigh, - BVSubmissionErrorCodeFormTooLong, - BVSubmissionErrorCodeFormTooLow, - BVSubmissionErrorCodeFormTooShort, - BVSubmissionErrorCodeFormUploadIo, - BVSubmissionErrorCodeParamDuplicateSubmission, - BVSubmissionErrorCodeParamInvalidSubjectId, - BVSubmissionErrorCodeParamMissingSubjectId, - BVSubmissionErrorCodeUnknown + BVSubmissionErrorCodeFormDuplicate, + BVSubmissionErrorCodeFormDuplicateNickname, + BVSubmissionErrorCodeFormInvalidEmailAddress, + BVSubmissionErrorCodeFormInvalidIpAddress, + BVSubmissionErrorCodeFormInvalidOption, + BVSubmissionErrorCodeFormPatternMismatch, + BVSubmissionErrorCodeFormProfanity, + BVSubmissionErrorCodeFormRejected, + BVSubmissionErrorCodeFormRequired, + BVSubmissionErrorCodeFormRequiredEither, + BVSubmissionErrorCodeFormRequiredNickname, + BVSubmissionErrorCodeFormRequiresTrue, + BVSubmissionErrorCodeFormRestricted, + BVSubmissionErrorCodeFormStorageProviderFailed, + BVSubmissionErrorCodeFormSubmittedNickname, + BVSubmissionErrorCodeFormTooFew, + BVSubmissionErrorCodeFormTooHigh, + BVSubmissionErrorCodeFormTooLong, + BVSubmissionErrorCodeFormTooLow, + BVSubmissionErrorCodeFormTooShort, + BVSubmissionErrorCodeFormUploadIo, + BVSubmissionErrorCodeParamDuplicateSubmission, + BVSubmissionErrorCodeParamInvalidSubjectId, + BVSubmissionErrorCodeParamMissingSubjectId, + BVSubmissionErrorCodeUnknown }; diff --git a/Pod/BVConversations/Submission/BVSubmissionErrorResponse.h b/Pod/BVConversations/Submission/BVSubmissionErrorResponse.h index caab1b69..a75a4835 100644 --- a/Pod/BVConversations/Submission/BVSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/BVSubmissionErrorResponse.h @@ -5,27 +5,29 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVConversationsError.h" #import "BVFieldError.h" +#import /* - The submission reached the Bazaarvoice server successfully but ultimately failed. This most commonly is caused - by issues with validation. For example: a review with `reviewText` that is too short, or a review that is missing user information. + The submission reached the Bazaarvoice server successfully but ultimately + failed. This most commonly is caused + by issues with validation. For example: a review with `reviewText` that is too + short, or a review that is missing user information. */ @interface BVSubmissionErrorResponse : NSObject @property bool hasErrors; -@property NSArray* _Nonnull errors; -@property NSArray* _Nonnull fieldErrors; +@property(nonnull) NSArray *errors; +@property(nonnull) NSArray *fieldErrors; -@property NSString* _Nullable locale; -@property NSString* _Nullable submissionId; -@property NSNumber* _Nullable typicalHoursToPost; -@property NSString* _Nullable authorSubmissionToken; +@property(nullable) NSString *locale; +@property(nullable) NSString *submissionId; +@property(nullable) NSNumber *typicalHoursToPost; +@property(nullable) NSString *authorSubmissionToken; --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; --(nonnull NSArray*)toNSErrors; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nonnull NSArray *)toNSErrors; @end diff --git a/Pod/BVConversations/Submission/BVSubmissionErrorResponse.m b/Pod/BVConversations/Submission/BVSubmissionErrorResponse.m index cf8843a4..6c023ede 100644 --- a/Pod/BVConversations/Submission/BVSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/BVSubmissionErrorResponse.m @@ -10,50 +10,52 @@ @implementation BVSubmissionErrorResponse --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSDictionary* apiObject = apiResponse; - - NSNumber* hasErrs = apiObject[@"HasErrors"]; - if(hasErrs != nil) { - self.hasErrors = [hasErrs boolValue]; - } - - if(self.hasErrors == false){ - return nil; - } - - SET_IF_NOT_NULL(self.locale, apiObject[@"Locale"]) - SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) - SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) - SET_IF_NOT_NULL(self.authorSubmissionToken, apiObject[@"AuthorSubmissionToken"]) - - self.errors = [BVConversationsError createErrorListFromApiResponse:apiObject[@"Errors"]]; - self.fieldErrors = [BVFieldError createListFromFormErrorsDictionary:apiObject[@"FormErrors"]]; - +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; -} --(nonnull NSArray*)toNSErrors { - - NSMutableArray* tempErrors = [NSMutableArray array]; - for(BVConversationsError* error in self.errors) { - [tempErrors addObject:[error toNSError]]; + NSDictionary *apiObject = apiResponse; + + NSNumber *hasErrs = apiObject[@"HasErrors"]; + if (hasErrs != nil) { + self.hasErrors = [hasErrs boolValue]; } - - for(BVFieldError* error in self.fieldErrors) { - [tempErrors addObject:[error toNSError]]; + + if (self.hasErrors == false) { + return nil; } - - return tempErrors; - + + SET_IF_NOT_NULL(self.locale, apiObject[@"Locale"]) + SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) + SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) + SET_IF_NOT_NULL(self.authorSubmissionToken, + apiObject[@"AuthorSubmissionToken"]) + + self.errors = [BVConversationsError + createErrorListFromApiResponse:apiObject[@"Errors"]]; + self.fieldErrors = [BVFieldError + createListFromFormErrorsDictionary:apiObject[@"FormErrors"]]; + } + return self; +} + +- (nonnull NSArray *)toNSErrors { + + NSMutableArray *tempErrors = [NSMutableArray array]; + for (BVConversationsError *error in self.errors) { + [tempErrors addObject:[error toNSError]]; + } + + for (BVFieldError *error in self.fieldErrors) { + [tempErrors addObject:[error toNSError]]; + } + + return tempErrors; } @end diff --git a/Pod/BVConversations/Submission/BVSubmissionResponse.h b/Pod/BVConversations/Submission/BVSubmissionResponse.h index 77066fe3..9800f07f 100644 --- a/Pod/BVConversations/Submission/BVSubmissionResponse.h +++ b/Pod/BVConversations/Submission/BVSubmissionResponse.h @@ -12,14 +12,17 @@ */ @interface BVSubmissionResponse : NSObject -@property NSString* _Nullable locale; -@property NSString* _Nullable submissionId; -@property NSNumber* _Nullable typicalHoursToPost; -@property NSString* _Nullable authorSubmissionToken; +@property(nullable) NSString *locale; +@property(nullable) NSString *submissionId; +@property(nullable) NSNumber *typicalHoursToPost; +@property(nullable) NSString *authorSubmissionToken; -/// Form fields are present in Preview mode only. The form fileds are a dictionry of BVFormField objects where the keys are form field elements. For details, please also refer to the Bazaarvoice Developer Portal: https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element -@property NSDictionary * _Nullable formFields; +/// Form fields are present in Preview mode only. The form fileds are a +/// dictionry of BVFormField objects where the keys are form field elements. For +/// details, please also refer to the Bazaarvoice Developer Portal: +/// https://developer.bazaarvoice.com/docs/read/conversations_api/tutorials/submission/how_to_build_a_subission_form#fields-element +@property(nullable) NSDictionary *formFields; --(nonnull instancetype)initWithApiResponse:(nonnull NSDictionary*)apiResponse; +- (nonnull instancetype)initWithApiResponse:(nonnull NSDictionary *)apiResponse; @end diff --git a/Pod/BVConversations/Submission/BVSubmissionResponse.m b/Pod/BVConversations/Submission/BVSubmissionResponse.m index f820b734..ef786c8a 100644 --- a/Pod/BVConversations/Submission/BVSubmissionResponse.m +++ b/Pod/BVConversations/Submission/BVSubmissionResponse.m @@ -6,37 +6,37 @@ // #import "BVSubmissionResponse.h" -#import "BVNullHelper.h" #import "BVFormField.h" +#import "BVNullHelper.h" @implementation BVSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - self = [super init]; - if(self){ - - SET_IF_NOT_NULL(self.locale, apiResponse[@"Locale"]) - SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) - SET_IF_NOT_NULL(self.typicalHoursToPost, apiResponse[@"TypicalHoursToPost"]) - SET_IF_NOT_NULL(self.authorSubmissionToken, apiResponse[@"AuthorSubmissionToken"]) - - NSMutableDictionary *tmpFields = [NSMutableDictionary dictionary]; - - NSDictionary *data = [apiResponse objectForKey:@"Data"]; - if (data){ - NSDictionary *fields = [data objectForKey:@"Fields"]; - - for (NSDictionary *fieldDict in [fields allValues]){ - BVFormField *formField = [[BVFormField alloc] initWithFormFieldDictionary:fieldDict]; - [tmpFields setObject:formField forKey:formField.identifier]; - } - - self.formFields = [NSDictionary dictionaryWithDictionary:tmpFields]; - - } - +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + self = [super init]; + if (self) { + + SET_IF_NOT_NULL(self.locale, apiResponse[@"Locale"]) + SET_IF_NOT_NULL(self.submissionId, apiResponse[@"SubmissionId"]) + SET_IF_NOT_NULL(self.typicalHoursToPost, apiResponse[@"TypicalHoursToPost"]) + SET_IF_NOT_NULL(self.authorSubmissionToken, + apiResponse[@"AuthorSubmissionToken"]) + + NSMutableDictionary *tmpFields = [NSMutableDictionary dictionary]; + + NSDictionary *data = [apiResponse objectForKey:@"Data"]; + if (data) { + NSDictionary *fields = [data objectForKey:@"Fields"]; + + for (NSDictionary *fieldDict in [fields allValues]) { + BVFormField *formField = + [[BVFormField alloc] initWithFormFieldDictionary:fieldDict]; + [tmpFields setObject:formField forKey:formField.identifier]; + } + + self.formFields = [NSDictionary dictionaryWithDictionary:tmpFields]; } - return self; + } + return self; } @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmission.h b/Pod/BVConversations/Submission/Comment/BVCommentSubmission.h index d9f97712..742456b3 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmission.h +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmission.h @@ -4,33 +4,37 @@ // // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import "BVCommentSubmission.h" #import "BVBaseUGCSubmission.h" +#import "BVCommentSubmission.h" #import "BVCommentSubmissionResponse.h" @class BVCommentSubmissionResponse; -typedef void (^CommentSubmissionCompletion)(BVCommentSubmissionResponse* _Nonnull response); +typedef void (^CommentSubmissionCompletion)( + BVCommentSubmissionResponse *__nonnull response); @interface BVCommentSubmission : BVBaseUGCSubmission - /** - Initialize a request object to add a review comment. Initialize the request with the supplied initializer, then add the additional parameters required by your API key. See also the Bazaarvoice Developer Reference: https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-submission + Initialize a request object to add a review comment. Initialize the request + with the supplied initializer, then add the additional parameters required by + your API key. See also the Bazaarvoice Developer Reference: + https://developer.bazaarvoice.com/conversations-api/reference/v5.4/comments/comment-submission @param reviewId The review ID to add the comment to @param commentText The user supplied text @return initialized BVCommentSubmission parameter */ -- (nonnull instancetype)initWithReviewId:(nonnull NSString *)reviewId withCommentText:(nonnull NSString * )commentText; -- (nonnull instancetype) __unavailable init; - +- (nonnull instancetype)initWithReviewId:(nonnull NSString *)reviewId + withCommentText:(nonnull NSString *)commentText; +- (nonnull instancetype)__unavailable init; --(void)submit:(nonnull CommentSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; +- (void)submit:(nonnull CommentSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure; -@property (readonly) NSString* _Nonnull reviewId; -@property (readonly) NSString* _Nonnull commentText; +@property(nonnull, readonly) NSString *reviewId; +@property(nonnull, readonly) NSString *commentText; -@property NSString * _Nonnull commentTitle; +@property(nonnull) NSString *commentTitle; @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmission.m b/Pod/BVConversations/Submission/Comment/BVCommentSubmission.m index 97f99dc2..07ce34f7 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmission.m +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmission.m @@ -6,9 +6,9 @@ // #import "BVCommentSubmission.h" +#import "BVCommentSubmissionErrorResponse.h" #import "BVSDKConfiguration.h" #import "BVSubmissionErrorResponse.h" -#import "BVCommentSubmissionErrorResponse.h" #import "BVUploadablePhoto.h" @interface BVCommentSubmission () @@ -17,233 +17,271 @@ @interface BVCommentSubmission () @end - @implementation BVCommentSubmission -- (nonnull instancetype)initWithReviewId:(NSString *)reviewId withCommentText:(NSString *)commentText { - - self = [super init]; - - if (self){ - _reviewId = reviewId; - _commentText = commentText; - } - - return self; -} +- (nonnull instancetype)initWithReviewId:(NSString *)reviewId + withCommentText:(NSString *)commentText { + self = [super init]; + + if (self) { + _reviewId = reviewId; + _commentText = commentText; + } + return self; +} --(void)submit:(nonnull CommentSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - if (self.action == BVSubmissionActionPreview) { - [[BVLogger sharedLogger] warning:@"Submitting a 'BVCommentSubmission' with action set to `BVSubmissionActionPreview` will not actially submit the comment! Set to `BVSubmissionActionSubmit` for real submission."]; - [self submitPreview:success failure:failure]; +- (void)submit:(nonnull CommentSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.action == BVSubmissionActionPreview) { + [[BVLogger sharedLogger] + warning:@"Submitting a 'BVCommentSubmission' with action set to " + @"`BVSubmissionActionPreview` will not actially submit " + @"the comment! Set to `BVSubmissionActionSubmit` for real " + @"submission."]; + [self submitPreview:success failure:failure]; + } else { + [self submitPreview:^(BVCommentSubmissionResponse *__nonnull response) { + [self submitForReal:success failure:failure]; } - else { - [self submitPreview:^(BVCommentSubmissionResponse * _Nonnull response) { - [self submitForReal:success failure:failure]; - } failure:^(NSArray * _Nonnull errors) { - [self sendErrors:errors failureCallback:failure]; + failure:^(NSArray *__nonnull errors) { + [self sendErrors:errors failureCallback:failure]; }]; - } + } } --(void)submitPreview:(CommentSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - [self submitCommentWithPhotoUrls:BVSubmissionActionPreview - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - +- (void)submitPreview:(CommentSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + [self submitCommentWithPhotoUrls:BVSubmissionActionPreview + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; } --(void)submitForReal:(CommentSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - if ([self.photos count] == 0) { - [self submitCommentWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - return; - } - - // upload photos before submitting comments (prr only) - NSMutableArray* photoUrls = [NSMutableArray array]; - NSMutableArray* photoCaptions = [NSMutableArray array]; - - for (BVUploadablePhoto* photo in self.photos) { - - [photo uploadForContentType:BVPhotoContentTypeComment success:^(NSString * _Nonnull photoUrl) { - - // Queue one event for each photo uploaded. - BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.reviewId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNamePhoto - withAdditionalParams:@{@"detail1":@"Comment"}]; - [BVPixel trackEvent:photoUploadEvent]; - - - [photoUrls addObject:photoUrl]; - [photoCaptions addObject:photo.photoCaption]; - - // all photos uploaded! submit comment - if ([photoUrls count] == [self.photos count]) { - [self submitCommentWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:photoUrls - photoCaptions:photoCaptions - success:success - failure:failure]; - } - - } failure:^(NSArray * _Nonnull errors) { - - if (!self.failureCalled) { - self.failureCalled = true; // only call failure block once, if multiple photos failed. - [self sendErrors:errors failureCallback:failure]; - } - +- (void)submitForReal:(CommentSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + if ([self.photos count] == 0) { + [self submitCommentWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; + return; + } + + // upload photos before submitting comments (prr only) + NSMutableArray *photoUrls = [NSMutableArray array]; + NSMutableArray *photoCaptions = [NSMutableArray array]; + + for (BVUploadablePhoto *photo in self.photos) { + [photo uploadForContentType:BVPhotoContentTypeComment + success:^(NSString *__nonnull photoUrl) { + + // Queue one event for each photo uploaded. + BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.reviewId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNamePhoto + withAdditionalParams:@{@"detail1" : @"Comment"}]; + [BVPixel trackEvent:photoUploadEvent]; + + [photoUrls addObject:photoUrl]; + [photoCaptions addObject:photo.photoCaption]; + + // all photos uploaded! submit comment + if ([photoUrls count] == [self.photos count]) { + [self submitCommentWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:photoUrls + photoCaptions:photoCaptions + success:success + failure:failure]; + } + + } + failure:^(NSArray *__nonnull errors) { + + if (!self.failureCalled) { + self.failureCalled = true; // only call failure block once, if + // multiple photos failed. + [self sendErrors:errors failureCallback:failure]; + } + }]; - - } - + } } --(void)submitCommentWithPhotoUrls:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions success:(nonnull CommentSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - - NSDictionary* parameters = [self createSubmissionParameters:action photoUrls:photoUrls photoCaptions:photoCaptions]; - NSData* postBody = [self transformToPostBody:parameters]; - - NSString* urlString = [NSString stringWithFormat:@"%@submitreviewcomment.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *httpError) { - - @try { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. +- (void)submitCommentWithPhotoUrls:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions + success:(nonnull CommentSubmissionCompletion)success + failure: + (nonnull ConversationsFailureHandler)failure { + NSDictionary *parameters = [self createSubmissionParameters:action + photoUrls:photoUrls + photoCaptions:photoCaptions]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@submitreviewcomment.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - BVCommentSubmissionErrorResponse* errorResponse = [[BVCommentSubmissionErrorResponse alloc] initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVCommentSubmissionErrorResponse *errorResponse = + [[BVCommentSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Photo upload failed."}]; - [self sendError:statusError failureCallback:failure]; - } - else if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - } - else if(errorResponse){ - // api returned successfully, but has bazaarvoice-specific errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; - } - else { - // success! - - // Fire event now that we've confirmed the comment was successfully uploaded. - BVFeatureUsedEvent *commentQuestionEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.reviewId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameReviewComment - withAdditionalParams:nil]; - - [BVPixel trackEvent:commentQuestionEvent]; - - BVCommentSubmissionResponse* response = [[BVCommentSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); + // network error was generated + [self sendError:httpError failureCallback:failure]; + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Photo upload failed." + }]; + [self sendError:statusError failureCallback:failure]; + } else if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + } else { + // success! + + // Fire event now that we've confirmed the comment was + // successfully uploaded. + BVFeatureUsedEvent *commentQuestionEvent = [ + [BVFeatureUsedEvent alloc] + initWithProductId:self.reviewId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameReviewComment + withAdditionalParams:nil]; + + [BVPixel trackEvent:commentQuestionEvent]; + + BVCommentSubmissionResponse *response = [ + [BVCommentSubmissionResponse alloc] initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); } - - } - @catch (NSException *exception) { - NSError* unknownError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; + + } @catch (NSException *exception) { + NSError *unknownError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; [self sendError:unknownError failureCallback:failure]; - } - - }]; - - // start uploading comment - [postDataTask resume]; - -} + } + }]; --(nonnull NSDictionary*)createSubmissionParameters:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion": @"5.4", - @"commenttext": _commentText, - @"reviewid": _reviewId, - }]; - - parameters[@"passkey"] = [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"action"] = [BVSubmissionActionUtil toString:action]; - - parameters[@"campaignid"] = self.campaignId; - parameters[@"locale"] = self.locale; - - parameters[@"title"] = self.commentTitle; - - parameters[@"hostedauthentication_authenticationemail"] = self.hostedAuthenticationEmail; - parameters[@"hostedauthentication_callbackurl"] = self.hostedAuthenticationCallback; - parameters[@"fp"] = self.fingerPrint; - parameters[@"user"] = self.user; - parameters[@"usernickname"] = self.userNickname; - parameters[@"useremail"] = self.userEmail; - parameters[@"userid"] = self.userId; - parameters[@"userlocation"] = self.userLocation; - - int photoIndex = 0; - for(NSString* url in photoUrls) { - NSString* key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; - parameters[key] = url; - photoIndex += 1; - } - - int captionIndex = 0; - for(NSString* caption in photoCaptions) { - NSString* key = [NSString stringWithFormat:@"photocaption_%i", captionIndex]; - parameters[key] = caption; - captionIndex += 1; - } - - if (self.sendEmailAlertWhenPublished) { - parameters[@"sendemailalertwhenpublished"] = [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; - } - - if (self.agreedToTermsAndConditions) { - parameters[@"agreedtotermsandconditions"] = [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; - } - - for (BVStringKeyValuePair* keyValuePair in self.customFormPairs) { - NSString* key = [keyValuePair key]; - NSString* value = [keyValuePair value]; - parameters[key] = value; - } - - return parameters; - + // start uploading comment + [postDataTask resume]; } +- (nonnull NSDictionary *) +createSubmissionParameters:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"commenttext" : _commentText, + @"reviewid" : _reviewId, + }]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"action"] = [BVSubmissionActionUtil toString:action]; + + parameters[@"campaignid"] = self.campaignId; + parameters[@"locale"] = self.locale; + + parameters[@"title"] = self.commentTitle; + + parameters[@"hostedauthentication_authenticationemail"] = + self.hostedAuthenticationEmail; + parameters[@"hostedauthentication_callbackurl"] = + self.hostedAuthenticationCallback; + parameters[@"fp"] = self.fingerPrint; + parameters[@"user"] = self.user; + parameters[@"usernickname"] = self.userNickname; + parameters[@"useremail"] = self.userEmail; + parameters[@"userid"] = self.userId; + parameters[@"userlocation"] = self.userLocation; + + int photoIndex = 0; + for (NSString *url in photoUrls) { + NSString *key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; + parameters[key] = url; + photoIndex += 1; + } + + int captionIndex = 0; + for (NSString *caption in photoCaptions) { + NSString *key = + [NSString stringWithFormat:@"photocaption_%i", captionIndex]; + parameters[key] = caption; + captionIndex += 1; + } + + if (self.sendEmailAlertWhenPublished) { + parameters[@"sendemailalertwhenpublished"] = + [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; + } + + if (self.agreedToTermsAndConditions) { + parameters[@"agreedtotermsandconditions"] = + [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; + } + + for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { + NSString *key = [keyValuePair key]; + NSString *value = [keyValuePair value]; + parameters[key] = value; + } + + return parameters; +} @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.h b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.h index 97d41081..614a1fbc 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.h @@ -10,6 +10,6 @@ @interface BVCommentSubmissionErrorResponse : BVSubmissionErrorResponse -@property BVSubmittedComment* _Nullable comment; +@property(nullable) BVSubmittedComment *comment; @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.m b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.m index d3b1a0b0..b531fa82 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionErrorResponse.m @@ -9,17 +9,19 @@ @implementation BVCommentSubmissionErrorResponse +- (instancetype)initWithApiResponse:(nullable id)apiResponse { --(instancetype)initWithApiResponse:(nullable id)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - NSDictionary* apiObject = apiResponse; // [super initWithApiResponse:] checks that this is nonnull and a dictionary - self.comment = [[BVSubmittedComment alloc] initWithApiResponse:apiObject[@"Comment"]]; - } - - return self; + self = [super initWithApiResponse:apiResponse]; + + if (self) { + NSDictionary *apiObject = apiResponse; // [super initWithApiResponse:] + // checks that this is nonnull and a + // dictionary + self.comment = + [[BVSubmittedComment alloc] initWithApiResponse:apiObject[@"Comment"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.h b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.h index fa976cf7..7d7c35cd 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.h @@ -10,6 +10,6 @@ @interface BVCommentSubmissionResponse : BVSubmissionResponse -@property BVSubmittedComment* _Nullable comment; +@property(nullable) BVSubmittedComment *comment; @end diff --git a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.m b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.m index 4b973dcd..3db49e0f 100644 --- a/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Comment/BVCommentSubmissionResponse.m @@ -9,16 +9,16 @@ @implementation BVCommentSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - self.comment = [[BVSubmittedComment alloc] initWithApiResponse:apiResponse[@"Comment"]]; - } - - return self; -} +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + if (self) { + self.comment = [[BVSubmittedComment alloc] + initWithApiResponse:apiResponse[@"Comment"]]; + } + + return self; +} @end diff --git a/Pod/BVConversations/Submission/Comment/BVSubmittedComment.h b/Pod/BVConversations/Submission/Comment/BVSubmittedComment.h index 76331dcb..4bfdffce 100644 --- a/Pod/BVConversations/Submission/Comment/BVSubmittedComment.h +++ b/Pod/BVConversations/Submission/Comment/BVSubmittedComment.h @@ -9,14 +9,14 @@ @interface BVSubmittedComment : NSObject -@property (readonly) NSString* _Nullable commentText; -@property (readonly) NSString* _Nullable title; -@property (readonly) BOOL sendEmailAlertWhenAnswered; -@property (readonly) NSDate* _Nullable submissionTime; -@property (readonly) NSNumber* _Nullable typicalHoursToPost; -@property (readonly) NSString* _Nullable submissionId; -@property (readonly) NSString* _Nullable commentId; +@property(nullable, readonly) NSString *commentText; +@property(nullable, readonly) NSString *title; +@property(readonly) BOOL sendEmailAlertWhenAnswered; +@property(nullable, readonly) NSDate *submissionTime; +@property(nullable, readonly) NSNumber *typicalHoursToPost; +@property(nullable, readonly) NSString *submissionId; +@property(nullable, readonly) NSString *commentId; --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Submission/Comment/BVSubmittedComment.m b/Pod/BVConversations/Submission/Comment/BVSubmittedComment.m index f1d3c0ff..0b83c59f 100644 --- a/Pod/BVConversations/Submission/Comment/BVSubmittedComment.m +++ b/Pod/BVConversations/Submission/Comment/BVSubmittedComment.m @@ -6,36 +6,37 @@ // #import "BVSubmittedComment.h" -#import "BVNullHelper.h" #import "BVModelUtil.h" +#import "BVNullHelper.h" @implementation BVSubmittedComment --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(_commentText, apiObject[@"CommentText"]) - SET_IF_NOT_NULL(_title, apiObject[@"Title"]) - SET_IF_NOT_NULL(_submissionId, apiObject[@"SubmissionId"]) - SET_IF_NOT_NULL(_typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) - SET_IF_NOT_NULL(_commentId, apiObject[@"CommentId"]) - - _submissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; - - NSNumber* emailAlert = apiObject[@"SendEmailAlertWhenAnswered"]; - if(emailAlert != nil) { - _sendEmailAlertWhenAnswered = [emailAlert boolValue]; - } +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; -} + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(_commentText, apiObject[@"CommentText"]) + SET_IF_NOT_NULL(_title, apiObject[@"Title"]) + SET_IF_NOT_NULL(_submissionId, apiObject[@"SubmissionId"]) + SET_IF_NOT_NULL(_typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) + SET_IF_NOT_NULL(_commentId, apiObject[@"CommentId"]) + + _submissionTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; + + NSNumber *emailAlert = apiObject[@"SendEmailAlertWhenAnswered"]; + if (emailAlert != nil) { + _sendEmailAlertWhenAnswered = [emailAlert boolValue]; + } + } + return self; +} @end diff --git a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.h b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.h index 9b3676d6..cfc50abc 100644 --- a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.h +++ b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.h @@ -7,8 +7,8 @@ #import -#import "BVSubmission.h" #import "BVFeedbackSubmissionResponse.h" +#import "BVSubmission.h" #import "BVSubmissionAction.h" #import "BVSubmissionErrorResponse.h" @@ -16,66 +16,71 @@ Types of Bazaarvoice content supplying feedback on. */ typedef NS_ENUM(NSInteger, BVFeedbackContentType) { - BVFeedbackContentTypeReview, - BVFeedbackContentTypeQuestion, - BVFeedbackContentTypeAnswer + BVFeedbackContentTypeReview, + BVFeedbackContentTypeQuestion, + BVFeedbackContentTypeAnswer }; /* Types of feedback. */ typedef NS_ENUM(NSInteger, BVFeedbackType) { - BVFeedbackTypeInappropriate, - BVFeedbackTypeHelpfulness + BVFeedbackTypeInappropriate, + BVFeedbackTypeHelpfulness }; - /* Feedback vote. */ typedef NS_ENUM(NSInteger, BVFeedbackVote) { - BVFeedbackVotePositive, - BVFeedbackVoteNegative + BVFeedbackVotePositive, + BVFeedbackVoteNegative }; -typedef void (^FeedbackSubmissionCompletion)(BVFeedbackSubmissionResponse* _Nonnull response); +typedef void (^FeedbackSubmissionCompletion)( + BVFeedbackSubmissionResponse *__nonnull response); /** Class to use to submit a feedback to the Bazaarvoice platform. - - You can use the submission request class to send helpfulness votes or flag inappropriate content for reviews, questions, or answers. - + + You can use the submission request class to send helpfulness votes or flag + inappropriate content for reviews, questions, or answers. + For a description of possible fields see our API documentation at: https://developer.bazaarvoice.com/docs/read/conversations_api/reference/latest/feedback/submit - + @availability BVSDK 6.1.0 and later */ @interface BVFeedbackSubmission : BVSubmission -@property NSString* _Nonnull contentId; +@property(nonnull) NSString *contentId; @property BVFeedbackContentType contentType; @property BVFeedbackType feedbackType; @property BVFeedbackVote vote; -@property NSString * _Nonnull userId; -@property NSString * _Nullable reasonText; +@property(nonnull) NSString *userId; +@property(nullable) NSString *reasonText; --(nonnull instancetype)initWithContentId:(nonnull NSString*)contentId +- (nonnull instancetype)initWithContentId:(nonnull NSString *)contentId withContentType:(BVFeedbackContentType)contentType - withFeedbackType:(BVFeedbackType)feedbackType; + withFeedbackType:(BVFeedbackType)feedbackType; --(nonnull instancetype) __unavailable init; +- (nonnull instancetype)__unavailable init; /** Submit feedback to the Bazaarvoice platform. - - A submission can fail for many reasons, and is dependent on your submission configuration. - - @param success The success block is called when a successful submission occurs. - @param failure The failure block is called when an unsuccessful submission occurs. This could be for a number of reasons: network failures, submission parameters invalid, or server errors occur. - */ --(void)submit:(nonnull FeedbackSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; + A submission can fail for many reasons, and is dependent on your submission + configuration. + + @param success The success block is called when a successful submission + occurs. + @param failure The failure block is called when an unsuccessful submission + occurs. This could be for a number of reasons: network failures, submission + parameters invalid, or server errors occur. + */ +- (void)submit:(nonnull FeedbackSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure; @end diff --git a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.m b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.m index b807d07d..67488d43 100644 --- a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.m +++ b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmission.m @@ -6,183 +6,208 @@ // #import "BVFeedbackSubmission.h" -#import "BVSDKManager.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" @implementation BVFeedbackSubmission --(nonnull instancetype)initWithContentId:(nonnull NSString*)contentId +- (nonnull instancetype)initWithContentId:(nonnull NSString *)contentId withContentType:(BVFeedbackContentType)contentType - withFeedbackType:(BVFeedbackType)feedbackType{ - - self = [super init]; - if(self){ - self.contentId = contentId; - self.feedbackType = feedbackType; - self.contentType = contentType; - } - return self; - + withFeedbackType:(BVFeedbackType)feedbackType { + self = [super init]; + if (self) { + self.contentId = contentId; + self.feedbackType = feedbackType; + self.contentType = contentType; + } + return self; } --(void)submit:(nonnull FeedbackSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure{ - - [self submitFeedbackWithCompletion:^(BVFeedbackSubmissionResponse * _Nonnull response) { - success(response); - } failure:^(NSArray * _Nonnull errors) { +- (void)submit:(nonnull FeedbackSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + [self submitFeedbackWithCompletion:^( + BVFeedbackSubmissionResponse *__nonnull response) { + success(response); + } + failure:^(NSArray *__nonnull errors) { failure(errors); - }]; - + }]; } --(void)submitFeedbackWithCompletion:(nonnull FeedbackSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - NSDictionary* parameters = [self createSubmissionParameters]; - NSData* postBody = [self transformToPostBody:parameters]; - - NSString* urlString = [NSString stringWithFormat:@"%@submitfeedback.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *httpError) { - - @try { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. +- (void) +submitFeedbackWithCompletion:(nonnull FeedbackSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + NSDictionary *parameters = [self createSubmissionParameters]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@submitfeedback.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - BVSubmissionErrorResponse* errorResponse = [[BVSubmissionErrorResponse alloc] initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVSubmissionErrorResponse *errorResponse = + [[BVSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Feedback submission failed."}]; - [self sendError:statusError failureCallback:failure]; - } - else if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - } - else if(errorResponse){ - // api returned successfully, but has bazaarvoice-specific errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; - } - else { - // success! - - [self trackFeedbackEvent]; - - BVFeedbackSubmissionResponse* response = [[BVFeedbackSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); + // network error was generated + [self sendError:httpError failureCallback:failure]; + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Feedback submission failed." + }]; + [self sendError:statusError failureCallback:failure]; + } else if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + } else { + // success! + + [self trackFeedbackEvent]; + + BVFeedbackSubmissionResponse *response = + [[BVFeedbackSubmissionResponse alloc] + initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); } - - } - @catch (NSException *exception) { - NSError* unknownError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; + + } @catch (NSException *exception) { + NSError *unknownError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; [self sendError:unknownError failureCallback:failure]; - } - - }]; - - // start uploading answer - [postDataTask resume]; - + } + + }]; + + // start uploading answer + [postDataTask resume]; } - (void)trackFeedbackEvent { + // Fire event now that we've confirmed the answer was successfully uploaded. + BVPixelImpressionContentType contentType = BVPixelImpressionContentTypeReview; + BVPixelProductType productTypeForEvent = + BVPixelProductTypeConversationsReviews; + if (self.contentType == BVFeedbackContentTypeReview) { + contentType = BVPixelImpressionContentTypeReview; + productTypeForEvent = BVPixelProductTypeConversationsReviews; + } else if (self.contentType == BVFeedbackContentTypeQuestion) { + contentType = BVPixelImpressionContentTypeQuestion; + productTypeForEvent = BVPixelProductTypeConversationsQuestionAnswer; + } else if (self.contentType == BVFeedbackContentTypeAnswer) { + contentType = BVPixelImpressionContentTypeAnswer; + productTypeForEvent = BVPixelProductTypeConversationsQuestionAnswer; + } - // Fire event now that we've confirmed the answer was successfully uploaded. - BVPixelImpressionContentType contentType = BVPixelImpressionContentTypeReview; - BVPixelProductType productTypeForEvent = BVPixelProductTypeConversationsReviews; - if (self.contentType == BVFeedbackContentTypeReview){ - contentType = BVPixelImpressionContentTypeReview; - productTypeForEvent = BVPixelProductTypeConversationsReviews; - } else if (self.contentType == BVFeedbackContentTypeQuestion){ - contentType = BVPixelImpressionContentTypeQuestion; - productTypeForEvent = BVPixelProductTypeConversationsQuestionAnswer; - } else if (self.contentType == BVFeedbackContentTypeAnswer){ - contentType = BVPixelImpressionContentTypeAnswer; - productTypeForEvent = BVPixelProductTypeConversationsQuestionAnswer; + BVPixelFeatureUsedEventName featureUsed = BVPixelFeatureUsedEventNameFeedback; + NSString *detail1 = @""; + if (self.feedbackType == BVFeedbackTypeHelpfulness) { + featureUsed = BVPixelFeatureUsedEventNameFeedback; + if (self.vote == BVFeedbackVotePositive) { + detail1 = @"Positive"; + } else if (self.vote == BVFeedbackVoteNegative) { + detail1 = @"Negative"; } - - BVPixelFeatureUsedEventName featureUsed = BVPixelFeatureUsedEventNameFeedback; - NSString *detail1 = @""; - if (self.feedbackType == BVFeedbackTypeHelpfulness){ - featureUsed = BVPixelFeatureUsedEventNameFeedback; - if (self.vote == BVFeedbackVotePositive){ - detail1 = @"Positive"; - } else if (self.vote == BVFeedbackVoteNegative){ - detail1 = @"Negative"; - } - - } else if (self.feedbackType == BVFeedbackTypeInappropriate){ - featureUsed = BVPixelFeatureUsedEventNameInappropriate; - detail1 = @"Inappropriate"; - } - - NSDictionary *additionalParams = @{@"contentType":[BVPixelImpressionContentTypeUtil toString:contentType], - @"contentId":self.contentId, - @"detail1":detail1}; - - BVFeatureUsedEvent *feedbackEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.contentId - withBrand:nil - withProductType:productTypeForEvent - withEventName:featureUsed - withAdditionalParams:additionalParams]; - - [BVPixel trackEvent:feedbackEvent]; - - + + } else if (self.feedbackType == BVFeedbackTypeInappropriate) { + featureUsed = BVPixelFeatureUsedEventNameInappropriate; + detail1 = @"Inappropriate"; + } + + NSDictionary *additionalParams = @{ + @"contentType" : [BVPixelImpressionContentTypeUtil toString:contentType], + @"contentId" : self.contentId, + @"detail1" : detail1 + }; + + BVFeatureUsedEvent *feedbackEvent = + [[BVFeatureUsedEvent alloc] initWithProductId:self.contentId + withBrand:nil + withProductType:productTypeForEvent + withEventName:featureUsed + withAdditionalParams:additionalParams]; + + [BVPixel trackEvent:feedbackEvent]; } --(nonnull NSDictionary*)createSubmissionParameters { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{ @"apiversion": @"5.4" }]; - - parameters[@"passkey"] = [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"userid"] = self.userId; - parameters[@"contentId"] = self.contentId; - - // Set the content type - if (self.contentType == BVFeedbackContentTypeReview){ - parameters[@"contentType"] = @"review"; - } else if (self.contentType == BVFeedbackContentTypeAnswer){ - parameters[@"contentType"] = @"answer"; - } else if (self.contentType == BVFeedbackContentTypeQuestion){ - parameters[@"contentType"] = @"question"; - } - - if (self.feedbackType == BVFeedbackTypeHelpfulness){ - parameters[@"feedbackType"] = @"helpfulness"; - if (self.vote == BVFeedbackVotePositive){ - parameters[@"vote"] = @"positive"; - } else if (self.vote == BVFeedbackVoteNegative){ - parameters[@"vote"] = @"negative"; - } - } else if (self.feedbackType == BVFeedbackTypeInappropriate) { - parameters[@"feedbackType"] = @"inappropriate"; - } - - if (self.reasonText){ - parameters[@"reasonText"] = self.reasonText; +- (nonnull NSDictionary *)createSubmissionParameters { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{@"apiversion" : @"5.4"}]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"userid"] = self.userId; + parameters[@"contentId"] = self.contentId; + + // Set the content type + if (self.contentType == BVFeedbackContentTypeReview) { + parameters[@"contentType"] = @"review"; + } else if (self.contentType == BVFeedbackContentTypeAnswer) { + parameters[@"contentType"] = @"answer"; + } else if (self.contentType == BVFeedbackContentTypeQuestion) { + parameters[@"contentType"] = @"question"; + } + + if (self.feedbackType == BVFeedbackTypeHelpfulness) { + parameters[@"feedbackType"] = @"helpfulness"; + if (self.vote == BVFeedbackVotePositive) { + parameters[@"vote"] = @"positive"; + } else if (self.vote == BVFeedbackVoteNegative) { + parameters[@"vote"] = @"negative"; } - + } else if (self.feedbackType == BVFeedbackTypeInappropriate) { + parameters[@"feedbackType"] = @"inappropriate"; + } + + if (self.reasonText) { + parameters[@"reasonText"] = self.reasonText; + } - return parameters; + return parameters; } @end diff --git a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.h b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.h index d9bcf55a..39dc6cf0 100644 --- a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.h @@ -5,15 +5,15 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmittedFeedback.h" #import "BVFeedbackSubmissionResponse.h" #import "BVSubmissionResponse.h" +#import "BVSubmittedFeedback.h" +#import @interface BVFeedbackSubmissionResponse : BVSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary * _Nonnull )apiResponse; +- (nonnull instancetype)initWithApiResponse:(nonnull NSDictionary *)apiResponse; -@property BVSubmittedFeedback* _Nullable feedback; +@property(nullable) BVSubmittedFeedback *feedback; @end diff --git a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.m b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.m index e90921e0..15020ffb 100644 --- a/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Feedback/BVFeedbackSubmissionResponse.m @@ -10,15 +10,16 @@ @implementation BVFeedbackSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - self.feedback = [[BVSubmittedFeedback alloc] initWithApiResponse:apiResponse[@"Feedback"]]; - } - - return self; +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + + if (self) { + self.feedback = [[BVSubmittedFeedback alloc] + initWithApiResponse:apiResponse[@"Feedback"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.h b/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.h index b3530dae..24bce927 100644 --- a/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.h +++ b/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.h @@ -9,28 +9,27 @@ @interface BVFeedbackInappropriateResponse : NSObject --(nullable instancetype)initWithFeedbackResponse:(nullable id)feedbackDict; +- (nullable instancetype)initWithFeedbackResponse:(nullable id)feedbackDict; + +@property(nonnull) NSString *authorId; +@property(nonnull) NSString *reasonText; -@property NSString * _Nonnull authorId; -@property NSString * _Nonnull reasonText; - @end @interface BVFeedbackHelpfulnessResponse : NSObject --(nullable instancetype)initWithFeedbackResponse:(nullable id)feedbackDict; +- (nullable instancetype)initWithFeedbackResponse:(nullable id)feedbackDict; -@property NSString * _Nonnull authorId; -@property NSString * _Nonnull vote; +@property(nonnull) NSString *authorId; +@property(nonnull) NSString *vote; @end - @interface BVSubmittedFeedback : NSObject --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; -@property BVFeedbackInappropriateResponse * _Nonnull inappropriateResponse; -@property BVFeedbackHelpfulnessResponse * _Nonnull helpfulnessResponse; +@property(nonnull) BVFeedbackInappropriateResponse *inappropriateResponse; +@property(nonnull) BVFeedbackHelpfulnessResponse *helpfulnessResponse; @end diff --git a/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.m b/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.m index 041931d6..8538f2d3 100644 --- a/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.m +++ b/Pod/BVConversations/Submission/Feedback/BVSubmittedFeedback.m @@ -10,76 +10,78 @@ @implementation BVFeedbackInappropriateResponse --(nullable instancetype)initWithFeedbackResponse:(nullable id)inapropriateDict { - self = [super init]; - if(self){ - - if(inapropriateDict == nil || ![inapropriateDict isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary *values = [inapropriateDict objectForKey:@"Inappropriate"]; - - if (values){ - SET_IF_NOT_NULL(self.authorId, [values objectForKey:@"AuthorId"]); - SET_IF_NOT_NULL(self.reasonText, [values objectForKey:@"ReasonText"]); - } else { - return nil; - } +- (nullable instancetype)initWithFeedbackResponse: + (nullable id)inapropriateDict { + self = [super init]; + if (self) { + + if (inapropriateDict == nil || + ![inapropriateDict isKindOfClass:[NSDictionary class]]) { + return nil; + } + + NSDictionary *values = [inapropriateDict objectForKey:@"Inappropriate"]; + + if (values) { + SET_IF_NOT_NULL(self.authorId, [values objectForKey:@"AuthorId"]); + SET_IF_NOT_NULL(self.reasonText, [values objectForKey:@"ReasonText"]); + } else { + return nil; } - return self; + } + return self; } @end - @implementation BVFeedbackHelpfulnessResponse --(nullable instancetype)initWithFeedbackResponse:(nullable id)helpfulnessDict { - self = [super init]; - if(self){ - - if(helpfulnessDict == nil || ![helpfulnessDict isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary *values = [helpfulnessDict objectForKey:@"Helpfulness"]; - - if (values){ - SET_IF_NOT_NULL(self.authorId, [values objectForKey:@"AuthorId"]); - SET_IF_NOT_NULL(self.vote, [values objectForKey:@"Vote"]); - } else { - return nil; - } +- (nullable instancetype)initWithFeedbackResponse:(nullable id)helpfulnessDict { + self = [super init]; + if (self) { + + if (helpfulnessDict == nil || + ![helpfulnessDict isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; + + NSDictionary *values = [helpfulnessDict objectForKey:@"Helpfulness"]; + + if (values) { + SET_IF_NOT_NULL(self.authorId, [values objectForKey:@"AuthorId"]); + SET_IF_NOT_NULL(self.vote, [values objectForKey:@"Vote"]); + } else { + return nil; + } + } + return self; } @end - @implementation BVSubmittedFeedback --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - if (apiObject){ - self.inappropriateResponse = [[BVFeedbackInappropriateResponse alloc] initWithFeedbackResponse:apiObject]; - self.helpfulnessResponse = [[BVFeedbackHelpfulnessResponse alloc] initWithFeedbackResponse:apiObject]; - } else { - return nil; - } - +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; -} + NSDictionary *apiObject = apiResponse; + + if (apiObject) { + self.inappropriateResponse = [[BVFeedbackInappropriateResponse alloc] + initWithFeedbackResponse:apiObject]; + self.helpfulnessResponse = [[BVFeedbackHelpfulnessResponse alloc] + initWithFeedbackResponse:apiObject]; + } else { + return nil; + } + } + return self; +} @end diff --git a/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.h b/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.h index 8db7dcfd..456ba0f8 100644 --- a/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.h +++ b/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.h @@ -5,10 +5,10 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmissionErrorCode.h" #import "BVFieldError.h" +#import "BVSubmissionErrorCode.h" +#import @interface NSError (BVSubmissionErrorCodeParser) --(BVSubmissionErrorCode)bvSubmissionErrorCode; +- (BVSubmissionErrorCode)bvSubmissionErrorCode; @end diff --git a/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.m b/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.m index 8bd4bde2..cdef75f1 100644 --- a/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.m +++ b/Pod/BVConversations/Submission/NSError+BVSubmissionErrorCodeParser.m @@ -9,85 +9,84 @@ @implementation NSError (BVErrorCodeParser) --(BVSubmissionErrorCode)bvSubmissionErrorCode -{ - NSString* code = [self userInfo][BVFieldErrorCode]; - if (!code) { - return BVSubmissionErrorCodeUnknown; - } - if([code isEqualToString:@"ERROR_FORM_DUPLICATE"]) { - return BVSubmissionErrorCodeFormDuplicate; - } - if([code isEqualToString:@"ERROR_FORM_DUPLICATE_NICKNAME"]) { - return BVSubmissionErrorCodeFormDuplicateNickname; - } - if([code isEqualToString:@"ERROR_FORM_INVALID_EMAILADDRESS"]) { - return BVSubmissionErrorCodeFormInvalidEmailAddress; - } - if([code isEqualToString:@"ERROR_FORM_INVALID_IPADDRESS"]) { - return BVSubmissionErrorCodeFormInvalidIpAddress; - } - if([code isEqualToString:@"ERROR_FORM_INVALID_OPTION"]) { - return BVSubmissionErrorCodeFormInvalidOption; - } - if([code isEqualToString:@"ERROR_FORM_PATTERN_MISMATCH"]) { - return BVSubmissionErrorCodeFormPatternMismatch; - } - if([code isEqualToString:@"ERROR_FORM_PROFANITY"]) { - return BVSubmissionErrorCodeFormProfanity; - } - if([code isEqualToString:@"ERROR_FORM_REJECTED"]) { - return BVSubmissionErrorCodeFormRejected; - } - if([code isEqualToString:@"ERROR_FORM_REQUIRED"]) { - return BVSubmissionErrorCodeFormRequired; - } - if([code isEqualToString:@"ERROR_FORM_REQUIRED_EITHER"]) { - return BVSubmissionErrorCodeFormRequiredEither; - } - if([code isEqualToString:@"ERROR_FORM_REQUIRED_NICKNAME"]) { - return BVSubmissionErrorCodeFormRequiredNickname; - } - if([code isEqualToString:@"ERROR_FORM_REQUIRES_TRUE"]) { - return BVSubmissionErrorCodeFormRequiresTrue; - } - if([code isEqualToString:@"ERROR_FORM_RESTRICTED"]) { - return BVSubmissionErrorCodeFormRestricted; - } - if([code isEqualToString:@"ERROR_FORM_STORAGE_PROVIDER_FAILED"]) { - return BVSubmissionErrorCodeFormStorageProviderFailed; - } - if([code isEqualToString:@"ERROR_FORM_SUBMITTED_NICKNAME"]) { - return BVSubmissionErrorCodeFormSubmittedNickname; - } - if([code isEqualToString:@"ERROR_FORM_TOO_FEW"]) { - return BVSubmissionErrorCodeFormTooFew; - } - if([code isEqualToString:@"ERROR_FORM_TOO_HIGH"]) { - return BVSubmissionErrorCodeFormTooHigh; - } - if([code isEqualToString:@"ERROR_FORM_TOO_LONG"]) { - return BVSubmissionErrorCodeFormTooLong; - } - if([code isEqualToString:@"ERROR_FORM_TOO_LOW"]) { - return BVSubmissionErrorCodeFormTooLow; - } - if([code isEqualToString:@"ERROR_FORM_TOO_SHORT"]) { - return BVSubmissionErrorCodeFormTooShort; - } - if([code isEqualToString:@"ERROR_FORM_UPLOAD_IO"]) { - return BVSubmissionErrorCodeFormUploadIo; - } - if([code isEqualToString:@"ERROR_PARAM_DUPLICATE_SUBMISSION"]) { - return BVSubmissionErrorCodeParamDuplicateSubmission; - } - if([code isEqualToString:@"ERROR_PARAM_INVALID_SUBJECT_ID"]) { - return BVSubmissionErrorCodeParamInvalidSubjectId; - } - if([code isEqualToString:@"ERROR_PARAM_MISSING_SUBJECT_ID"]) { - return BVSubmissionErrorCodeParamMissingSubjectId; - } +- (BVSubmissionErrorCode)bvSubmissionErrorCode { + NSString *code = [self userInfo][BVFieldErrorCode]; + if (!code) { return BVSubmissionErrorCodeUnknown; + } + if ([code isEqualToString:@"ERROR_FORM_DUPLICATE"]) { + return BVSubmissionErrorCodeFormDuplicate; + } + if ([code isEqualToString:@"ERROR_FORM_DUPLICATE_NICKNAME"]) { + return BVSubmissionErrorCodeFormDuplicateNickname; + } + if ([code isEqualToString:@"ERROR_FORM_INVALID_EMAILADDRESS"]) { + return BVSubmissionErrorCodeFormInvalidEmailAddress; + } + if ([code isEqualToString:@"ERROR_FORM_INVALID_IPADDRESS"]) { + return BVSubmissionErrorCodeFormInvalidIpAddress; + } + if ([code isEqualToString:@"ERROR_FORM_INVALID_OPTION"]) { + return BVSubmissionErrorCodeFormInvalidOption; + } + if ([code isEqualToString:@"ERROR_FORM_PATTERN_MISMATCH"]) { + return BVSubmissionErrorCodeFormPatternMismatch; + } + if ([code isEqualToString:@"ERROR_FORM_PROFANITY"]) { + return BVSubmissionErrorCodeFormProfanity; + } + if ([code isEqualToString:@"ERROR_FORM_REJECTED"]) { + return BVSubmissionErrorCodeFormRejected; + } + if ([code isEqualToString:@"ERROR_FORM_REQUIRED"]) { + return BVSubmissionErrorCodeFormRequired; + } + if ([code isEqualToString:@"ERROR_FORM_REQUIRED_EITHER"]) { + return BVSubmissionErrorCodeFormRequiredEither; + } + if ([code isEqualToString:@"ERROR_FORM_REQUIRED_NICKNAME"]) { + return BVSubmissionErrorCodeFormRequiredNickname; + } + if ([code isEqualToString:@"ERROR_FORM_REQUIRES_TRUE"]) { + return BVSubmissionErrorCodeFormRequiresTrue; + } + if ([code isEqualToString:@"ERROR_FORM_RESTRICTED"]) { + return BVSubmissionErrorCodeFormRestricted; + } + if ([code isEqualToString:@"ERROR_FORM_STORAGE_PROVIDER_FAILED"]) { + return BVSubmissionErrorCodeFormStorageProviderFailed; + } + if ([code isEqualToString:@"ERROR_FORM_SUBMITTED_NICKNAME"]) { + return BVSubmissionErrorCodeFormSubmittedNickname; + } + if ([code isEqualToString:@"ERROR_FORM_TOO_FEW"]) { + return BVSubmissionErrorCodeFormTooFew; + } + if ([code isEqualToString:@"ERROR_FORM_TOO_HIGH"]) { + return BVSubmissionErrorCodeFormTooHigh; + } + if ([code isEqualToString:@"ERROR_FORM_TOO_LONG"]) { + return BVSubmissionErrorCodeFormTooLong; + } + if ([code isEqualToString:@"ERROR_FORM_TOO_LOW"]) { + return BVSubmissionErrorCodeFormTooLow; + } + if ([code isEqualToString:@"ERROR_FORM_TOO_SHORT"]) { + return BVSubmissionErrorCodeFormTooShort; + } + if ([code isEqualToString:@"ERROR_FORM_UPLOAD_IO"]) { + return BVSubmissionErrorCodeFormUploadIo; + } + if ([code isEqualToString:@"ERROR_PARAM_DUPLICATE_SUBMISSION"]) { + return BVSubmissionErrorCodeParamDuplicateSubmission; + } + if ([code isEqualToString:@"ERROR_PARAM_INVALID_SUBJECT_ID"]) { + return BVSubmissionErrorCodeParamInvalidSubjectId; + } + if ([code isEqualToString:@"ERROR_PARAM_MISSING_SUBJECT_ID"]) { + return BVSubmissionErrorCodeParamMissingSubjectId; + } + return BVSubmissionErrorCodeUnknown; } @end diff --git a/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.h b/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.h index dbc706e8..8b938699 100644 --- a/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.h +++ b/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.h @@ -8,25 +8,29 @@ #import #import -typedef void (^PhotoUploadCompletion)(NSString* _Nonnull photoUrl); -typedef void (^PhotoUploadFailure)(NSArray* _Nonnull errors); +typedef void (^PhotoUploadCompletion)(NSString *__nonnull photoUrl); +typedef void (^PhotoUploadFailure)(NSArray *__nonnull errors); typedef NS_ENUM(NSInteger, BVPhotoContentType) { - BVPhotoContentTypeReview, - BVPhotoContentTypeQuestion, - BVPhotoContentTypeAnswer, - BVPhotoContentTypeComment // PRR only + BVPhotoContentTypeReview, + BVPhotoContentTypeQuestion, + BVPhotoContentTypeAnswer, + BVPhotoContentTypeComment // PRR only }; @interface BVUploadablePhoto : NSObject -@property (readonly) UIImage* _Nonnull photo; -@property (readonly) NSString* _Nullable photoCaption; -@property (readwrite) int maxImageBytes; // Set by BVUploadablePhoto itself, but is here for testing +@property(nonnull, readonly) UIImage *photo; +@property(nullable, readonly) NSString *photoCaption; +@property(readwrite) int + maxImageBytes; // Set by BVUploadablePhoto itself, but is here for testing --(nonnull instancetype)initWithPhoto:(nonnull UIImage*)photo photoCaption:(nullable NSString*)caption; --(nonnull instancetype) __unavailable init; +- (nonnull instancetype)initWithPhoto:(nonnull UIImage *)photo + photoCaption:(nullable NSString *)caption; +- (nonnull instancetype)__unavailable init; --(void)uploadForContentType:(BVPhotoContentType)type success:(nonnull PhotoUploadCompletion)success failure:(nonnull PhotoUploadFailure)failure; +- (void)uploadForContentType:(BVPhotoContentType)type + success:(nonnull PhotoUploadCompletion)success + failure:(nonnull PhotoUploadFailure)failure; @end diff --git a/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.m b/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.m index dc7bb529..18683640 100644 --- a/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.m +++ b/Pod/BVConversations/Submission/Photo/BVUploadablePhoto.m @@ -6,184 +6,252 @@ // #import "BVUploadablePhoto.h" +#import "BVAnalyticsManager.h" #import "BVConversationsRequest.h" +#import "BVSDKConfiguration.h" #import "BVSDKManager.h" #import "BVSubmissionErrorResponse.h" -#import "BVAnalyticsManager.h" -#import "BVSDKConfiguration.h" static int const MAX_IMAGE_BYTES = 5 * 1024 * 1024; // BV API max is 5MB -@interface BVUploadablePhoto() +@interface BVUploadablePhoto () -@property (readwrite) UIImage* _Nonnull photo; -@property (readwrite) NSString* _Nullable photoCaption; +@property(nonnull, readwrite) UIImage *photo; +@property(nullable, readwrite) NSString *photoCaption; @end @implementation BVUploadablePhoto --(nonnull instancetype)initWithPhoto:(nonnull UIImage*)photo photoCaption:(nullable NSString*)caption { - self = [super init]; - if(self){ - self.photo = photo; - self.photoCaption = caption; - self.maxImageBytes = MAX_IMAGE_BYTES; - } - return self; +- (nonnull instancetype)initWithPhoto:(nonnull UIImage *)photo + photoCaption:(nullable NSString *)caption { + self = [super init]; + if (self) { + self.photo = photo; + self.photoCaption = caption; + self.maxImageBytes = MAX_IMAGE_BYTES; + } + return self; } --(NSString*)BVPhotoContentTypeToString:(BVPhotoContentType)type { - switch (type) { - case BVPhotoContentTypeReview: return @"review"; - case BVPhotoContentTypeAnswer: return @"answer"; - case BVPhotoContentTypeQuestion: return @"question"; - case BVPhotoContentTypeComment: return @"review_comment"; - } +- (NSString *)BVPhotoContentTypeToString:(BVPhotoContentType)type { + switch (type) { + case BVPhotoContentTypeReview: + return @"review"; + case BVPhotoContentTypeAnswer: + return @"answer"; + case BVPhotoContentTypeQuestion: + return @"question"; + case BVPhotoContentTypeComment: + return @"review_comment"; + } } --(void)uploadForContentType:(BVPhotoContentType)type success:(nonnull PhotoUploadCompletion)success failure:(nonnull PhotoUploadFailure)failure { - - NSString* urlString = [NSString stringWithFormat:@"%@uploadphoto.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - // create request - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - - // add multipart form data - NSMutableData *body = [NSMutableData data]; - NSString *boundary = @"----------------------------f3a1ba9c57bd"; - NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; - [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; - - [self appendKey:@"apiversion" value:@"5.4" toMultipartData:body withBoundary:boundary]; - [self appendKey:@"passkey" value:[self getPasskey] toMultipartData:body withBoundary:boundary]; - [self appendKey:@"contenttype" value:[self BVPhotoContentTypeToString:type] toMultipartData:body withBoundary:boundary]; - - NSData *nsData = [self nsDataForPhoto]; - - [self appendKey:@"photo" data: nsData toMultipartData:body withBoundary:boundary]; - - // close form - [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[body length]] forHTTPHeaderField:@"Content-Length"]; - [request setHTTPBody:body]; - - NSURLSession* session = [NSURLSession sharedSession]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n", urlString]]; - - NSURLSessionDataTask *sessionTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable httpError) { - - @try { - +- (void)uploadForContentType:(BVPhotoContentType)type + success:(nonnull PhotoUploadCompletion)success + failure:(nonnull PhotoUploadFailure)failure { + NSString *urlString = + [NSString stringWithFormat:@"%@uploadphoto.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + // create request + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + + // add multipart form data + NSMutableData *body = [NSMutableData data]; + NSString *boundary = @"----------------------------f3a1ba9c57bd"; + NSString *contentType = + [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; + [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; + + [self appendKey:@"apiversion" + value:@"5.4" + toMultipartData:body + withBoundary:boundary]; + [self appendKey:@"passkey" + value:[self getPasskey] + toMultipartData:body + withBoundary:boundary]; + [self appendKey:@"contenttype" + value:[self BVPhotoContentTypeToString:type] + toMultipartData:body + withBoundary:boundary]; + + NSData *nsData = [self nsDataForPhoto]; + + [self appendKey:@"photo" + data:nsData + toMultipartData:body + withBoundary:boundary]; + + // close form + [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [request setValue:[NSString + stringWithFormat:@"%lu", (unsigned long)[body length]] + forHTTPHeaderField:@"Content-Length"]; + [request setHTTPBody:body]; + + NSURLSession *session = [NSURLSession sharedSession]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n", urlString]]; + + NSURLSessionDataTask *sessionTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable httpError) { + + @try { // Attempt to get photoUrl from response. - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - NSString* photoUrl = json[@"Photo"][@"Sizes"][@"normal"][@"Url"]; // fails gracefully + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + NSString *photoUrl = + json[@"Photo"][@"Sizes"][@"normal"][@"Url"]; // fails gracefully // Generate bazaarvoice-specific error response, if applicable. - BVSubmissionErrorResponse* errorResponse = [[BVSubmissionErrorResponse alloc] initWithApiResponse:json]; //fail gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + BVSubmissionErrorResponse *errorResponse = + [[BVSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fail gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (photoUrl) { - // successful response! - success(photoUrl); - } - else if (httpError) { - // network error was generated - [[BVLogger sharedLogger] printError:httpError]; - failure(@[httpError]); - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Photo upload failed. Error code: BV_ERROR_NETWORK_FAILED"}]; - [[BVLogger sharedLogger] printError:statusError]; - failure(@[statusError]); - } - else if (jsonParsingError) { - // json parsing failed - [[BVLogger sharedLogger] printError:jsonParsingError]; - failure(@[jsonParsingError]); + // successful response! + success(photoUrl); + } else if (httpError) { + // network error was generated + [[BVLogger sharedLogger] printError:httpError]; + failure(@[ httpError ]); + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Photo upload failed. Error code: " + @"BV_ERROR_NETWORK_FAILED" + }]; + [[BVLogger sharedLogger] printError:statusError]; + failure(@[ statusError ]); + } else if (jsonParsingError) { + // json parsing failed + [[BVLogger sharedLogger] printError:jsonParsingError]; + failure(@[ jsonParsingError ]); + } else if (errorResponse) { + // bazaarvoice-specific error occured -- ex: API key is + // invalid + NSArray *errors = [errorResponse toNSErrors]; + [[BVLogger sharedLogger] printErrors:errors]; + failure(errors); + } else { + // Unknown error -- ex: api responded successfully but did + // not have photo URL + NSError *error = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_PARSING_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Photo upload failed. Error code: " + @"BV_ERROR_PARSING_FAILED" + }]; + [[BVLogger sharedLogger] printError:error]; + failure(@[ error ]); } - else if (errorResponse) { - // bazaarvoice-specific error occured -- ex: API key is invalid - NSArray* errors = [errorResponse toNSErrors]; - [[BVLogger sharedLogger] printErrors:errors]; - failure(errors); - } - else { - // Unknown error -- ex: api responded successfully but did not have photo URL - NSError* error = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_PARSING_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Photo upload failed. Error code: BV_ERROR_PARSING_FAILED"}]; - [[BVLogger sharedLogger] printError:error]; - failure(@[error]); - } - - } - @catch (NSException *exception) { - NSError* error = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred. Error code: BV_ERROR_UNKNOWN"}]; + + } @catch (NSException *exception) { + NSError *error = [NSError + errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred. Error " + @"code: BV_ERROR_UNKNOWN" + }]; [[BVLogger sharedLogger] printError:error]; - failure(@[error]); - } - - - }]; - - // start photo upload - [sessionTask resume]; + failure(@[ error ]); + } + + }]; + // start photo upload + [sessionTask resume]; } -- (nonnull NSData*) nsDataForPhoto { - NSData *nsData = UIImageJPEGRepresentation(self.photo, 1.0); - if (nsData.length > self.maxImageBytes) { - nsData = UIImageJPEGRepresentation(self.photo, 0.9); - NSUInteger imageByteCount = nsData.length; - if (imageByteCount > self.maxImageBytes) { - UIImage *workingImage = self.photo; - - while (imageByteCount > self.maxImageBytes) { - CGSize oldSize = workingImage.size; - CGSize newSize = CGSizeMake(oldSize.width / 2, oldSize.height / 2); - workingImage = [self resize:workingImage withTargetSize:newSize]; - nsData = UIImageJPEGRepresentation(workingImage, 0.9); - imageByteCount = nsData.length; - } - } +- (nonnull NSData *)nsDataForPhoto { + NSData *nsData = UIImageJPEGRepresentation(self.photo, 1.0); + if (nsData.length > self.maxImageBytes) { + nsData = UIImageJPEGRepresentation(self.photo, 0.9); + NSUInteger imageByteCount = nsData.length; + if (imageByteCount > self.maxImageBytes) { + UIImage *workingImage = self.photo; + + while (imageByteCount > self.maxImageBytes) { + CGSize oldSize = workingImage.size; + CGSize newSize = CGSizeMake(oldSize.width / 2, oldSize.height / 2); + workingImage = [self resize:workingImage withTargetSize:newSize]; + nsData = UIImageJPEGRepresentation(workingImage, 0.9); + imageByteCount = nsData.length; + } } - return nsData; + } + return nsData; } -- (nonnull UIImage*)resize:(UIImage *)image withTargetSize:(CGSize)targetSize { - CGRect rect = CGRectMake(0, 0, targetSize.width, targetSize.height); - UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0); - [image drawInRect:rect]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return newImage; +- (nonnull UIImage *)resize:(UIImage *)image withTargetSize:(CGSize)targetSize { + CGRect rect = CGRectMake(0, 0, targetSize.width, targetSize.height); + UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0); + [image drawInRect:rect]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; } - -- (void)appendKey:(NSString *)key value:(NSString *)value toMultipartData:(NSMutableData *)body withBoundary:(NSString *)boundary { - [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[value dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; +- (void)appendKey:(NSString *)key + value:(NSString *)value + toMultipartData:(NSMutableData *)body + withBoundary:(NSString *)boundary { + [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithFormat:@"Content-Disposition: " + @"form-data; " + @"name=\"%@\"\r\n\r\n", + key] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[value dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; } -- (void)appendKey:(NSString *)key data:(NSData *)data toMultipartData:(NSMutableData *)body withBoundary:(NSString *)boundary { - [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"upload\"\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; - [body appendData:[NSData dataWithData:data]]; - [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; +- (void)appendKey:(NSString *)key + data:(NSData *)data + toMultipartData:(NSMutableData *)body + withBoundary:(NSString *)boundary { + [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithFormat:@"Content-Disposition: " + @"form-data; name=\"%@\"; " + @"filename=\"upload\"\r\n", + key] + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" + dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[NSData dataWithData:data]]; + [body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; } -- (NSString * _Nonnull)getPasskey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversations; +- (nonnull NSString *)getPasskey { + return [BVSDKManager sharedManager].configuration.apiKeyConversations; } @end diff --git a/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.h b/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.h index b60272de..45f319ad 100644 --- a/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.h +++ b/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.h @@ -5,9 +5,9 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // +#import "BVUploadablePhoto.h" #import #import -#import "BVUploadablePhoto.h" @interface BVUploadableStorePhoto : BVUploadablePhoto diff --git a/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.m b/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.m index f40563b5..0f123a7d 100644 --- a/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.m +++ b/Pod/BVConversations/Submission/Photo/Stores/BVUploadableStorePhoto.m @@ -7,19 +7,18 @@ #import "BVUploadableStorePhoto.h" #import "BVConversationsRequest.h" +#import "BVSDKConfiguration.h" #import "BVSDKManager.h" #import "BVSubmissionErrorResponse.h" -#import "BVSDKConfiguration.h" -@interface BVUploadableStorePhoto() +@interface BVUploadableStorePhoto () @end @implementation BVUploadableStorePhoto - -- (NSString * _Nonnull)getPasskey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; +- (nonnull NSString *)getPasskey { + return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; } @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmission.h b/Pod/BVConversations/Submission/Question/BVQuestionSubmission.h index 8a0532b3..acb641c5 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmission.h +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmission.h @@ -5,55 +5,61 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import +#import "BVBaseUGCSubmission.h" +#import "BVConversationsRequest.h" +#import "BVQuestionSubmissionResponse.h" #import "BVSubmissionAction.h" #import "BVUploadablePhoto.h" -#import "BVQuestionSubmissionResponse.h" -#import "BVConversationsRequest.h" -#import "BVBaseUGCSubmission.h" - - -typedef void (^QuestionSubmissionCompletion)(BVQuestionSubmissionResponse* _Nonnull response); +#import +typedef void (^QuestionSubmissionCompletion)( + BVQuestionSubmissionResponse *__nonnull response); /** Class to use to submit a question to the Bazaarvoice platform. - - Of the many parameters possible on a BVQuestionSubmission, the ones needed for submission depend on your specific implementation. - + + Of the many parameters possible on a BVQuestionSubmission, the ones needed for + submission depend on your specific implementation. + For a description of possible fields see our API documentation at: https://developer.bazaarvoice.com/docs/read/conversations/questions/submit/5_4 - + @availability 4.1.0 and later */ @interface BVQuestionSubmission : BVBaseUGCSubmission /** Create a new BVQuestionSubmission. - + @param productId The product ID that this question refers to. */ --(nonnull instancetype)initWithProductId:(nonnull NSString*)productId; --(nonnull instancetype) __unavailable init; - +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId; +- (nonnull instancetype)__unavailable init; /** - Submit this answer to the Bazaarvoice platform. If the `action` of this object is set to `BVSubmissionActionPreview` then the submission will NOT actually take place. - - A submission can fail for many reasons, and is dependent on your submission configuration. - - @param success The success block is called when a successful submission occurs. - @param failure The failure block is called when an unsuccessful submission occurs. This could be for a number of reasons: network failures, submission parameters invalid, or server errors occur. + Submit this answer to the Bazaarvoice platform. If the `action` of this object + is set to `BVSubmissionActionPreview` then the submission will NOT actually + take place. + + A submission can fail for many reasons, and is dependent on your submission + configuration. + + @param success The success block is called when a successful submission + occurs. + @param failure The failure block is called when an unsuccessful submission + occurs. This could be for a number of reasons: network failures, submission + parameters invalid, or server errors occur. */ --(void)submit:(nonnull QuestionSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; +- (void)submit:(nonnull QuestionSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure; -@property NSString* _Nullable questionSummary; -@property NSString* _Nullable questionDetails; +@property(nullable) NSString *questionSummary; +@property(nullable) NSString *questionDetails; -@property NSNumber* _Nullable isUserAnonymous; +@property(nullable) NSNumber *isUserAnonymous; -@property NSNumber* _Nullable sendEmailAlertWhenAnswered; +@property(nullable) NSNumber *sendEmailAlertWhenAnswered; -@property (readonly) NSString* _Nonnull productId; +@property(nonnull, readonly) NSString *productId; @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmission.m b/Pod/BVConversations/Submission/Question/BVQuestionSubmission.m index 2bf9bdf3..daa60f0d 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmission.m +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmission.m @@ -7,240 +7,283 @@ #import "BVQuestionSubmission.h" #import "BVQuestionSubmissionErrorResponse.h" -#import "BVSDKManager.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" -@interface BVQuestionSubmission() +@interface BVQuestionSubmission () -@property (readwrite) NSString* _Nonnull productId; +@property(nonnull, readwrite) NSString *productId; @property bool failureCalled; @end @implementation BVQuestionSubmission --(nonnull instancetype)initWithProductId:(nonnull NSString*)productId { - self = [super init]; - if(self){ - self.productId = productId; - } - return self; +- (nonnull instancetype)initWithProductId:(nonnull NSString *)productId { + self = [super init]; + if (self) { + self.productId = productId; + } + return self; } --(void)submit:(nonnull QuestionSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - if (self.action == BVSubmissionActionPreview) { - [[BVLogger sharedLogger] warning:@"Submitting a 'BVQuestionSubmission' with action set to `BVSubmissionActionPreview` will not actially submit the question! Set to `BVSubmissionActionSubmit` for real submission."]; - [self submitPreview:success failure:failure]; +- (void)submit:(nonnull QuestionSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.action == BVSubmissionActionPreview) { + [[BVLogger sharedLogger] + warning:@"Submitting a 'BVQuestionSubmission' with action set to " + @"`BVSubmissionActionPreview` will not actially submit " + @"the question! Set to `BVSubmissionActionSubmit` for " + @"real submission."]; + [self submitPreview:success failure:failure]; + } else { + [self submitPreview:^(BVQuestionSubmissionResponse *__nonnull response) { + [self submitForReal:success failure:failure]; } - else { - [self submitPreview:^(BVQuestionSubmissionResponse * _Nonnull response) { - [self submitForReal:success failure:failure]; - } failure:^(NSArray * _Nonnull errors) { - [self sendErrors:errors failureCallback:failure]; + failure:^(NSArray *__nonnull errors) { + [self sendErrors:errors failureCallback:failure]; }]; - } + } } --(void)submitPreview:(QuestionSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - [self submitQuestionWithPhotoUrls:BVSubmissionActionPreview - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - +- (void)submitPreview:(QuestionSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + [self submitQuestionWithPhotoUrls:BVSubmissionActionPreview + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; } --(void)submitForReal:(QuestionSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - if ([self.photos count] == 0) { - [self submitQuestionWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - return; - } - - // upload photos before submitting content - NSMutableArray* photoUrls = [NSMutableArray array]; - NSMutableArray* photoCaptions = [NSMutableArray array]; - - for (BVUploadablePhoto* photo in self.photos) { - - [photo uploadForContentType:BVPhotoContentTypeQuestion success:^(NSString * _Nonnull photoUrl) { - - // Queue one event for each photo uploaded. - BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNamePhoto - withAdditionalParams:nil]; - [BVPixel trackEvent:photoUploadEvent]; - - [photoUrls addObject:photoUrl]; - [photoCaptions addObject:photo.photoCaption]; - - // all photos uploaded! submit content - if ([photoUrls count] == [self.photos count]) { - [self submitQuestionWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:photoUrls - photoCaptions:photoCaptions - success:success - failure:failure]; - } - - } failure:^(NSArray * _Nonnull errors) { - - if (!self.failureCalled) { - self.failureCalled = true; // only call failure block once, if multiple photos failed. - - [self sendErrors:errors failureCallback:failure]; - } - +- (void)submitForReal:(QuestionSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + if ([self.photos count] == 0) { + [self submitQuestionWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; + return; + } + + // upload photos before submitting content + NSMutableArray *photoUrls = [NSMutableArray array]; + NSMutableArray *photoCaptions = [NSMutableArray array]; + + for (BVUploadablePhoto *photo in self.photos) { + [photo uploadForContentType:BVPhotoContentTypeQuestion + success:^(NSString *__nonnull photoUrl) { + + // Queue one event for each photo uploaded. + BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNamePhoto + withAdditionalParams:nil]; + [BVPixel trackEvent:photoUploadEvent]; + + [photoUrls addObject:photoUrl]; + [photoCaptions addObject:photo.photoCaption]; + + // all photos uploaded! submit content + if ([photoUrls count] == [self.photos count]) { + [self submitQuestionWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:photoUrls + photoCaptions:photoCaptions + success:success + failure:failure]; + } + + } + failure:^(NSArray *__nonnull errors) { + + if (!self.failureCalled) { + self.failureCalled = true; // only call failure block once, if + // multiple photos failed. + + [self sendErrors:errors failureCallback:failure]; + } + }]; - - } - + } } --(void)submitQuestionWithPhotoUrls:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions success:(nonnull QuestionSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - - NSDictionary* parameters = [self createSubmissionParameters:action photoUrls:photoUrls photoCaptions:photoCaptions]; - NSData* postBody = [self transformToPostBody:parameters]; - - NSString* urlString = [NSString stringWithFormat:@"%@submitquestion.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *httpError) { - - @try { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. +- (void) +submitQuestionWithPhotoUrls:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions + success:(nonnull QuestionSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + NSDictionary *parameters = [self createSubmissionParameters:action + photoUrls:photoUrls + photoCaptions:photoCaptions]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@submitquestion.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - BVQuestionSubmissionErrorResponse* errorResponse = [[BVQuestionSubmissionErrorResponse alloc] initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVQuestionSubmissionErrorResponse *errorResponse = + [[BVQuestionSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (httpError) { - // network error was generated - [self sendError:httpError failureCallback:failure]; - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Question upload failed."}]; - [self sendError:statusError failureCallback:failure]; - } - else if (jsonParsingError) { - // json parsing failed - [self sendError:jsonParsingError failureCallback:failure]; - } - else if(errorResponse){ - // api returned successfully, but has bazaarvoice-specific errors. Example: 'invalid api key' - [self sendErrors:[errorResponse toNSErrors] failureCallback:failure]; - } - else { - // success! - - // Fire event now that we've confirmed the question was successfully uploaded. - BVFeatureUsedEvent *writeQuestionEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameAskQuestion - withAdditionalParams:nil]; - - [BVPixel trackEvent:writeQuestionEvent]; - - BVQuestionSubmissionResponse* response = [[BVQuestionSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); + // network error was generated + [self sendError:httpError failureCallback:failure]; + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Question upload failed." + }]; + [self sendError:statusError failureCallback:failure]; + } else if (jsonParsingError) { + // json parsing failed + [self sendError:jsonParsingError failureCallback:failure]; + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + [self sendErrors:[errorResponse toNSErrors] + failureCallback:failure]; + } else { + // success! + + // Fire event now that we've confirmed the question was + // successfully uploaded. + BVFeatureUsedEvent *writeQuestionEvent = [ + [BVFeatureUsedEvent alloc] + initWithProductId:self.productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameAskQuestion + withAdditionalParams:nil]; + + [BVPixel trackEvent:writeQuestionEvent]; + + BVQuestionSubmissionResponse *response = + [[BVQuestionSubmissionResponse alloc] + initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); } - - } - @catch (NSException *exception) { - NSError* unexpectedError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; + + } @catch (NSException *exception) { + NSError *unexpectedError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; [self sendError:unexpectedError failureCallback:failure]; - } - - }]; - - // start uploading question - [postDataTask resume]; - -} + } --(nonnull NSDictionary*)createSubmissionParameters:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion": @"5.4", - @"productId": self.productId - }]; - - parameters[@"passkey"] = [BVSDKManager sharedManager].configuration.apiKeyConversations; - parameters[@"action"] = [BVSubmissionActionUtil toString:action]; - - parameters[@"questionsummary"] = self.questionSummary; - parameters[@"questiondetails"] = self.questionDetails; - - parameters[@"campaignid"] = self.campaignId; - parameters[@"locale"] = self.locale; - parameters[@"hostedauthentication_authenticationemail"] = self.hostedAuthenticationEmail; - parameters[@"hostedauthentication_callbackurl"] = self.hostedAuthenticationCallback; - parameters[@"fp"] = self.fingerPrint; - parameters[@"user"] = self.user; - parameters[@"usernickname"] = self.userNickname; - parameters[@"useremail"] = self.userEmail; - parameters[@"userid"] = self.userId; - parameters[@"userlocation"] = self.userLocation; - - if (self.isUserAnonymous) { - parameters[@"isuseranonymous"] = [self.isUserAnonymous boolValue] ? @"true" : @"false"; - } - - if (self.sendEmailAlertWhenPublished) { - parameters[@"sendemailalertwhenpublished"] = [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; - } - - if (self.agreedToTermsAndConditions) { - parameters[@"agreedtotermsandconditions"] = [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; - } - - int photoIndex = 0; - for(NSString* url in photoUrls) { - NSString* key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; - parameters[key] = url; - photoIndex += 1; - } - - int captionIndex = 0; - for(NSString* caption in photoCaptions) { - NSString* key = [NSString stringWithFormat:@"photocaption_%i", captionIndex]; - parameters[key] = caption; - captionIndex += 1; - } - - for (BVStringKeyValuePair* keyValuePair in self.customFormPairs) { - NSString* key = [keyValuePair key]; - NSString* value = [keyValuePair value]; - parameters[key] = value; - } - - return parameters; - + }]; + + // start uploading question + [postDataTask resume]; } +- (nonnull NSDictionary *) +createSubmissionParameters:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"productId" : self.productId + }]; + + parameters[@"passkey"] = + [BVSDKManager sharedManager].configuration.apiKeyConversations; + parameters[@"action"] = [BVSubmissionActionUtil toString:action]; + + parameters[@"questionsummary"] = self.questionSummary; + parameters[@"questiondetails"] = self.questionDetails; + + parameters[@"campaignid"] = self.campaignId; + parameters[@"locale"] = self.locale; + parameters[@"hostedauthentication_authenticationemail"] = + self.hostedAuthenticationEmail; + parameters[@"hostedauthentication_callbackurl"] = + self.hostedAuthenticationCallback; + parameters[@"fp"] = self.fingerPrint; + parameters[@"user"] = self.user; + parameters[@"usernickname"] = self.userNickname; + parameters[@"useremail"] = self.userEmail; + parameters[@"userid"] = self.userId; + parameters[@"userlocation"] = self.userLocation; + if (self.isUserAnonymous) { + parameters[@"isuseranonymous"] = + [self.isUserAnonymous boolValue] ? @"true" : @"false"; + } + + if (self.sendEmailAlertWhenPublished) { + parameters[@"sendemailalertwhenpublished"] = + [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; + } + + if (self.agreedToTermsAndConditions) { + parameters[@"agreedtotermsandconditions"] = + [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; + } + + int photoIndex = 0; + for (NSString *url in photoUrls) { + NSString *key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; + parameters[key] = url; + photoIndex += 1; + } + + int captionIndex = 0; + for (NSString *caption in photoCaptions) { + NSString *key = + [NSString stringWithFormat:@"photocaption_%i", captionIndex]; + parameters[key] = caption; + captionIndex += 1; + } + + for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { + NSString *key = [keyValuePair key]; + NSString *value = [keyValuePair value]; + parameters[key] = value; + } + + return parameters; +} @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.h b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.h index 9f12d75a..7ae0cd6a 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionErrorResponse.h" #import "BVSubmittedQuestion.h" +#import /// Failed question submission response. @interface BVQuestionSubmissionErrorResponse : BVSubmissionErrorResponse -@property BVSubmittedQuestion* _Nullable question; +@property(nullable) BVSubmittedQuestion *question; @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.m b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.m index 38e59ce7..9db12501 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionErrorResponse.m @@ -9,16 +9,19 @@ @implementation BVQuestionSubmissionErrorResponse --(instancetype)initWithApiResponse:(nullable id)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - NSDictionary* apiObject = apiResponse; // [super initWithApiResponse:] checks that this is nonnull and a dictionary - self.question = [[BVSubmittedQuestion alloc] initWithApiResponse:apiObject[@"Question"]]; - } - - return self; +- (instancetype)initWithApiResponse:(nullable id)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + + if (self) { + NSDictionary *apiObject = apiResponse; // [super initWithApiResponse:] + // checks that this is nonnull and a + // dictionary + self.question = [[BVSubmittedQuestion alloc] + initWithApiResponse:apiObject[@"Question"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.h b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.h index 5a41f797..05457741 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmittedQuestion.h" #import "BVSubmissionResponse.h" +#import "BVSubmittedQuestion.h" +#import /// Successful question submission response. @interface BVQuestionSubmissionResponse : BVSubmissionResponse -@property BVSubmittedQuestion* _Nullable question; +@property(nullable) BVSubmittedQuestion *question; @end diff --git a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.m b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.m index 3007f1e5..e30d29b5 100644 --- a/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Question/BVQuestionSubmissionResponse.m @@ -9,15 +9,16 @@ @implementation BVQuestionSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - self.question = [[BVSubmittedQuestion alloc] initWithApiResponse:apiResponse[@"Question"]]; - } - - return self; +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + + if (self) { + self.question = [[BVSubmittedQuestion alloc] + initWithApiResponse:apiResponse[@"Question"]]; + } + + return self; } @end diff --git a/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.h b/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.h index ee29a2fe..ae24c5b3 100644 --- a/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.h +++ b/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.h @@ -10,17 +10,17 @@ /// A successfully submitted question. @interface BVSubmittedQuestion : NSObject -@property NSString* _Nullable questionSummary; -@property NSString* _Nullable questionDetails; -@property NSString* _Nullable questionId; -@property NSString* _Nullable submissionId; +@property(nullable) NSString *questionSummary; +@property(nullable) NSString *questionDetails; +@property(nullable) NSString *questionId; +@property(nullable) NSString *submissionId; -@property NSNumber* _Nullable sendEmailAlertWhenAnswered; -@property NSNumber* _Nullable sendEmailAlertWhenPublished; -@property NSNumber* _Nullable typicalHoursToPost; +@property(nullable) NSNumber *sendEmailAlertWhenAnswered; +@property(nullable) NSNumber *sendEmailAlertWhenPublished; +@property(nullable) NSNumber *typicalHoursToPost; -@property NSDate* _Nullable submissionTime; +@property(nullable) NSDate *submissionTime; --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.m b/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.m index 4f0bfcdb..e81196a2 100644 --- a/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.m +++ b/Pod/BVConversations/Submission/Question/BVSubmittedQuestion.m @@ -11,30 +11,32 @@ @implementation BVSubmittedQuestion --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.questionSummary, apiObject[@"QuestionSummary"]) - SET_IF_NOT_NULL(self.questionDetails, apiObject[@"QuestionDetails"]) - SET_IF_NOT_NULL(self.questionId, apiObject[@"QuestionId"]) - SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) - - SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) - SET_IF_NOT_NULL(self.sendEmailAlertWhenAnswered, apiObject[@"SendEmailAlertWhenAnswered"]) - SET_IF_NOT_NULL(self.sendEmailAlertWhenPublished, apiObject[@"SendEmailAlertWhenPublished"]) - - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; - +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; -} + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.questionSummary, apiObject[@"QuestionSummary"]) + SET_IF_NOT_NULL(self.questionDetails, apiObject[@"QuestionDetails"]) + SET_IF_NOT_NULL(self.questionId, apiObject[@"QuestionId"]) + SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) + + SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) + SET_IF_NOT_NULL(self.sendEmailAlertWhenAnswered, + apiObject[@"SendEmailAlertWhenAnswered"]) + SET_IF_NOT_NULL(self.sendEmailAlertWhenPublished, + apiObject[@"SendEmailAlertWhenPublished"]) + + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; + } + return self; +} @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmission.h b/Pod/BVConversations/Submission/Review/BVReviewSubmission.h index cfe2dfd2..8e40b108 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmission.h +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmission.h @@ -5,77 +5,99 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVReviewSubmissionResponse.h" +#import "BVBaseUGCSubmission.h" #import "BVConversationsRequest.h" +#import "BVReviewSubmissionResponse.h" #import "BVUploadablePhoto.h" -#import "BVBaseUGCSubmission.h" - -typedef void (^ReviewSubmissionCompletion)(BVReviewSubmissionResponse* _Nonnull response); +#import +typedef void (^ReviewSubmissionCompletion)( + BVReviewSubmissionResponse *__nonnull response); /** Class to use to submit a review to the Bazaarvoice platform. - - Of the many parameters possible on a BVReviewSubmission, the ones needed for submission depend on your specific implementation. - + + Of the many parameters possible on a BVReviewSubmission, the ones needed for + submission depend on your specific implementation. + For a description of possible fields see our API documentation at: https://developer.bazaarvoice.com/docs/read/conversations/reviews/submit/5_4 - + @availability 4.1.0 and later */ @interface BVReviewSubmission : BVBaseUGCSubmission /** Create a new BVReviewSubmission. - + @param reviewTitle The user-provided title of the review. @param reviewText The user-provided body of the review. @param rating The user-provided rating: 1-5. @param productId The productId that this review is associated with. */ --(nonnull instancetype)initWithReviewTitle:(nonnull NSString*)reviewTitle reviewText:(nonnull NSString*)reviewText rating:(NSUInteger)rating productId:(nonnull NSString*)productId; --(nonnull instancetype) __unavailable init; - +- (nonnull instancetype)initWithReviewTitle:(nonnull NSString *)reviewTitle + reviewText:(nonnull NSString *)reviewText + rating:(NSUInteger)rating + productId:(nonnull NSString *)productId; +- (nonnull instancetype)__unavailable init; /** - Submit this answer to the Bazaarvoice platform. If the `action` of this object is set to `BVSubmissionActionPreview` then the submission will NOT actually take place. - - A submission can fail for many reasons, and is dependent on your submission configuration. - - @param success The success block is called when a successful submission occurs. - @param failure The failure block is called when an unsuccessful submission occurs. This could be for a number of reasons: network failures, submission parameters invalid, or server errors occur. - */ --(void)submit:(nonnull ReviewSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure; + Submit this answer to the Bazaarvoice platform. If the `action` of this object + is set to `BVSubmissionActionPreview` then the submission will NOT actually + take place. + A submission can fail for many reasons, and is dependent on your submission + configuration. -/// Value is text representing a user comment to explain numerical Net Promoter score. -@property NSString* _Nullable netPromoterComment; + @param success The success block is called when a successful submission + occurs. + @param failure The failure block is called when an unsuccessful submission + occurs. This could be for a number of reasons: network failures, submission + parameters invalid, or server errors occur. + */ +- (void)submit:(nonnull ReviewSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure; + +/// Value is text representing a user comment to explain numerical Net Promoter +/// score. +@property(nullable) NSString *netPromoterComment; -/// Value is positive integer between 1 and 10 representing a numerical rating in response to “How would you rate this company?” -@property NSNumber* _Nullable netPromoterScore; +/// Value is positive integer between 1 and 10 representing a numerical rating +/// in response to “How would you rate this company?” +@property(nullable) NSNumber *netPromoterScore; -/// Value is true or false; default is null – "true" or "false" answer to "I would recommend this to a friend". Required dependent on client settings. -@property NSNumber* _Nullable isRecommended; +/// Value is true or false; default is null – "true" or "false" answer to "I +/// would recommend this to a friend". Required dependent on client settings. +@property(nullable) NSNumber *isRecommended; /// The product id used to filter the review on. -@property (readonly) NSString* _Nonnull productId; +@property(nonnull, readonly) NSString *productId; --(void)addAdditionalField:(nonnull NSString*)fieldName value:(nonnull NSString*)value; --(void)addContextDataValueString:(nonnull NSString*)contextDataValueName value:(nonnull NSString*)value; --(void)addContextDataValueBool:(nonnull NSString*)contextDataValueName value:(bool)value; --(void)addRatingQuestion:(nonnull NSString*)ratingQuestionName value:(int)value; --(void)addRatingSlider:(nonnull NSString*)ratingQuestionName value:(nonnull NSString*)value; --(void)addPredefinedTagDimension:(nonnull NSString*)tagQuestionId tagId:(nonnull NSString*)tagId value:(nonnull NSString*)value; --(void)addFreeformTagDimension:(nonnull NSString*)tagQuestionId tagNumber:(int)tagNumber value:(nonnull NSString*)value; -- (NSString * _Nonnull)getPasskey; +- (void)addAdditionalField:(nonnull NSString *)fieldName + value:(nonnull NSString *)value; +- (void)addContextDataValueString:(nonnull NSString *)contextDataValueName + value:(nonnull NSString *)value; +- (void)addContextDataValueBool:(nonnull NSString *)contextDataValueName + value:(bool)value; +- (void)addRatingQuestion:(nonnull NSString *)ratingQuestionName + value:(int)value; +- (void)addRatingSlider:(nonnull NSString *)ratingQuestionName + value:(nonnull NSString *)value; +- (void)addPredefinedTagDimension:(nonnull NSString *)tagQuestionId + tagId:(nonnull NSString *)tagId + value:(nonnull NSString *)value; +- (void)addFreeformTagDimension:(nonnull NSString *)tagQuestionId + tagNumber:(int)tagNumber + value:(nonnull NSString *)value; +- (nonnull NSString *)getPasskey; /** Submit a Youtube video link with UGC - + @param url The full URL string of the Youtube video @param videoCaption The use-provided caption for the video */ --(void)addVideoURL:(nonnull NSString*)url withCaption:(nullable NSString*)videoCaption; - +- (void)addVideoURL:(nonnull NSString *)url + withCaption:(nullable NSString *)videoCaption; + @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmission.m b/Pod/BVConversations/Submission/Review/BVReviewSubmission.m index 21b2d72b..a5b0565b 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmission.m +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmission.m @@ -7,382 +7,437 @@ #import "BVReviewSubmission.h" #import "BVReviewSubmissionErrorResponse.h" -#import "BVSDKManager.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" + +@interface BVReviewSubmission () + +@property NSUInteger rating; +@property(nonnull) NSString *reviewText; +@property(nonnull) NSString *reviewTitle; +@property(nonnull, readwrite) NSString *productId; -@interface BVReviewSubmission() - - @property NSUInteger rating; - @property NSString* _Nonnull reviewText; - @property NSString* _Nonnull reviewTitle; - @property (readwrite) NSString* _Nonnull productId; - - @property NSMutableDictionary* _Nonnull additionalFields; - @property NSMutableDictionary* _Nonnull contextDataValues; - @property NSMutableDictionary* _Nonnull ratingQuestions; - @property NSMutableDictionary* _Nonnull ratingSliders; - @property NSMutableDictionary* _Nonnull predefinedTags; - @property NSMutableDictionary* _Nonnull freeformTags; - - @property NSMutableArray* _Nonnull videos; - - @property BOOL failureCalled; - @end +@property(nonnull) NSMutableDictionary *additionalFields; +@property(nonnull) NSMutableDictionary *contextDataValues; +@property(nonnull) NSMutableDictionary *ratingQuestions; +@property(nonnull) NSMutableDictionary *ratingSliders; +@property(nonnull) NSMutableDictionary *predefinedTags; +@property(nonnull) NSMutableDictionary *freeformTags; + +@property(nonnull) NSMutableArray *videos; + +@property BOOL failureCalled; +@end @implementation BVReviewSubmission - --(nonnull instancetype)initWithReviewTitle:(nonnull NSString*)reviewTitle reviewText:(nonnull NSString*)reviewText rating:(NSUInteger)rating productId:(nonnull NSString*)productId { - self = [super init]; - if(self){ - self.reviewTitle = reviewTitle; - self.reviewText = reviewText; - self.rating = rating; - self.productId = productId; - - self.additionalFields = [NSMutableDictionary dictionary]; - self.contextDataValues = [NSMutableDictionary dictionary]; - self.ratingQuestions = [NSMutableDictionary dictionary]; - self.ratingSliders = [NSMutableDictionary dictionary]; - self.predefinedTags = [NSMutableDictionary dictionary]; - self.freeformTags = [NSMutableDictionary dictionary]; - self.videos = [NSMutableArray array]; - } - return self; + +- (nonnull instancetype)initWithReviewTitle:(nonnull NSString *)reviewTitle + reviewText:(nonnull NSString *)reviewText + rating:(NSUInteger)rating + productId:(nonnull NSString *)productId { + self = [super init]; + if (self) { + self.reviewTitle = reviewTitle; + self.reviewText = reviewText; + self.rating = rating; + self.productId = productId; + + self.additionalFields = [NSMutableDictionary dictionary]; + self.contextDataValues = [NSMutableDictionary dictionary]; + self.ratingQuestions = [NSMutableDictionary dictionary]; + self.ratingSliders = [NSMutableDictionary dictionary]; + self.predefinedTags = [NSMutableDictionary dictionary]; + self.freeformTags = [NSMutableDictionary dictionary]; + self.videos = [NSMutableArray array]; + } + return self; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#additional-field --(void)addAdditionalField:(nonnull NSString*)fieldName value:(nonnull NSString*)value { - NSString* key = [NSString stringWithFormat:@"additionalfield_%@", fieldName]; - self.additionalFields[key] = value; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#additional-field +- (void)addAdditionalField:(nonnull NSString *)fieldName + value:(nonnull NSString *)value { + NSString *key = [NSString stringWithFormat:@"additionalfield_%@", fieldName]; + self.additionalFields[key] = value; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#context-data-question --(void)addContextDataValueString:(nonnull NSString*)contextDataValueName value:(nonnull NSString*)value { - NSString* key = [NSString stringWithFormat:@"contextdatavalue_%@", contextDataValueName]; - self.contextDataValues[key] = value; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#context-data-question +- (void)addContextDataValueString:(nonnull NSString *)contextDataValueName + value:(nonnull NSString *)value { + NSString *key = + [NSString stringWithFormat:@"contextdatavalue_%@", contextDataValueName]; + self.contextDataValues[key] = value; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#context-data-question --(void)addContextDataValueBool:(nonnull NSString*)contextDataValueName value:(bool)value { - NSString* key = [NSString stringWithFormat:@"contextdatavalue_%@", contextDataValueName]; - self.contextDataValues[key] = value ? @"true" : @"false"; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#context-data-question +- (void)addContextDataValueBool:(nonnull NSString *)contextDataValueName + value:(bool)value { + NSString *key = + [NSString stringWithFormat:@"contextdatavalue_%@", contextDataValueName]; + self.contextDataValues[key] = value ? @"true" : @"false"; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#rating-question---normal --(void)addRatingQuestion:(nonnull NSString*)ratingQuestionName value:(int)value { - NSString* key = [NSString stringWithFormat:@"rating_%@", ratingQuestionName]; - NSString* valueAsString = [NSString stringWithFormat:@"%i", value]; - self.ratingQuestions[key] = valueAsString; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#rating-question---normal +- (void)addRatingQuestion:(nonnull NSString *)ratingQuestionName + value:(int)value { + NSString *key = [NSString stringWithFormat:@"rating_%@", ratingQuestionName]; + NSString *valueAsString = [NSString stringWithFormat:@"%i", value]; + self.ratingQuestions[key] = valueAsString; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#rating-question---slider --(void)addRatingSlider:(nonnull NSString*)ratingQuestionName value:(nonnull NSString*)value { - NSString* key = [NSString stringWithFormat:@"rating_%@", ratingQuestionName]; - self.ratingSliders[key] = value; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#rating-question---slider +- (void)addRatingSlider:(nonnull NSString *)ratingQuestionName + value:(nonnull NSString *)value { + NSString *key = [NSString stringWithFormat:@"rating_%@", ratingQuestionName]; + self.ratingSliders[key] = value; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#tags---tag-dimensions --(void)addPredefinedTagDimension:(nonnull NSString*)tagQuestionId tagId:(nonnull NSString*)tagId value:(nonnull NSString*)value { - NSString* key = [NSString stringWithFormat:@"tagid_%@/%@", tagQuestionId, tagId]; - self.predefinedTags[key] = value; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#tags---tag-dimensions +- (void)addPredefinedTagDimension:(nonnull NSString *)tagQuestionId + tagId:(nonnull NSString *)tagId + value:(nonnull NSString *)value { + NSString *key = + [NSString stringWithFormat:@"tagid_%@/%@", tagQuestionId, tagId]; + self.predefinedTags[key] = value; } - - - /// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#tags---tag-dimensions --(void)addFreeformTagDimension:(nonnull NSString*)tagQuestionId tagNumber:(int)tagNumber value:(nonnull NSString*)value { - NSString* key = [NSString stringWithFormat:@"tag_%@_%i", tagQuestionId, tagNumber]; - self.freeformTags[key] = value; + +/// https://developer.bazaarvoice.com/apis/conversations/tutorials/field_types#tags---tag-dimensions +- (void)addFreeformTagDimension:(nonnull NSString *)tagQuestionId + tagNumber:(int)tagNumber + value:(nonnull NSString *)value { + NSString *key = + [NSString stringWithFormat:@"tag_%@_%i", tagQuestionId, tagNumber]; + self.freeformTags[key] = value; } - - --(void)submit:(nonnull ReviewSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - if (self.action == BVSubmissionActionPreview) { - [[BVLogger sharedLogger] warning:@"Submitting a 'BVReviewSubmission' with action set to `BVSubmissionActionPreview` will not actially submit the review! Set to `BVSubmissionActionSubmit` for real submission."]; - [self submitPreview:success failure:failure]; + +- (void)submit:(nonnull ReviewSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + if (self.action == BVSubmissionActionPreview) { + [[BVLogger sharedLogger] + warning:@"Submitting a 'BVReviewSubmission' with action set to " + @"`BVSubmissionActionPreview` will not actially submit " + @"the review! Set to `BVSubmissionActionSubmit` for real " + @"submission."]; + [self submitPreview:success failure:failure]; + } else { + [self submitPreview:^(BVReviewSubmissionResponse *__nonnull response) { + //[BVConversationsAnalyticsUtil + // queueAnalyticsEventForReviewSubmission:self]; + [self submitForReal:success failure:failure]; } - else { - [self submitPreview:^(BVReviewSubmissionResponse * _Nonnull response) { - //[BVConversationsAnalyticsUtil queueAnalyticsEventForReviewSubmission:self]; - [self submitForReal:success failure:failure]; - } failure:^(NSArray * _Nonnull errors) { - [[BVLogger sharedLogger] printErrors:errors]; - failure(errors); + failure:^(NSArray *__nonnull errors) { + [[BVLogger sharedLogger] printErrors:errors]; + failure(errors); }]; - } + } +} + +- (void)submitPreview:(ReviewSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + [self submitReviewWithPhotoUrls:BVSubmissionActionPreview + photoUrls:@[] + photoCaptions:@[] + success:success + failure:failure]; } - --(void)submitPreview:(ReviewSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - [self submitReviewWithPhotoUrls:BVSubmissionActionPreview + +- (void)submitForReal:(ReviewSubmissionCompletion)success + failure:(ConversationsFailureHandler)failure { + if ([self.photos count] == 0) { + [self submitReviewWithPhotoUrls:BVSubmissionActionSubmit photoUrls:@[] photoCaptions:@[] success:success failure:failure]; - -} - --(void)submitForReal:(ReviewSubmissionCompletion)success failure:(ConversationsFailureHandler)failure { - - if ([self.photos count] == 0) { - [self submitReviewWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:@[] - photoCaptions:@[] - success:success - failure:failure]; - return; - } - - // upload photos before submitting content - NSMutableArray* photoUrls = [NSMutableArray array]; - NSMutableArray* photoCaptions = [NSMutableArray array]; - - for (BVUploadablePhoto* photo in self.photos) { - - [photo uploadForContentType:BVPhotoContentTypeReview success:^(NSString * _Nonnull photoUrl) { - - // Queue one event for each photo uploaded. - BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNamePhoto - withAdditionalParams:nil]; - - [BVPixel trackEvent:photoUploadEvent]; - - [photoUrls addObject:photoUrl]; - [photoCaptions addObject:photo.photoCaption]; - - // all photos uploaded! submit content - if ([photoUrls count] == [self.photos count]) { - [self submitReviewWithPhotoUrls:BVSubmissionActionSubmit - photoUrls:photoUrls - photoCaptions:photoCaptions - success:success - failure:failure]; - } - - } failure:^(NSArray * _Nonnull errors) { - if (!self.failureCalled) { - self.failureCalled = true; // only call failure block once, if multiple photos failed. - [[BVLogger sharedLogger] printErrors:errors]; - failure(errors); - } - + return; + } + + // upload photos before submitting content + NSMutableArray *photoUrls = [NSMutableArray array]; + NSMutableArray *photoCaptions = [NSMutableArray array]; + + for (BVUploadablePhoto *photo in self.photos) { + [photo uploadForContentType:BVPhotoContentTypeReview + success:^(NSString *__nonnull photoUrl) { + + // Queue one event for each photo uploaded. + BVFeatureUsedEvent *photoUploadEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNamePhoto + withAdditionalParams:nil]; + + [BVPixel trackEvent:photoUploadEvent]; + + [photoUrls addObject:photoUrl]; + [photoCaptions addObject:photo.photoCaption]; + + // all photos uploaded! submit content + if ([photoUrls count] == [self.photos count]) { + [self submitReviewWithPhotoUrls:BVSubmissionActionSubmit + photoUrls:photoUrls + photoCaptions:photoCaptions + success:success + failure:failure]; + } + + } + failure:^(NSArray *__nonnull errors) { + if (!self.failureCalled) { + self.failureCalled = true; // only call failure block once, if + // multiple photos failed. + [[BVLogger sharedLogger] printErrors:errors]; + failure(errors); + } + }]; - - } - + } } - - --(void)submitReviewWithPhotoUrls:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions success:(nonnull ReviewSubmissionCompletion)success failure:(nonnull ConversationsFailureHandler)failure { - - - NSDictionary* parameters = [self createSubmissionParameters:action photoUrls:photoUrls photoCaptions:photoCaptions]; - NSData* postBody = [self transformToPostBody:parameters]; - - NSString* urlString = [NSString stringWithFormat:@"%@submitreview.json", [BVConversationsRequest commonEndpoint]]; - NSURL* url = [NSURL URLWithString:urlString]; - - NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - [request setHTTPBody:postBody]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, parameters]]; - - NSURLSession *session = [NSURLSession sharedSession]; - NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *httpError) { - - @try { - - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; // This dataTask is used with only HTTP requests. + +- (void)submitReviewWithPhotoUrls:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions + success:(nonnull ReviewSubmissionCompletion)success + failure:(nonnull ConversationsFailureHandler)failure { + NSDictionary *parameters = [self createSubmissionParameters:action + photoUrls:photoUrls + photoCaptions:photoCaptions]; + NSData *postBody = [self transformToPostBody:parameters]; + + NSString *urlString = + [NSString stringWithFormat:@"%@submitreview.json", + [BVConversationsRequest commonEndpoint]]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:postBody]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@\n with BODY: %@", urlString, + parameters]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *postDataTask = [session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *httpError) { + + @try { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) + response; // This dataTask is used with only HTTP requests. NSInteger statusCode = httpResponse.statusCode; - NSError* jsonParsingError; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonParsingError]; - BVReviewSubmissionErrorResponse* errorResponse = [[BVReviewSubmissionErrorResponse alloc] initWithApiResponse:json]; // fails gracefully - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, (long)statusCode]]; - + NSError *jsonParsingError; + NSDictionary *json = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&jsonParsingError]; + BVReviewSubmissionErrorResponse *errorResponse = + [[BVReviewSubmissionErrorResponse alloc] + initWithApiResponse:json]; // fails gracefully + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: %@ (%ld)", json, + (long)statusCode]]; + if (httpError) { - // network error was generated - [[BVLogger sharedLogger] printError:httpError]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(@[httpError]); - }); - } - else if(statusCode >= 300){ - // HTTP status code indicates failure - NSError* statusError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_NETWORK_FAILED userInfo:@{NSLocalizedDescriptionKey:@"Review upload failed."}]; - [[BVLogger sharedLogger] printError:statusError]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(@[statusError]); - }); - } - else if (jsonParsingError) { - // json parsing failed - [[BVLogger sharedLogger] printError:jsonParsingError]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(@[jsonParsingError]); - }); - } - else if(errorResponse){ - // api returned successfully, but has bazaarvoice-specific errors. Example: 'invalid api key' - NSArray* errors = [errorResponse toNSErrors]; - [[BVLogger sharedLogger] printErrors:errors]; - dispatch_async(dispatch_get_main_queue(), ^{ - failure(errors); - }); - } - else { - // success! - - // Fire event now that we've confirmed the event was successfully uploaded. - BVFeatureUsedEvent *writeReviewEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameWriteReview - withAdditionalParams:nil]; - - [BVPixel trackEvent:writeReviewEvent]; - - BVReviewSubmissionResponse* response = [[BVReviewSubmissionResponse alloc] initWithApiResponse:json]; - dispatch_async(dispatch_get_main_queue(), ^{ - success(response); - }); + // network error was generated + [[BVLogger sharedLogger] printError:httpError]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(@[ httpError ]); + }); + } else if (statusCode >= 300) { + // HTTP status code indicates failure + NSError *statusError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_NETWORK_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"Review upload failed." + }]; + [[BVLogger sharedLogger] printError:statusError]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(@[ statusError ]); + }); + } else if (jsonParsingError) { + // json parsing failed + [[BVLogger sharedLogger] printError:jsonParsingError]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(@[ jsonParsingError ]); + }); + } else if (errorResponse) { + // api returned successfully, but has bazaarvoice-specific + // errors. Example: 'invalid api key' + NSArray *errors = [errorResponse toNSErrors]; + [[BVLogger sharedLogger] printErrors:errors]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(errors); + }); + } else { + // success! + + // Fire event now that we've confirmed the event was + // successfully uploaded. + BVFeatureUsedEvent *writeReviewEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameWriteReview + withAdditionalParams:nil]; + + [BVPixel trackEvent:writeReviewEvent]; + + BVReviewSubmissionResponse *response = + [[BVReviewSubmissionResponse alloc] initWithApiResponse:json]; + dispatch_async(dispatch_get_main_queue(), ^{ + success(response); + }); } - - } - @catch (NSException *exception) { - NSError* unexpectedError = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_UNKNOWN userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; - [[BVLogger sharedLogger] printError: unexpectedError]; + + } @catch (NSException *exception) { + NSError *unexpectedError = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_UNKNOWN + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + [[BVLogger sharedLogger] printError:unexpectedError]; dispatch_async(dispatch_get_main_queue(), ^{ - failure(@[unexpectedError]); + failure(@[ unexpectedError ]); }); - } - - }]; - - // start uploading review - [postDataTask resume]; - + } + + }]; + + // start uploading review + [postDataTask resume]; } - --(nonnull NSDictionary*)createSubmissionParameters:(BVSubmissionAction)action photoUrls:(nonnull NSArray*)photoUrls photoCaptions:(nonnull NSArray*)photoCaptions { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionaryWithDictionary:@{ - @"apiversion": @"5.4", - @"reviewtext": self.reviewText, - @"title": self.reviewTitle, - @"rating": [NSString stringWithFormat:@"%lu", (unsigned long)self.rating], - @"productId": self.productId - }]; - - parameters[@"passkey"] = [self getPasskey]; - parameters[@"action"] = [BVSubmissionActionUtil toString:action]; - - parameters[@"campaignid"] = self.campaignId; - parameters[@"locale"] = self.locale; - - if (self.sendEmailAlertWhenCommented) { - parameters[@"sendemailalertwhencommented"] = [self.sendEmailAlertWhenCommented boolValue] ? @"true" : @"false"; - } - - if (self.sendEmailAlertWhenPublished) { - parameters[@"sendemailalertwhenpublished"] = [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; - } - - if (self.agreedToTermsAndConditions) { - parameters[@"agreedtotermsandconditions"] = [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; - } - - parameters[@"hostedauthentication_authenticationemail"] = self.hostedAuthenticationEmail; - parameters[@"hostedauthentication_callbackurl"] = self.hostedAuthenticationCallback; - parameters[@"netpromotercomment"] = self.netPromoterComment; - - if(self.netPromoterScore) { - parameters[@"netpromoterscore"] = [NSString stringWithFormat:@"%i", [self.netPromoterScore intValue]]; - } - - parameters[@"fp"] = self.fingerPrint; - - if (self.isRecommended != nil){ - parameters[@"isrecommended"] = [self.isRecommended boolValue] == YES ? @"true" : @"false"; - } - - parameters[@"user"] = self.user; - parameters[@"usernickname"] = self.userNickname; - parameters[@"useremail"] = self.userEmail; - parameters[@"userid"] = self.userId; - parameters[@"userlocation"] = self.userLocation; - - int photoIndex = 0; - for(NSString* url in photoUrls) { - NSString* key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; - parameters[key] = url; - photoIndex += 1; - } - - int captionIndex = 0; - for(NSString* caption in photoCaptions) { - NSString* key = [NSString stringWithFormat:@"photocaption_%i", captionIndex]; - parameters[key] = caption; - captionIndex += 1; - } - - int videoIndex = 1; - for(BVUploadableYouTubeVideo* video in self.videos) { - NSString* key = [NSString stringWithFormat:@"VideoUrl_%i", videoIndex]; - parameters[key] = video.videoURL; - - if (video.videoCaption){ - NSString* key = [NSString stringWithFormat:@"VideoCaption_%i", videoIndex]; - parameters[key] = video.videoCaption; - } - - videoIndex += 1; - } - - for (NSString* key in self.additionalFields) { - parameters[key] = self.additionalFields[key]; - } - for (NSString* key in self.contextDataValues) { - parameters[key] = self.contextDataValues[key]; - } - for (NSString* key in self.ratingQuestions) { - parameters[key] = self.ratingQuestions[key]; - } - for (NSString* key in self.ratingSliders) { - parameters[key] = self.ratingSliders[key]; - } - for (NSString* key in self.predefinedTags) { - parameters[key] = self.predefinedTags[key]; - } - for (NSString* key in self.freeformTags) { - parameters[key] = self.freeformTags[key]; - } - for (BVStringKeyValuePair* keyValuePair in self.customFormPairs) { - NSString* key = [keyValuePair key]; - NSString* value = [keyValuePair value]; - parameters[key] = value; + +- (nonnull NSDictionary *) +createSubmissionParameters:(BVSubmissionAction)action + photoUrls:(nonnull NSArray *)photoUrls + photoCaptions:(nonnull NSArray *)photoCaptions { + NSMutableDictionary *parameters = + [NSMutableDictionary dictionaryWithDictionary:@{ + @"apiversion" : @"5.4", + @"reviewtext" : self.reviewText, + @"title" : self.reviewTitle, + @"rating" : + [NSString stringWithFormat:@"%lu", (unsigned long)self.rating], + @"productId" : self.productId + }]; + + parameters[@"passkey"] = [self getPasskey]; + parameters[@"action"] = [BVSubmissionActionUtil toString:action]; + + parameters[@"campaignid"] = self.campaignId; + parameters[@"locale"] = self.locale; + + if (self.sendEmailAlertWhenCommented) { + parameters[@"sendemailalertwhencommented"] = + [self.sendEmailAlertWhenCommented boolValue] ? @"true" : @"false"; + } + + if (self.sendEmailAlertWhenPublished) { + parameters[@"sendemailalertwhenpublished"] = + [self.sendEmailAlertWhenPublished boolValue] ? @"true" : @"false"; + } + + if (self.agreedToTermsAndConditions) { + parameters[@"agreedtotermsandconditions"] = + [self.agreedToTermsAndConditions boolValue] ? @"true" : @"false"; + } + + parameters[@"hostedauthentication_authenticationemail"] = + self.hostedAuthenticationEmail; + parameters[@"hostedauthentication_callbackurl"] = + self.hostedAuthenticationCallback; + parameters[@"netpromotercomment"] = self.netPromoterComment; + + if (self.netPromoterScore) { + parameters[@"netpromoterscore"] = + [NSString stringWithFormat:@"%i", [self.netPromoterScore intValue]]; + } + + parameters[@"fp"] = self.fingerPrint; + + if (self.isRecommended != nil) { + parameters[@"isrecommended"] = + [self.isRecommended boolValue] == YES ? @"true" : @"false"; + } + + parameters[@"user"] = self.user; + parameters[@"usernickname"] = self.userNickname; + parameters[@"useremail"] = self.userEmail; + parameters[@"userid"] = self.userId; + parameters[@"userlocation"] = self.userLocation; + + int photoIndex = 0; + for (NSString *url in photoUrls) { + NSString *key = [NSString stringWithFormat:@"photourl_%i", photoIndex]; + parameters[key] = url; + photoIndex += 1; + } + + int captionIndex = 0; + for (NSString *caption in photoCaptions) { + NSString *key = + [NSString stringWithFormat:@"photocaption_%i", captionIndex]; + parameters[key] = caption; + captionIndex += 1; + } + + int videoIndex = 1; + for (BVUploadableYouTubeVideo *video in self.videos) { + NSString *key = [NSString stringWithFormat:@"VideoUrl_%i", videoIndex]; + parameters[key] = video.videoURL; + + if (video.videoCaption) { + NSString *key = + [NSString stringWithFormat:@"VideoCaption_%i", videoIndex]; + parameters[key] = video.videoCaption; } - - return parameters; - + + videoIndex += 1; + } + + for (NSString *key in self.additionalFields) { + parameters[key] = self.additionalFields[key]; + } + for (NSString *key in self.contextDataValues) { + parameters[key] = self.contextDataValues[key]; + } + for (NSString *key in self.ratingQuestions) { + parameters[key] = self.ratingQuestions[key]; + } + for (NSString *key in self.ratingSliders) { + parameters[key] = self.ratingSliders[key]; + } + for (NSString *key in self.predefinedTags) { + parameters[key] = self.predefinedTags[key]; + } + for (NSString *key in self.freeformTags) { + parameters[key] = self.freeformTags[key]; + } + for (BVStringKeyValuePair *keyValuePair in self.customFormPairs) { + NSString *key = [keyValuePair key]; + NSString *value = [keyValuePair value]; + parameters[key] = value; + } + + return parameters; } - -- (NSString * _Nonnull)getPasskey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversations; + +- (nonnull NSString *)getPasskey { + return [BVSDKManager sharedManager].configuration.apiKeyConversations; } - --(void)addVideoURL:(nonnull NSString*)url withCaption:(nullable NSString*)videoCaption { - BVUploadableYouTubeVideo* video = [[BVUploadableYouTubeVideo alloc] initWithVideoURL:url videoCaption:videoCaption]; - [self.videos addObject:video]; + +- (void)addVideoURL:(nonnull NSString *)url + withCaption:(nullable NSString *)videoCaption { + BVUploadableYouTubeVideo *video = + [[BVUploadableYouTubeVideo alloc] initWithVideoURL:url + videoCaption:videoCaption]; + [self.videos addObject:video]; } @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.h b/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.h index f2d4aba6..e2f2180a 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.h +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVSubmissionErrorResponse.h" #import "BVSubmittedReview.h" +#import /// Failed review submission response. @interface BVReviewSubmissionErrorResponse : BVSubmissionErrorResponse -@property BVSubmittedReview* _Nullable review; +@property(nullable) BVSubmittedReview *review; @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.m b/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.m index c1cf12bc..0ee7d1b4 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.m +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmissionErrorResponse.m @@ -9,17 +9,19 @@ @implementation BVReviewSubmissionErrorResponse --(instancetype)initWithApiResponse:(nullable id)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - NSDictionary* apiObject = apiResponse; // [super initWithApiResponse:] checks that this is nonnull and a dictionary - self.review = [[BVSubmittedReview alloc] initWithApiResponse:apiObject[@"Review"]]; - } - - return self; -} +- (instancetype)initWithApiResponse:(nullable id)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + if (self) { + NSDictionary *apiObject = apiResponse; // [super initWithApiResponse:] + // checks that this is nonnull and a + // dictionary + self.review = + [[BVSubmittedReview alloc] initWithApiResponse:apiObject[@"Review"]]; + } + + return self; +} @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.h b/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.h index 817e08bb..afd68c0b 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.h +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.h @@ -5,13 +5,13 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVSubmittedReview.h" #import "BVSubmissionResponse.h" +#import "BVSubmittedReview.h" +#import /// Successful review submission response. @interface BVReviewSubmissionResponse : BVSubmissionResponse -@property BVSubmittedReview* _Nullable review; +@property(nullable) BVSubmittedReview *review; @end diff --git a/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.m b/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.m index 251b45d8..d2e3cddc 100644 --- a/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.m +++ b/Pod/BVConversations/Submission/Review/BVReviewSubmissionResponse.m @@ -9,16 +9,16 @@ @implementation BVReviewSubmissionResponse --(nonnull instancetype)initWithApiResponse:(NSDictionary*)apiResponse { - - self = [super initWithApiResponse:apiResponse]; - - if(self){ - self.review = [[BVSubmittedReview alloc] initWithApiResponse:apiResponse[@"Review"]]; - } - - return self; -} +- (nonnull instancetype)initWithApiResponse:(NSDictionary *)apiResponse { + + self = [super initWithApiResponse:apiResponse]; + if (self) { + self.review = + [[BVSubmittedReview alloc] initWithApiResponse:apiResponse[@"Review"]]; + } + + return self; +} @end diff --git a/Pod/BVConversations/Submission/Review/BVSubmittedReview.h b/Pod/BVConversations/Submission/Review/BVSubmittedReview.h index 654bc8d4..047305a2 100644 --- a/Pod/BVConversations/Submission/Review/BVSubmittedReview.h +++ b/Pod/BVConversations/Submission/Review/BVSubmittedReview.h @@ -10,20 +10,20 @@ /// A successfully submitted review. @interface BVSubmittedReview : NSObject -@property NSString* _Nullable title; -@property NSString* _Nullable reviewText; -@property NSNumber* _Nullable rating; -@property NSString* _Nullable reviewId; +@property(nullable) NSString *title; +@property(nullable) NSString *reviewText; +@property(nullable) NSNumber *rating; +@property(nullable) NSString *reviewId; -@property NSString* _Nullable submissionId; -@property NSDate* _Nullable submissionTime; +@property(nullable) NSString *submissionId; +@property(nullable) NSDate *submissionTime; -@property NSNumber* _Nullable isRecommended; -@property NSNumber* _Nullable sendEmailAlertWhenCommented; -@property NSNumber* _Nullable sendEmailAlertWhenPublished; +@property(nullable) NSNumber *isRecommended; +@property(nullable) NSNumber *sendEmailAlertWhenCommented; +@property(nullable) NSNumber *sendEmailAlertWhenPublished; -@property NSNumber* _Nullable typicalHoursToPost; +@property(nullable) NSNumber *typicalHoursToPost; --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse; +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse; @end diff --git a/Pod/BVConversations/Submission/Review/BVSubmittedReview.m b/Pod/BVConversations/Submission/Review/BVSubmittedReview.m index 79886b41..5ec0fb86 100644 --- a/Pod/BVConversations/Submission/Review/BVSubmittedReview.m +++ b/Pod/BVConversations/Submission/Review/BVSubmittedReview.m @@ -11,32 +11,35 @@ @implementation BVSubmittedReview --(nullable instancetype)initWithApiResponse:(nullable id)apiResponse { - self = [super init]; - if(self){ - - if(apiResponse == nil || ![apiResponse isKindOfClass:[NSDictionary class]]){ - return nil; - } - - NSDictionary* apiObject = apiResponse; - - SET_IF_NOT_NULL(self.title, apiObject[@"Title"]) - SET_IF_NOT_NULL(self.reviewText, apiObject[@"ReviewText"]) - SET_IF_NOT_NULL(self.rating, apiObject[@"Rating"]) - SET_IF_NOT_NULL(self.reviewId, apiObject[@"Id"]) - - SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) - SET_IF_NOT_NULL(self.isRecommended, apiObject[@"IsRecommended"]) - SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) - - self.submissionTime = [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; - - self.sendEmailAlertWhenCommented = apiObject[@"SendEmailAlertWhenCommented"]; - self.sendEmailAlertWhenPublished = apiObject[@"SendEmailAlertWhenPublished"]; - +- (nullable instancetype)initWithApiResponse:(nullable id)apiResponse { + self = [super init]; + if (self) { + + if (apiResponse == nil || + ![apiResponse isKindOfClass:[NSDictionary class]]) { + return nil; } - return self; + + NSDictionary *apiObject = apiResponse; + + SET_IF_NOT_NULL(self.title, apiObject[@"Title"]) + SET_IF_NOT_NULL(self.reviewText, apiObject[@"ReviewText"]) + SET_IF_NOT_NULL(self.rating, apiObject[@"Rating"]) + SET_IF_NOT_NULL(self.reviewId, apiObject[@"Id"]) + + SET_IF_NOT_NULL(self.submissionId, apiObject[@"SubmissionId"]) + SET_IF_NOT_NULL(self.isRecommended, apiObject[@"IsRecommended"]) + SET_IF_NOT_NULL(self.typicalHoursToPost, apiObject[@"TypicalHoursToPost"]) + + self.submissionTime = + [BVModelUtil convertTimestampToDatetime:apiObject[@"SubmissionTime"]]; + + self.sendEmailAlertWhenCommented = + apiObject[@"SendEmailAlertWhenCommented"]; + self.sendEmailAlertWhenPublished = + apiObject[@"SendEmailAlertWhenPublished"]; + } + return self; } @end diff --git a/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.h b/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.h index 0add69f2..4ec7db23 100644 --- a/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.h +++ b/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.h @@ -5,38 +5,43 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import "BVReviewSubmissionResponse.h" +#import "BVConversationsRequest.h" #import "BVReviewSubmission.h" +#import "BVReviewSubmissionResponse.h" #import "BVSubmissionAction.h" -#import "BVConversationsRequest.h" #import "BVUploadablePhoto.h" +#import -typedef void (^ReviewSubmissionCompletion)(BVReviewSubmissionResponse* _Nonnull response); - +typedef void (^ReviewSubmissionCompletion)( + BVReviewSubmissionResponse *__nonnull response); /** - Class to use to submit a review on a store to the Bazaarvoice platform. Requires that you have set the apiKeyConversationsStore value in the BVSDKManager and client id before you begin using submission request. - - Of the many parameters possible on a BVStoreReviewSubmission, the ones needed for submission depend on your specific implementation. - + Class to use to submit a review on a store to the Bazaarvoice platform. + Requires that you have set the apiKeyConversationsStore value in the + BVSDKManager and client id before you begin using submission request. + + Of the many parameters possible on a BVStoreReviewSubmission, the ones needed + for submission depend on your specific implementation. + For a description of possible fields see our API documentation at: https://developer.bazaarvoice.com/docs/read/conversations/reviews/submit/5_4 - + @availability 4.2.0 and later */ @interface BVStoreReviewSubmission : BVReviewSubmission /** Create a new BVStoreReviewSubmission. - + @param reviewTitle The user-provided title of the review. @param reviewText The user-provided body of the review. @param rating The user-provided rating: 1-5. @param storeId The store identifier that this review is associated with. */ --(nonnull instancetype)initWithReviewTitle:(nonnull NSString*)reviewTitle reviewText:(nonnull NSString*)reviewText rating:(NSUInteger)rating storeId:(nonnull NSString*)storeId; --(nonnull instancetype) __unavailable init; - +- (nonnull instancetype)initWithReviewTitle:(nonnull NSString *)reviewTitle + reviewText:(nonnull NSString *)reviewText + rating:(NSUInteger)rating + storeId:(nonnull NSString *)storeId; +- (nonnull instancetype)__unavailable init; @end diff --git a/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.m b/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.m index 40916858..146b61d6 100644 --- a/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.m +++ b/Pod/BVConversations/Submission/Review/Store/BVStoreReviewSubmission.m @@ -7,28 +7,37 @@ #import "BVStoreReviewSubmission.h" #import "BVReviewSubmissionErrorResponse.h" +#import "BVSDKConfiguration.h" #import "BVSDKManager.h" #import "BVUploadableStorePhoto.h" -#import "BVSDKConfiguration.h" -@interface BVStoreReviewSubmission() +@interface BVStoreReviewSubmission () @end @implementation BVStoreReviewSubmission --(nonnull instancetype)initWithReviewTitle:(nonnull NSString*)reviewTitle reviewText:(nonnull NSString*)reviewText rating:(NSUInteger)rating storeId:(nonnull NSString*)storeId { - self = [self initWithReviewTitle:reviewTitle reviewText:reviewText rating:rating productId:storeId]; - return self; +- (nonnull instancetype)initWithReviewTitle:(nonnull NSString *)reviewTitle + reviewText:(nonnull NSString *)reviewText + rating:(NSUInteger)rating + storeId:(nonnull NSString *)storeId { + self = [self initWithReviewTitle:reviewTitle + reviewText:reviewText + rating:rating + productId:storeId]; + return self; } --(void)addPhoto:(nonnull UIImage*)image withPhotoCaption:(nullable NSString*)photoCaption { - BVUploadableStorePhoto* photo = [[BVUploadableStorePhoto alloc] initWithPhoto:image photoCaption:photoCaption]; - [self.photos addObject:photo]; +- (void)addPhoto:(nonnull UIImage *)image + withPhotoCaption:(nullable NSString *)photoCaption { + BVUploadableStorePhoto *photo = + [[BVUploadableStorePhoto alloc] initWithPhoto:image + photoCaption:photoCaption]; + [self.photos addObject:photo]; } -- (NSString * _Nonnull)getPasskey{ - return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; +- (nonnull NSString *)getPasskey { + return [BVSDKManager sharedManager].configuration.apiKeyConversationsStores; } @end diff --git a/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.h b/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.h index 840ba17b..d7ac746d 100644 --- a/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.h +++ b/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.h @@ -9,9 +9,10 @@ @interface BVUploadableYouTubeVideo : NSObject --(nonnull instancetype)initWithVideoURL:(nonnull NSString*)url videoCaption:(nullable NSString*)caption; +- (nonnull instancetype)initWithVideoURL:(nonnull NSString *)url + videoCaption:(nullable NSString *)caption; -@property (readonly) NSString* _Nonnull videoURL; -@property (readonly) NSString* _Nullable videoCaption; +@property(nonnull, readonly) NSString *videoURL; +@property(nullable, readonly) NSString *videoCaption; @end diff --git a/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.m b/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.m index d53b08bb..94fee0f9 100644 --- a/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.m +++ b/Pod/BVConversations/Submission/Video/BVUploadableYouTubeVideo.m @@ -9,14 +9,15 @@ @implementation BVUploadableYouTubeVideo --(nonnull instancetype)initWithVideoURL:(nonnull NSString*)url videoCaption:(nullable NSString*)caption; { - self = [super init]; - if(self){ - _videoURL = url; - _videoCaption = caption; - } - return self; +- (nonnull instancetype)initWithVideoURL:(nonnull NSString *)url + videoCaption:(nullable NSString *)caption; +{ + self = [super init]; + if (self) { + _videoURL = url; + _videoCaption = caption; + } + return self; } - @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.h b/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.h index 30a28704..6eb73ef2 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.h +++ b/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.h @@ -5,16 +5,16 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVAnswer.h" +#import /** -A sub-classed UICollectionView cell that contains one BVAnswer object for display. -Clients should provide their one xib for the view. +A sub-classed UICollectionView cell that contains one BVAnswer object for +display. Clients should provide their one xib for the view. */ @interface BVAnswerCollectionViewCell : UICollectionViewCell /// The Conversations Answer associated with this collectionViewCell -@property (strong, nonatomic) BVAnswer *answer; +@property(strong, nonatomic) BVAnswer *answer; @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.m b/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.m index a1bc8ce5..f2049441 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.m +++ b/Pod/BVConversations/Views/Answers/BVAnswerCollectionViewCell.m @@ -10,17 +10,15 @@ @implementation BVAnswerCollectionViewCell --(void)setAnswer:(BVAnswer*) answer { - - _answer = answer; - +- (void)setAnswer:(BVAnswer *)answer { + + _answer = answer; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.h b/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.h index 2d9dfbce..91d2b946 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.h +++ b/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.h @@ -1,20 +1,20 @@ // // BVAnswerTableViewCell.h -// BVConersations +// BVConersations // // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVAnswer.h" +#import /** -A sub-classed UITableViewCell cell that contains one BVAnswer object for display. -Clients should provide their one xib for the view. +A sub-classed UITableViewCell cell that contains one BVAnswer object for +display. Clients should provide their one xib for the view. */ @interface BVAnswerTableViewCell : UITableViewCell /// The Conversations Answer associated with this tableViewCell -@property (strong, nonatomic) BVAnswer *answer; +@property(strong, nonatomic) BVAnswer *answer; @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.m b/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.m index 189a5bb5..96da4ae2 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.m +++ b/Pod/BVConversations/Views/Answers/BVAnswerTableViewCell.m @@ -10,17 +10,15 @@ @implementation BVAnswerTableViewCell --(void)setAnswer:(BVAnswer *)answer { - - _answer = answer; - +- (void)setAnswer:(BVAnswer *)answer { + + _answer = answer; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerView.h b/Pod/BVConversations/Views/Answers/BVAnswerView.h index 2b7f7e7d..1d30c399 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerView.h +++ b/Pod/BVConversations/Views/Answers/BVAnswerView.h @@ -5,8 +5,8 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVAnswer.h" +#import /** A sub-classed UIView cell that contains one BVAnswer object for display. @@ -15,6 +15,6 @@ Clients should provide their one xib for the view. @interface BVAnswerView : UIView /// The Conversations Answer associated with this view -@property (strong, nonatomic) BVAnswer *answer; +@property(strong, nonatomic) BVAnswer *answer; @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswerView.m b/Pod/BVConversations/Views/Answers/BVAnswerView.m index 98fe8dd7..c2970ae2 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswerView.m +++ b/Pod/BVConversations/Views/Answers/BVAnswerView.m @@ -10,17 +10,15 @@ @implementation BVAnswerView --(void)setAnswer:(BVAnswer *)answer { - - _answer = answer; - +- (void)setAnswer:(BVAnswer *)answer { + + _answer = answer; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Answers/BVAnswersCollectionView.m b/Pod/BVConversations/Views/Answers/BVAnswersCollectionView.m index 0970edc2..88f7ea73 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswersCollectionView.m +++ b/Pod/BVConversations/Views/Answers/BVAnswersCollectionView.m @@ -6,169 +6,179 @@ // #import "BVAnswersCollectionView.h" -#import "BVAnswerCollectionViewCell.h" #import "BVAnswer.h" -#import "BVMessageInterceptor.h" -#import "BVViewsHelper.h" +#import "BVAnswerCollectionViewCell.h" #import "BVCore.h" #import "BVLogger.h" +#import "BVMessageInterceptor.h" +#import "BVViewsHelper.h" -@interface BVAnswersCollectionView() -{ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentSeenEvent; - NSMutableDictionary* cellToProductMap; +@interface BVAnswersCollectionView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentSeenEvent; + NSMutableDictionary *cellToProductMap; } @end @implementation BVAnswersCollectionView --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - self = [super initWithFrame:frame collectionViewLayout:layout]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame + collectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithFrame:frame collectionViewLayout:layout]; + if (self) { + [self setup]; + } + return self; } --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } - --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; - - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:@"none" withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContainerId:@"AnswersCollectionView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; + + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContainerId:@"AnswersCollectionView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } - #pragma mark UIScrollViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - } - -} +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - [BVPixel trackEvent:scrollEvent]; - + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; } #pragma mark UICollectionViewDelegate +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { --(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - - if([delegate_interceptor.receiver respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) { - [delegate_interceptor.receiver collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - } + if ([delegate_interceptor.receiver + respondsToSelector:@selector + (collectionView:didSelectItemAtIndexPath:)]) { + [delegate_interceptor.receiver collectionView:collectionView + didSelectItemAtIndexPath:indexPath]; + } } #pragma mark - UICollectionViewDataSource --(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - UICollectionViewCell* cell = [datasource_interceptor.receiver collectionView:collectionView cellForItemAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVAnswerCollectionViewCell class]]) { - - BVAnswerCollectionViewCell* bvCell = (BVAnswerCollectionViewCell*)cell; - BVAnswer *answer = bvCell.answer; - if (answer) { - [cellToProductMap setObject:answer forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have answer set - NSString* message = @"BVAnswerCollectionViewCell has nil `answer` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + UICollectionViewCell *cell = + [datasource_interceptor.receiver collectionView:collectionView + cellForItemAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVAnswerCollectionViewCell class]]) { + + BVAnswerCollectionViewCell *bvCell = (BVAnswerCollectionViewCell *)cell; + BVAnswer *answer = bvCell.answer; + if (answer) { + [cellToProductMap setObject:answer + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have answer set + NSString *message = @"BVAnswerCollectionViewCell has nil `answer` " + @"property. This must be set in " + @"`cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } - - return cell; - -} + } --(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [datasource_interceptor.receiver collectionView:collectionView numberOfItemsInSection:section]; + return cell; } +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [datasource_interceptor.receiver collectionView:collectionView + numberOfItemsInSection:section]; +} @end - diff --git a/Pod/BVConversations/Views/Answers/BVAnswersTableView.m b/Pod/BVConversations/Views/Answers/BVAnswersTableView.m index 585a82d9..3458adf7 100644 --- a/Pod/BVConversations/Views/Answers/BVAnswersTableView.m +++ b/Pod/BVConversations/Views/Answers/BVAnswersTableView.m @@ -6,159 +6,165 @@ // #import "BVAnswersTableView.h" -#import "BVMessageInterceptor.h" #import "BVAnswerTableViewCell.h" #import "BVCore.h" +#import "BVMessageInterceptor.h" #import "BVViewsHelper.h" -@interface BVAnswersTableView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentRenderedEvent; - bool hasSentSeenEvent; - NSMutableDictionary* cellToProductMap; +@interface BVAnswersTableView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentRenderedEvent; + bool hasSentSeenEvent; + NSMutableDictionary *cellToProductMap; } @end @implementation BVAnswersTableView --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } - --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; - - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:@"none" withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContainerId:@"AnswersTableView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; + + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContainerId:@"AnswersTableView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } #pragma mark UITableViewDataSource -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [datasource_interceptor.receiver tableView:tableView numberOfRowsInSection:section]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [datasource_interceptor.receiver tableView:tableView + numberOfRowsInSection:section]; } -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - UITableViewCell* cell = [datasource_interceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVAnswerTableViewCell class]]) { - - BVAnswerTableViewCell* answerCell = (BVAnswerTableViewCell*)cell; - BVAnswer* answer = answerCell.answer; - if (answer) { - [cellToProductMap setObject:answer forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have answer set - NSString* message = @"BVAnswerTableViewCell has nil `answer` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + UITableViewCell *cell = [datasource_interceptor.receiver tableView:tableView + cellForRowAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVAnswerTableViewCell class]]) { + + BVAnswerTableViewCell *answerCell = (BVAnswerTableViewCell *)cell; + BVAnswer *answer = answerCell.answer; + if (answer) { + [cellToProductMap setObject:answer + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have answer set + NSString *message = @"BVAnswerTableViewCell has nil `answer` property. " + @"This must be set in `cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } - - return cell; -} + } + return cell; +} #pragma mark UITableViewDelegate -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - if([delegate_interceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { - [delegate_interceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath]; - } +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { + [delegate_interceptor.receiver tableView:tableView + didSelectRowAtIndexPath:indexPath]; + } } #pragma mark UIScrollViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - - } - -} +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - [BVPixel trackEvent:scrollEvent]; - + } } -@end +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + [BVPixel trackEvent:scrollEvent]; +} + +@end diff --git a/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.h b/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.h index 8e88fe9d..644534f0 100644 --- a/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.h +++ b/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.h @@ -5,32 +5,33 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVProduct.h" +#import - -/// This sub-class of UIViewController can be sublcassed by clients who also implement the BVProductDisplayPageRequest API. +/// This sub-class of UIViewController can be sublcassed by clients who also +/// implement the BVProductDisplayPageRequest API. @interface BVProductDisplayPageViewController : UIViewController /// The product model on the product display page. -@property (nonatomic, strong) BVProduct* product; +@property(nonatomic, strong) BVProduct *product; @end - -/// This sub-class of UITableViewController can be sublcassed by clients who also implement the BVProductDisplayPageRequest API. +/// This sub-class of UITableViewController can be sublcassed by clients who +/// also implement the BVProductDisplayPageRequest API. @interface BVProductDisplayPageTableViewController : UITableViewController /// The product model on the product display page. -@property (nonatomic, strong) BVProduct* product; +@property(nonatomic, strong) BVProduct *product; @end - -/// This sub-class of UICollectionViewController can be sublcassed by clients who also implement the BVProductDisplayPageRequest API. -@interface BVProductDisplayPageCollectionViewController : UICollectionViewController +/// This sub-class of UICollectionViewController can be sublcassed by clients +/// who also implement the BVProductDisplayPageRequest API. +@interface BVProductDisplayPageCollectionViewController + : UICollectionViewController /// The product model on the product display page. -@property (nonatomic, strong) BVProduct* product; +@property(nonatomic, strong) BVProduct *product; @end diff --git a/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.m b/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.m index 558ddebf..084b6910 100644 --- a/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.m +++ b/Pod/BVConversations/Views/ProductDisplayPage/BVProductPageViews.m @@ -8,91 +8,89 @@ #import "BVProductPageViews.h" #import "BVPixel.h" - -@interface BVProductDisplayPageViewController() { - bool hasSentPageviewEvent; +@interface BVProductDisplayPageViewController () { + bool hasSentPageviewEvent; } @end @implementation BVProductDisplayPageViewController --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - if (!hasSentPageviewEvent && self.product != nil) { - hasSentPageviewEvent = true; - - NSString *brandName = self.product.brand != nil ? self.product.brand.name : nil; - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:self.product.identifier - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:brandName - withCategoryId:self.product.categoryId - withRootCategoryId:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:pageView]; - - } -} +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; -@end + if (!hasSentPageviewEvent && self.product != nil) { + hasSentPageviewEvent = true; + NSString *brandName = + self.product.brand != nil ? self.product.brand.name : nil; + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:self.product.identifier + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:brandName + withCategoryId:self.product.categoryId + withRootCategoryId:nil + withAdditionalParams:nil]; + [BVPixel trackEvent:pageView]; + } +} -@interface BVProductDisplayPageTableViewController(){ - bool hasSentPageviewEvent; +@end + +@interface BVProductDisplayPageTableViewController () { + bool hasSentPageviewEvent; } @end @implementation BVProductDisplayPageTableViewController --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - if (!hasSentPageviewEvent && self.product != nil) { - hasSentPageviewEvent = true; - - NSString *brandName = self.product.brand != nil ? self.product.brand.name : nil; - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:self.product.identifier - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:brandName - withCategoryId:self.product.categoryId - withRootCategoryId:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:pageView]; - - } -} +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; -@end + if (!hasSentPageviewEvent && self.product != nil) { + hasSentPageviewEvent = true; + + NSString *brandName = + self.product.brand != nil ? self.product.brand.name : nil; + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:self.product.identifier + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:brandName + withCategoryId:self.product.categoryId + withRootCategoryId:nil + withAdditionalParams:nil]; + [BVPixel trackEvent:pageView]; + } +} +@end -@interface BVProductDisplayPageCollectionViewController() { - bool hasSentPageviewEvent; +@interface BVProductDisplayPageCollectionViewController () { + bool hasSentPageviewEvent; } @end @implementation BVProductDisplayPageCollectionViewController --(void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - if (!hasSentPageviewEvent && self.product != nil) { - hasSentPageviewEvent = true; - - NSString *brandName = self.product.brand != nil ? self.product.brand.name : nil; - BVPageViewEvent *pageView = [[BVPageViewEvent alloc] initWithProductId:self.product.identifier - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:brandName - withCategoryId:self.product.categoryId - withRootCategoryId:nil - withAdditionalParams:nil]; - - [BVPixel trackEvent:pageView]; - - } +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + + if (!hasSentPageviewEvent && self.product != nil) { + hasSentPageviewEvent = true; + + NSString *brandName = + self.product.brand != nil ? self.product.brand.name : nil; + BVPageViewEvent *pageView = [[BVPageViewEvent alloc] + initWithProductId:self.product.identifier + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:brandName + withCategoryId:self.product.categoryId + withRootCategoryId:nil + withAdditionalParams:nil]; + + [BVPixel trackEvent:pageView]; + } } @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.h b/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.h index 009c5678..3424b5bb 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.h +++ b/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.h @@ -5,16 +5,16 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVQuestion.h" +#import /** - A sub-classed UICollectionView cell that contains one BVQuestion object for display. - Clients should provide their one xib for the view. + A sub-classed UICollectionView cell that contains one BVQuestion object for + display. Clients should provide their one xib for the view. */ @interface BVQuestionCollectionViewCell : UICollectionViewCell /// The Conversations Question associated with this collectionViewCell -@property (strong, nonatomic) BVQuestion *question; +@property(strong, nonatomic) BVQuestion *question; @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.m b/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.m index c51465db..e27696cb 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.m +++ b/Pod/BVConversations/Views/Questions/BVQuestionCollectionViewCell.m @@ -10,17 +10,15 @@ @implementation BVQuestionCollectionViewCell --(void)setQuestion:(BVQuestion *)question { - - _question = question; - +- (void)setQuestion:(BVQuestion *)question { + + _question = question; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.h b/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.h index 34d41f12..f7a1baa3 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.h +++ b/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.h @@ -1,21 +1,20 @@ // // BVQuestionTableViewCell.h -// BVConersations +// BVConersations // // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVQuestion.h" - +#import /** - A sub-classed UITableViewCell cell that contains one BVQuestion object for display. - Clients should provide their one xib for the view. + A sub-classed UITableViewCell cell that contains one BVQuestion object for + display. Clients should provide their one xib for the view. */ @interface BVQuestionTableViewCell : UITableViewCell /// The Conversations Question associated with this tableViewCell -@property (strong, nonatomic) BVQuestion *question; +@property(strong, nonatomic) BVQuestion *question; @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.m b/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.m index b59d347c..b6d4f94f 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.m +++ b/Pod/BVConversations/Views/Questions/BVQuestionTableViewCell.m @@ -10,17 +10,15 @@ @implementation BVQuestionTableViewCell --(void)setQuestion:(BVQuestion *)question { - - _question = question; - +- (void)setQuestion:(BVQuestion *)question { + + _question = question; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionView.h b/Pod/BVConversations/Views/Questions/BVQuestionView.h index f8c76a5a..f37c2df4 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionView.h +++ b/Pod/BVConversations/Views/Questions/BVQuestionView.h @@ -5,8 +5,8 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVQuestion.h" +#import /** A sub-classed UIView cell that contains one BVQuestion object for display. @@ -15,6 +15,6 @@ @interface BVQuestionView : UIView /// The Conversations Question associated with this view -@property (strong, nonatomic) BVQuestion *question; +@property(strong, nonatomic) BVQuestion *question; @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionView.m b/Pod/BVConversations/Views/Questions/BVQuestionView.m index 209689c1..858eb775 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionView.m +++ b/Pod/BVConversations/Views/Questions/BVQuestionView.m @@ -10,18 +10,15 @@ @implementation BVQuestionView --(void)setQuestion:(BVQuestion *)question { - - _question = question; - +- (void)setQuestion:(BVQuestion *)question { + + _question = question; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - -} + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; +} @end diff --git a/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.h b/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.h index 2c942da4..7e153bdf 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.h +++ b/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.h @@ -12,8 +12,9 @@ /// A sub-classed UICollectionView for displaying BVQuestionTableViewCells @interface BVQuestionsCollectionView : UICollectionView -/// Helper method to asynchronously load the Questions for given request. This helper also ensures the proper analytic tracking is fired for reporting. -- (void)load:(nonnull BVQuestionsAndAnswersRequest*)request +/// Helper method to asynchronously load the Questions for given request. This +/// helper also ensures the proper analytic tracking is fired for reporting. +- (void)load:(nonnull BVQuestionsAndAnswersRequest *)request success:(nonnull QuestionsAndAnswersSuccessHandler)success failure:(nonnull ConversationsFailureHandler)failure; diff --git a/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.m b/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.m index d84cc302..58dff3c8 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.m +++ b/Pod/BVConversations/Views/Questions/BVQuestionsCollectionView.m @@ -7,197 +7,204 @@ #import "BVQuestionsCollectionView.h" +#import "BVCore.h" #import "BVMessageInterceptor.h" #import "BVQuestion.h" #import "BVQuestionCollectionViewCell.h" -#import "BVCore.h" #import "BVViewsHelper.h" -@interface BVQuestionsCollectionView() -{ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentInViewEvent; - bool hasEnteredView; - NSMutableDictionary* cellToProductMap; - NSString *productId; +@interface BVQuestionsCollectionView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentInViewEvent; + bool hasEnteredView; + NSMutableDictionary *cellToProductMap; + NSString *productId; } @end @implementation BVQuestionsCollectionView --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; -} - --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; -} - --(id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - self = [super initWithFrame:frame collectionViewLayout:layout]; - if(self){ - [self setup]; - } - return self; -} - --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame + collectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithFrame:frame collectionViewLayout:layout]; + if (self) { + [self setup]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; +} + +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDataSource:(id)datasource_interceptor]; +} + +- (void)tryToSendInViewEvent { + + if (hasEnteredView && productId != nil) { + if (!hasSentInViewEvent) { + hasSentInViewEvent = true; + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContainerId:@"QuestionsCollectionView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - return self; + } } +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + hasEnteredView = true; --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDataSource:(id)datasource_interceptor]; + [self tryToSendInViewEvent]; } --(void)tryToSendInViewEvent { - - if (hasEnteredView && productId != nil) { - if (!hasSentInViewEvent){ - hasSentInViewEvent = true; - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:productId withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContainerId:@"QuestionsCollectionView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - - } - } - -} - --(void)willMoveToWindow:(UIWindow *)newWindow { - [super willMoveToWindow:newWindow]; - hasEnteredView = true; - - [self tryToSendInViewEvent]; -} - -- (void)load:(nonnull BVQuestionsAndAnswersRequest*)request +- (void)load:(nonnull BVQuestionsAndAnswersRequest *)request success:(nonnull QuestionsAndAnswersSuccessHandler)success failure:(nonnull ConversationsFailureHandler)failure { - - productId = request.productId; - [self tryToSendInViewEvent]; - [request load:success failure:failure]; - + + productId = request.productId; + [self tryToSendInViewEvent]; + [request load:success failure:failure]; } --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } - #pragma mark UIScrollViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - } - -} +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - [BVPixel trackEvent:scrollEvent]; - + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; } #pragma mark UICollectionViewDelegate +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { --(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - - if([delegate_interceptor.receiver respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) { - [delegate_interceptor.receiver collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - } + if ([delegate_interceptor.receiver + respondsToSelector:@selector + (collectionView:didSelectItemAtIndexPath:)]) { + [delegate_interceptor.receiver collectionView:collectionView + didSelectItemAtIndexPath:indexPath]; + } } #pragma mark - UICollectionViewDataSource --(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - UICollectionViewCell* cell = [datasource_interceptor.receiver collectionView:collectionView cellForItemAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVQuestionCollectionViewCell class]]) { - - BVQuestionCollectionViewCell* bvCell = (BVQuestionCollectionViewCell*)cell; - BVQuestion *question = bvCell.question; - if(question != nil){ - [cellToProductMap setObject:question forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have question set - NSString* message = @"BVQuestionCollectionViewCell has nil `question` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + UICollectionViewCell *cell = + [datasource_interceptor.receiver collectionView:collectionView + cellForItemAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVQuestionCollectionViewCell class]]) { + + BVQuestionCollectionViewCell *bvCell = (BVQuestionCollectionViewCell *)cell; + BVQuestion *question = bvCell.question; + if (question != nil) { + [cellToProductMap setObject:question + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have question set + NSString *message = @"BVQuestionCollectionViewCell has nil `question` " + @"property. This must be set in " + @"`cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } - - return cell; - -} + } --(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [datasource_interceptor.receiver collectionView:collectionView numberOfItemsInSection:section]; + return cell; } +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [datasource_interceptor.receiver collectionView:collectionView + numberOfItemsInSection:section]; +} @end - diff --git a/Pod/BVConversations/Views/Questions/BVQuestionsTableView.h b/Pod/BVConversations/Views/Questions/BVQuestionsTableView.h index 1f400b42..777fb0eb 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionsTableView.h +++ b/Pod/BVConversations/Views/Questions/BVQuestionsTableView.h @@ -12,8 +12,9 @@ /// A sub-classed UITableView for displaying BVQuestionTableViewCells @interface BVQuestionsTableView : UITableView -/// Helper method to asynchronously load the Questions for given request. This helper also ensures the proper analytic tracking is fired for reporting. -- (void)load:(nonnull BVQuestionsAndAnswersRequest*)request +/// Helper method to asynchronously load the Questions for given request. This +/// helper also ensures the proper analytic tracking is fired for reporting. +- (void)load:(nonnull BVQuestionsAndAnswersRequest *)request success:(nonnull QuestionsAndAnswersSuccessHandler)success failure:(nonnull ConversationsFailureHandler)failure; diff --git a/Pod/BVConversations/Views/Questions/BVQuestionsTableView.m b/Pod/BVConversations/Views/Questions/BVQuestionsTableView.m index ec792c13..72e5f58d 100644 --- a/Pod/BVConversations/Views/Questions/BVQuestionsTableView.m +++ b/Pod/BVConversations/Views/Questions/BVQuestionsTableView.m @@ -6,187 +6,193 @@ // #import "BVQuestionsTableView.h" +#import "BVCore.h" #import "BVMessageInterceptor.h" #import "BVQuestionTableViewCell.h" -#import "BVCore.h" #import "BVViewsHelper.h" -@interface BVQuestionsTableView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentInViewEvent; - bool hasEnteredView; - NSMutableDictionary* cellToProductMap; - NSString *productId; +@interface BVQuestionsTableView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentInViewEvent; + bool hasEnteredView; + NSMutableDictionary *cellToProductMap; + NSString *productId; } @end @implementation BVQuestionsTableView --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; +} --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; -} - --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; -} - --(void)tryToSendInViewEvent { - - if (hasEnteredView && productId != nil) { - if (!hasSentInViewEvent){ - hasSentInViewEvent = true; - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:productId withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withContainerId:@"QuestionsTableView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - } +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; +} + +- (void)tryToSendInViewEvent { + + if (hasEnteredView && productId != nil) { + if (!hasSentInViewEvent) { + hasSentInViewEvent = true; + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withContainerId:@"QuestionsTableView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - + } } --(void)willMoveToWindow:(UIWindow *)newWindow { - [super willMoveToWindow:newWindow]; - hasEnteredView = true; - - [self tryToSendInViewEvent]; +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + hasEnteredView = true; + + [self tryToSendInViewEvent]; } -- (void)load:(nonnull BVQuestionsAndAnswersRequest*)request +- (void)load:(nonnull BVQuestionsAndAnswersRequest *)request success:(nonnull QuestionsAndAnswersSuccessHandler)success failure:(nonnull ConversationsFailureHandler)failure { - - productId = request.productId; - [self tryToSendInViewEvent]; - - [request load:success failure:failure]; - -} + productId = request.productId; + [self tryToSendInViewEvent]; + + [request load:success failure:failure]; +} --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } #pragma mark UITableViewDataSource -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [datasource_interceptor.receiver tableView:tableView numberOfRowsInSection:section]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - UITableViewCell* cell = [datasource_interceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVQuestionTableViewCell class]]) { - - BVQuestionTableViewCell* quesetionCell = (BVQuestionTableViewCell*)cell; - BVQuestion* question = quesetionCell.question; - if(question != nil){ - [cellToProductMap setObject:question forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have question set - NSString* message = @"BVQuestionTableViewCell has nil `question` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - - } - - return cell; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [datasource_interceptor.receiver tableView:tableView + numberOfRowsInSection:section]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { -#pragma mark UITableViewDelegate + UITableViewCell *cell = [datasource_interceptor.receiver tableView:tableView + cellForRowAtIndexPath:indexPath]; -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - if([delegate_interceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { - [delegate_interceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath]; + if ([cell isKindOfClass:[BVQuestionTableViewCell class]]) { + + BVQuestionTableViewCell *quesetionCell = (BVQuestionTableViewCell *)cell; + BVQuestion *question = quesetionCell.question; + if (question != nil) { + [cellToProductMap setObject:question + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have question set + NSString *message = @"BVQuestionTableViewCell has nil `question` " + @"property. This must be set in " + @"`cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } + } + + return cell; } -#pragma mark UIScrollViewDelegate +#pragma mark UITableViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - - } - +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { + [delegate_interceptor.receiver tableView:tableView + didSelectRowAtIndexPath:indexPath]; + } } --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - +#pragma mark UIScrollViewDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + [BVPixel trackEvent:scrollEvent]; - + } } -@end +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; +} + +@end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.h b/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.h index e57b73d0..d16cd57c 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.h +++ b/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.h @@ -5,16 +5,16 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVReview.h" +#import /** - A sub-classed UICollectionView cell that contains one BVReview object for display. - Clients should provide their one xib for the view. + A sub-classed UICollectionView cell that contains one BVReview object for + display. Clients should provide their one xib for the view. */ @interface BVReviewCollectionViewCell : UICollectionViewCell /// The Conversations Reivew associated with this collectionViewCell -@property (strong, nonatomic) BVReview *review; +@property(strong, nonatomic) BVReview *review; @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.m b/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.m index d0d69b47..3982586b 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.m +++ b/Pod/BVConversations/Views/Reviews/BVReviewCollectionViewCell.m @@ -10,18 +10,15 @@ @implementation BVReviewCollectionViewCell --(void)setReview:(BVReview *)review { - - _review = review; - +- (void)setReview:(BVReview *)review { + + _review = review; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end - diff --git a/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.h b/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.h index 05824551..b690e0f3 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.h +++ b/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.h @@ -5,16 +5,16 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVReview.h" +#import /** - A sub-classed UITableViewCell cell that contains one BVReview object for display. - Clients should provide their one xib for the view. + A sub-classed UITableViewCell cell that contains one BVReview object for + display. Clients should provide their one xib for the view. */ @interface BVReviewTableViewCell : UITableViewCell /// The Conversations Reivew associated with this tableViewCell -@property (strong, nonatomic) BVReview *review; +@property(strong, nonatomic) BVReview *review; @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.m b/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.m index 383cf8ac..917a9f75 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.m +++ b/Pod/BVConversations/Views/Reviews/BVReviewTableViewCell.m @@ -10,16 +10,15 @@ @implementation BVReviewTableViewCell --(void)setReview:(BVReview *)review { - - _review = review; - +- (void)setReview:(BVReview *)review { + + _review = review; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewView.h b/Pod/BVConversations/Views/Reviews/BVReviewView.h index 1a76c236..aca84dbc 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewView.h +++ b/Pod/BVConversations/Views/Reviews/BVReviewView.h @@ -5,9 +5,8 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import #import "BVReview.h" - +#import /** A sub-classed UIView cell that contains one BVReview object for display. @@ -17,7 +16,6 @@ @interface BVReviewView : UIView /// The Conversations Reivew associated with this view -@property (strong, nonatomic) BVReview *review; - +@property(strong, nonatomic) BVReview *review; @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewView.m b/Pod/BVConversations/Views/Reviews/BVReviewView.m index e267a83f..fbc20350 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewView.m +++ b/Pod/BVConversations/Views/Reviews/BVReviewView.m @@ -10,17 +10,15 @@ @implementation BVReviewView --(void)setReview:(BVReview *)review { - - _review = review; - +- (void)setReview:(BVReview *)review { + + _review = review; } - (void)didMoveToSuperview { - - [super didMoveToSuperview]; - [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; - + + [super didMoveToSuperview]; + [BVViewsHelper checkGestureRecognizers:self.gestureRecognizers]; } @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.h b/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.h index 8e627e93..2ae3e359 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.h +++ b/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.h @@ -12,8 +12,9 @@ /// A sub-classed UICollectionView for displaying BVReviewTableViewCells @interface BVReviewsCollectionView : UICollectionView -/// Helper method to asynchronously load the Reviews for a given request. This helper also ensures the proper analytic tracking is fired for reporting. -- (void)load:(nonnull BVReviewsRequest*)request +/// Helper method to asynchronously load the Reviews for a given request. This +/// helper also ensures the proper analytic tracking is fired for reporting. +- (void)load:(nonnull BVReviewsRequest *)request success:(nonnull ReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure; diff --git a/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.m b/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.m index 5c5072f4..1a013efd 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.m +++ b/Pod/BVConversations/Views/Reviews/BVReviewsCollectionView.m @@ -6,198 +6,206 @@ // #import "BVReviewsCollectionView.h" +#import "BVCore.h" #import "BVMessageInterceptor.h" #import "BVReview.h" #import "BVReviewCollectionViewCell.h" -#import "BVCore.h" #import "BVViewsHelper.h" -@interface BVReviewsCollectionView() -{ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentInViewEvent; - bool hasEnteredView; - NSMutableDictionary* cellToProductMap; - NSString *productId; +@interface BVReviewsCollectionView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentInViewEvent; + bool hasEnteredView; + NSMutableDictionary *cellToProductMap; + NSString *productId; } @end @implementation BVReviewsCollectionView --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - self = [super initWithFrame:frame collectionViewLayout:layout]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame + collectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithFrame:frame collectionViewLayout:layout]; + if (self) { + [self setup]; + } + return self; } --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; -} - - --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; -} - --(void)tryToSendInViewEvent { - - if (hasEnteredView && productId != nil) { - if (!hasSentInViewEvent){ - hasSentInViewEvent = true; - - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withContainerId:@"ReviewsCollectionView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - } +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; +} + +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; +} + +- (void)tryToSendInViewEvent { + + if (hasEnteredView && productId != nil) { + if (!hasSentInViewEvent) { + hasSentInViewEvent = true; + + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withContainerId:@"ReviewsCollectionView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - + } } --(void)willMoveToWindow:(UIWindow *)newWindow { - [super willMoveToWindow:newWindow]; - hasEnteredView = true; - - [self tryToSendInViewEvent]; +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + hasEnteredView = true; + + [self tryToSendInViewEvent]; } -- (void)load:(nonnull BVReviewsRequest*)request +- (void)load:(nonnull BVReviewsRequest *)request success:(nonnull ReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure { - - productId = request.productId; - [self tryToSendInViewEvent]; - - [request load:success failure:failure]; - + + productId = request.productId; + [self tryToSendInViewEvent]; + + [request load:success failure:failure]; } --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } - #pragma mark UIScrollViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - } - -} +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - [BVPixel trackEvent:scrollEvent]; + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + [BVPixel trackEvent:scrollEvent]; } #pragma mark UICollectionViewDelegate +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { --(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - - if([delegate_interceptor.receiver respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) { - [delegate_interceptor.receiver collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - } + if ([delegate_interceptor.receiver + respondsToSelector:@selector + (collectionView:didSelectItemAtIndexPath:)]) { + [delegate_interceptor.receiver collectionView:collectionView + didSelectItemAtIndexPath:indexPath]; + } } #pragma mark - UICollectionViewDataSource --(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - UICollectionViewCell* cell = [datasource_interceptor.receiver collectionView:collectionView cellForItemAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVReviewCollectionViewCell class]]) { - - BVReviewCollectionViewCell* bvCell = (BVReviewCollectionViewCell*)cell; - BVReview *review = bvCell.review; - if(review != nil){ - [cellToProductMap setObject:review forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have review set - NSString* message = @"BVReviewCollectionViewCell has nil `review` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + UICollectionViewCell *cell = + [datasource_interceptor.receiver collectionView:collectionView + cellForItemAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVReviewCollectionViewCell class]]) { + + BVReviewCollectionViewCell *bvCell = (BVReviewCollectionViewCell *)cell; + BVReview *review = bvCell.review; + if (review != nil) { + [cellToProductMap setObject:review + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have review set + NSString *message = @"BVReviewCollectionViewCell has nil `review` " + @"property. This must be set in " + @"`cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } - - return cell; - -} + } --(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [datasource_interceptor.receiver collectionView:collectionView numberOfItemsInSection:section]; + return cell; } +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [datasource_interceptor.receiver collectionView:collectionView + numberOfItemsInSection:section]; +} @end diff --git a/Pod/BVConversations/Views/Reviews/BVReviewsTableView.h b/Pod/BVConversations/Views/Reviews/BVReviewsTableView.h index 315238f8..ddb9935d 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewsTableView.h +++ b/Pod/BVConversations/Views/Reviews/BVReviewsTableView.h @@ -7,14 +7,15 @@ #import -#import "BVViewsHelper.h" #import "BVReviewsRequest.h" +#import "BVViewsHelper.h" /// A sub-classed UITableView for displaying BVReviewTableViewCells @interface BVReviewsTableView : UITableView -/// Helper method to asynchronously load the Reviews for a given request. This helper also ensures the proper analytic tracking is fired for reporting. -- (void)load:(nonnull BVReviewsRequest*)request +/// Helper method to asynchronously load the Reviews for a given request. This +/// helper also ensures the proper analytic tracking is fired for reporting. +- (void)load:(nonnull BVReviewsRequest *)request success:(nonnull ReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure; diff --git a/Pod/BVConversations/Views/Reviews/BVReviewsTableView.m b/Pod/BVConversations/Views/Reviews/BVReviewsTableView.m index 999cf1f9..a5accbd7 100644 --- a/Pod/BVConversations/Views/Reviews/BVReviewsTableView.m +++ b/Pod/BVConversations/Views/Reviews/BVReviewsTableView.m @@ -6,189 +6,192 @@ // #import "BVReviewsTableView.h" +#import "BVCore.h" #import "BVMessageInterceptor.h" #import "BVReviewTableViewCell.h" -#import "BVCore.h" #import "BVViewsHelper.h" -@interface BVReviewsTableView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentInViewEvent; - bool hasEnteredView; - NSMutableDictionary* cellToProductMap; - NSString *productId; +@interface BVReviewsTableView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentInViewEvent; + bool hasEnteredView; + NSMutableDictionary *cellToProductMap; + NSString *productId; } @end @implementation BVReviewsTableView +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; +} --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; +} --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; -} - --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; -} - --(void)tryToSendInViewEvent { - - if (hasEnteredView && productId != nil) { - if (!hasSentInViewEvent){ - hasSentInViewEvent = true; - - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withContainerId:@"ReviewsTableView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - - } +- (void)tryToSendInViewEvent { + + if (hasEnteredView && productId != nil) { + if (!hasSentInViewEvent) { + hasSentInViewEvent = true; + + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withContainerId:@"ReviewsTableView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - + } } --(void)willMoveToWindow:(UIWindow *)newWindow { - [super willMoveToWindow:newWindow]; - hasEnteredView = true; - - [self tryToSendInViewEvent]; +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + hasEnteredView = true; + + [self tryToSendInViewEvent]; } -- (void)load:(nonnull BVReviewsRequest*)request +- (void)load:(nonnull BVReviewsRequest *)request success:(nonnull ReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure { - - productId = request.productId; - [self tryToSendInViewEvent]; - - [request load:success failure:failure]; - -} + productId = request.productId; + [self tryToSendInViewEvent]; + + [request load:success failure:failure]; +} --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } #pragma mark UITableViewDataSource -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [datasource_interceptor.receiver tableView:tableView numberOfRowsInSection:section]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - UITableViewCell* cell = [datasource_interceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVReviewTableViewCell class]]) { - - BVReviewTableViewCell* reviewCell = (BVReviewTableViewCell*)cell; - BVReview* review = reviewCell.review; - if(review != nil){ - [cellToProductMap setObject:review forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have review set - NSString* message = @"BVReviewsTableViewCell has nil `review` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - - } - - return cell; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [datasource_interceptor.receiver tableView:tableView + numberOfRowsInSection:section]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { -#pragma mark UITableViewDelegate + UITableViewCell *cell = [datasource_interceptor.receiver tableView:tableView + cellForRowAtIndexPath:indexPath]; -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - if([delegate_interceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { - [delegate_interceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath]; + if ([cell isKindOfClass:[BVReviewTableViewCell class]]) { + + BVReviewTableViewCell *reviewCell = (BVReviewTableViewCell *)cell; + BVReview *review = reviewCell.review; + if (review != nil) { + [cellToProductMap setObject:review + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have review set + NSString *message = @"BVReviewsTableViewCell has nil `review` property. " + @"This must be set in `cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } + } + + return cell; } -#pragma mark UIScrollViewDelegate +#pragma mark UITableViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - } - +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { + [delegate_interceptor.receiver tableView:tableView + didSelectRowAtIndexPath:indexPath]; + } } --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - +#pragma mark UIScrollViewDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + [BVPixel trackEvent:scrollEvent]; - + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; } @end diff --git a/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.h b/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.h index 51e3dac1..e80e1c6d 100644 --- a/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.h +++ b/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.h @@ -7,14 +7,16 @@ #import -#import "BVViewsHelper.h" #import "BVStoreReviewsRequest.h" +#import "BVViewsHelper.h" -/// A sub-classed UITableView for displaying BVReviewTableViewCells, where review are from a parent BVStore. +/// A sub-classed UITableView for displaying BVReviewTableViewCells, where +/// review are from a parent BVStore. @interface BVStoreReviewsTableView : UITableView -/// Helper method to asynchronously load the Reviews for a given request. This helper also ensures the proper analytic tracking is fired for reporting. -- (void)load:(nonnull BVStoreReviewsRequest*)request +/// Helper method to asynchronously load the Reviews for a given request. This +/// helper also ensures the proper analytic tracking is fired for reporting. +- (void)load:(nonnull BVStoreReviewsRequest *)request success:(nonnull StoreReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure; diff --git a/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.m b/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.m index 64f4ac67..119ca5dd 100644 --- a/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.m +++ b/Pod/BVConversations/Views/Reviews/Stores/BVStoreReviewsTableView.m @@ -6,188 +6,193 @@ // #import "BVStoreReviewsTableView.h" +#import "BVCore.h" #import "BVMessageInterceptor.h" #import "BVReviewTableViewCell.h" -#import "BVCore.h" #import "BVViewsHelper.h" -@interface BVStoreReviewsTableView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentInViewEvent; - bool hasEnteredView; - NSMutableDictionary* cellToProductMap; - NSString *productId; +@interface BVStoreReviewsTableView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentInViewEvent; + bool hasEnteredView; + NSMutableDictionary *cellToProductMap; + NSString *productId; } @end @implementation BVStoreReviewsTableView +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; +} --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + [super setDataSource:(id)datasource_interceptor]; +} --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; -} - --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - [super setDataSource:(id)datasource_interceptor]; -} - --(void)tryToSendInViewEvent { - - if (hasEnteredView && productId != nil) { - if (!hasSentInViewEvent){ - hasSentInViewEvent = true; - - BVInViewEvent *inView = [[BVInViewEvent alloc] initWithProductId:productId withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withContainerId:@"SoreReviewsTableView" - withAdditionalParams:nil]; - - [BVPixel trackEvent:inView]; - - } +- (void)tryToSendInViewEvent { + + if (hasEnteredView && productId != nil) { + if (!hasSentInViewEvent) { + hasSentInViewEvent = true; + + BVInViewEvent *inView = [[BVInViewEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withContainerId:@"SoreReviewsTableView" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inView]; } - + } } --(void)willMoveToWindow:(UIWindow *)newWindow { - [super willMoveToWindow:newWindow]; - hasEnteredView = true; - - [self tryToSendInViewEvent]; +- (void)willMoveToWindow:(UIWindow *)newWindow { + [super willMoveToWindow:newWindow]; + hasEnteredView = true; + + [self tryToSendInViewEvent]; } -- (void)load:(nonnull BVStoreReviewsRequest*)request +- (void)load:(nonnull BVStoreReviewsRequest *)request success:(nonnull StoreReviewRequestCompletionHandler)success failure:(nonnull ConversationsFailureHandler)failure { - - productId = request.storeId; - [self tryToSendInViewEvent]; - - [request load:success failure:failure]; - -} + productId = request.storeId; + [self tryToSendInViewEvent]; + + [request load:success failure:failure]; +} --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } #pragma mark UITableViewDataSource -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return [datasource_interceptor.receiver tableView:tableView numberOfRowsInSection:section]; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ - - UITableViewCell* cell = [datasource_interceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVReviewTableViewCell class]]) { - - BVReviewTableViewCell* reviewCell = (BVReviewTableViewCell*)cell; - BVReview* review = reviewCell.review; - if(review != nil){ - [cellToProductMap setObject:review forKey:[BVViewsHelper formatIndex:indexPath]]; - } - else { - // error, cell must have review set - NSString* message = @"BVReviewsTableViewCell has nil `review` property. This must be set in `cellForItemAtIndexPath`."; - [[BVLogger sharedLogger] error:message]; - NSAssert(false, message); - } - - } - - return cell; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [datasource_interceptor.receiver tableView:tableView + numberOfRowsInSection:section]; } +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { -#pragma mark UITableViewDelegate + UITableViewCell *cell = [datasource_interceptor.receiver tableView:tableView + cellForRowAtIndexPath:indexPath]; -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - - if([delegate_interceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { - [delegate_interceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath]; + if ([cell isKindOfClass:[BVReviewTableViewCell class]]) { + + BVReviewTableViewCell *reviewCell = (BVReviewTableViewCell *)cell; + BVReview *review = reviewCell.review; + if (review != nil) { + [cellToProductMap setObject:review + forKey:[BVViewsHelper formatIndex:indexPath]]; + } else { + // error, cell must have review set + NSString *message = @"BVReviewsTableViewCell has nil `review` property. " + @"This must be set in `cellForItemAtIndexPath`."; + [[BVLogger sharedLogger] error:message]; + NSAssert(false, message); } + } + + return cell; } -#pragma mark UIScrollViewDelegate +#pragma mark UITableViewDelegate --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - } - +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { + [delegate_interceptor.receiver tableView:tableView + didSelectRowAtIndexPath:indexPath]; + } } --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:productId - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - +#pragma mark UIScrollViewDelegate + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } + + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + [BVPixel trackEvent:scrollEvent]; - + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:productId + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; } @end diff --git a/Pod/BVCurations/BVCurations.h b/Pod/BVCurations/BVCurations.h index f1656903..7bfbbfb5 100644 --- a/Pod/BVCurations/BVCurations.h +++ b/Pod/BVCurations/BVCurations.h @@ -10,12 +10,11 @@ #define BVCurations_h #import "BVCore.h" +#import "BVCurationsAddPostRequest.h" +#import "BVCurationsFeedItem.h" #import "BVCurationsFeedLoader.h" #import "BVCurationsFeedRequest.h" -#import "BVCurationsFeedLoader.h" -#import "BVPixel.h" -#import "BVCurationsFeedItem.h" -#import "BVCurationsAddPostRequest.h" #import "BVCurationsPhotoUploader.h" +#import "BVPixel.h" #endif diff --git a/Pod/BVCurations/BVCurationsAddPostRequest.h b/Pod/BVCurations/BVCurationsAddPostRequest.h index 54eeebc2..a96087a2 100644 --- a/Pod/BVCurations/BVCurationsAddPostRequest.h +++ b/Pod/BVCurations/BVCurationsAddPostRequest.h @@ -6,97 +6,109 @@ // // +#import "BVCurationsFeedItem.h" +#import #import #import -#import -#import "BVCurationsFeedItem.h" - -NS_ASSUME_NONNULL_BEGIN @interface BVCurationsAddPostRequest : NSObject /*! Initialize the parameters with the always required groups parameter - - @param groups Array of NSStrings that specifies which Curations group or groups to post to. + + @param groups Array of NSStrings that specifies which Curations group or groups + to post to. @param alias The author's alias @param token Unique identifier for this author/alias @param text The user's comment for this post object - - @return Fully initialized BVCurationsAddPostParams with the minimum required parameters. - - */ -- (id)initWithGroups:(NSArray*)groups withAuthorAlias:(NSString *)alias withToken:(NSString *)token withText:(NSString *)text; + @return Fully initialized BVCurationsAddPostParams with the minimum required + parameters. + + */ +- (nonnull id)initWithGroups:(nonnull NSArray *)groups + withAuthorAlias:(nonnull NSString *)alias + withToken:(nonnull NSString *)token + withText:(nonnull NSString *)text; /*! Initialize the parameters with the always required groups parameter - - @param groups Array of NSStrings that specifies which Curations group or groups to post to. + + @param groups Array of NSStrings that specifies which Curations group or groups + to post to. @param alias The author's alias @param token Unique identifier for this author/alias @param text The user's comment for this post object @param image The UIImage supplied by the user for upload - - @return Fully initialized BVCurationsAddPostParams with the minimum required parameters. - + + @return Fully initialized BVCurationsAddPostParams with the minimum required + parameters. + */ -- (id)initWithGroups:(NSArray*)groups withAuthorAlias:(NSString *)alias withToken:(NSString *)token withText:(NSString *)text withImage:(UIImage *)image; +- (nonnull id)initWithGroups:(nonnull NSArray *)groups + withAuthorAlias:(nonnull NSString *)alias + withToken:(nonnull NSString *)token + withText:(nonnull NSString *)text + withImage:(nonnull UIImage *)image; /*! * Unavailable, use the designated initializer. * * @return nil */ -- (instancetype)init __attribute__((unavailable("Use -initWithGroups: instead"))); - +- (nonnull instancetype)init + __attribute__((unavailable("Use -initWithGroups: instead"))); /*! JSON Serialize all parameters set on this object. */ -- (NSData *)serializeParameters; - +- (nonnull NSData *)serializeParameters; /////////////////////// // Readonly parameters /////////////////////// -/*!Array of NSStrings that specifies which Curations group or groups to post to. Set this with the designated initializer. */ -@property (readonly) NSArray*groups; +/*!Array of NSStrings that specifies which Curations group or groups to post to. + * Set this with the designated initializer. */ +@property(nonnull, readonly) NSArray *groups; /*! The author's alias. Set this with the designated initializer. */ -@property (readonly) NSString *alias; +@property(nonnull, readonly) NSString *alias; -/*! The user's comment for this post object. Set this with the designated initializer. */ -@property (readonly) NSString *token; +/*! The user's comment for this post object. Set this with the designated + * initializer. */ +@property(nonnull, readonly) NSString *token; ///////////////////////////////////////// // Optional parameters that can be set ///////////////////////////////////////// /*! User's comment on the post. Set this with the designated initializer. */ -@property NSString *text; +@property(nonnull) NSString *text; /*! The URL string to an avatar for the author. */ -@property NSString *authorAvatarURL; +@property(nonnull) NSString *authorAvatarURL; /*! The URL to a profile page for the author. */ -@property NSString *authorProfileURL; +@property(nonnull) NSString *authorProfileURL; -/*! A list of tags, with each tag specified as a string, to be added to the post. Example: @["ffiv", "cecil"] */ -@property NSArray*tags; +/*! A list of tags, with each tag specified as a string, to be added to the + * post. Example: @["ffiv", "cecil"] */ +@property(nonnull) NSArray *tags; /*! The URL to a permalink correspoding with this update. */ -@property NSString *permalink; +@property(nonnull) NSString *permalink; /*! A human readable place, such as "Austin, TX" or "Mysidia". */ -@property NSString *place; +@property(nonnull) NSString *place; /*! A longer block of teaser text, such as we send down for articles. */ -@property NSString *teaser; +@property(nonnull) NSString *teaser; -/*! The Unix timestamp for then this update was considered to be created. If you don't send this, we'll use the time that it was added to our system as the default. */ -@property double unixTimeStamp; +/*! The Unix timestamp for then this update was considered to be created. If you + * don't send this, we'll use the time that it was added to our system as the + * default. */ +@property double unixTimeStamp; /*! The longitude attributed to the post. */ @property double longitude; @@ -105,14 +117,15 @@ NS_ASSUME_NONNULL_BEGIN @property double latitude; /*! The UIImage asset to be uploaded to Curations */ -@property UIImage *image; +@property(nonnull) UIImage *image; -/*! Attach URL strings for links for custom udpates. These links will be attached to the post. */ -@property NSArray*links; +/*! Attach URL strings for links for custom udpates. These links will be + * attached to the post. */ +@property(nonnull) NSArray *links; -/*! Attach URLs to photos for custom udpates. These images displayed from the fully-qualified URLs will be displayed in the post. When this option is used, the 'image' value will be ignored. */ -@property NSArray*photos; +/*! Attach URLs to photos for custom udpates. These images displayed from the + * fully-qualified URLs will be displayed in the post. When this option is used, + * the 'image' value will be ignored. */ +@property(nonnull) NSArray *photos; @end - -NS_ASSUME_NONNULL_END diff --git a/Pod/BVCurations/BVCurationsAddPostRequest.m b/Pod/BVCurations/BVCurationsAddPostRequest.m index d84c501c..d8f6cf22 100644 --- a/Pod/BVCurations/BVCurationsAddPostRequest.m +++ b/Pod/BVCurations/BVCurationsAddPostRequest.m @@ -10,129 +10,146 @@ @implementation BVCurationsAddPostRequest -- (id)initWithGroups:(NSArray*)groups withAuthorAlias:(NSString *)alias withToken:(NSString *)token withText:(NSString *)text{ - - self = [super init]; - if (self){ - _groups = groups; - _alias = alias; - _token = token; - _text = text; - _unixTimeStamp = 0; - _latitude = 0; - _longitude = 0; - - _photos = [NSArray array]; - _links = [NSArray array]; - } - - return self; - -} +- (id)initWithGroups:(NSArray *)groups + withAuthorAlias:(NSString *)alias + withToken:(NSString *)token + withText:(NSString *)text { -- (id)initWithGroups:(NSArray*)groups withAuthorAlias:(NSString *)alias withToken:(NSString *)token withText:(NSString *)text withImage:(UIImage *)image{ - - self = [self initWithGroups:groups withAuthorAlias:alias withToken:token withText:text]; - if (self){ - _image = image; - } - - return self; + self = [super init]; + if (self) { + _groups = groups; + _alias = alias; + _token = token; + _text = text; + _unixTimeStamp = 0; + _latitude = 0; + _longitude = 0; + + _photos = [NSArray array]; + _links = [NSArray array]; + } + + return self; } +- (id)initWithGroups:(NSArray *)groups + withAuthorAlias:(NSString *)alias + withToken:(NSString *)token + withText:(NSString *)text + withImage:(UIImage *)image { + + self = [self initWithGroups:groups + withAuthorAlias:alias + withToken:token + withText:text]; + if (self) { + _image = image; + } + + return self; +} - (NSDictionary *)toDictionary { - - // Create the dictionary - - NSMutableDictionary *authorDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:self.alias, @"alias", self.token, @"token", nil]; // author dict - - if (self.authorAvatarURL){ - [authorDict setObject:self.authorAvatarURL forKey:@"avatar"]; - } - - if (self.authorProfileURL){ - [authorDict setObject:self.authorProfileURL forKey:@"profile"]; - } - - NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:self.groups, @"groups", authorDict, @"author", self.text, @"text", nil]; - - // Serialize the dictionary - - if (self.tags){ - [params setObject:self.tags forKey:@"tags"]; - } - - if (self.permalink){ - [params setObject:self.permalink forKey:@"permalink"]; - } - - if (self.place){ - [params setObject:self.place forKey:@"place"]; - } - - if (self.teaser){ - [params setObject:self.teaser forKey:@"teaser"]; - } - - if (self.unixTimeStamp > 0){ - [params setObject:[NSNumber numberWithDouble:self.unixTimeStamp]forKey:@"timestamp"]; - } - - if (self.longitude != 0 && self.latitude != 0){ - - NSNumber *longNum = [NSNumber numberWithDouble:self.longitude]; - NSNumber *latNum = [NSNumber numberWithDouble:self.latitude]; - - NSDictionary *coordinatesDict = [NSDictionary dictionaryWithObjectsAndKeys:latNum, @"x", longNum, @"y", nil]; - - [params setObject:coordinatesDict forKey:@"coordinates"]; - } - if (self.links.count > 0){ - NSMutableArray *linksArray = [NSMutableArray array]; - for (NSString *link in self.links){ - [linksArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:link, @"url", nil]]; - } - - [params setObject:linksArray forKey:@"links"]; + // Create the dictionary + + NSMutableDictionary *authorDict = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.alias, @"alias", self.token, @"token", + nil]; // author dict + + if (self.authorAvatarURL) { + [authorDict setObject:self.authorAvatarURL forKey:@"avatar"]; + } + + if (self.authorProfileURL) { + [authorDict setObject:self.authorProfileURL forKey:@"profile"]; + } + + NSMutableDictionary *params = [NSMutableDictionary + dictionaryWithObjectsAndKeys:self.groups, @"groups", authorDict, + @"author", self.text, @"text", nil]; + + // Serialize the dictionary + if (self.tags) { + [params setObject:self.tags forKey:@"tags"]; + } + + if (self.permalink) { + [params setObject:self.permalink forKey:@"permalink"]; + } + + if (self.place) { + [params setObject:self.place forKey:@"place"]; + } + + if (self.teaser) { + [params setObject:self.teaser forKey:@"teaser"]; + } + + if (self.unixTimeStamp > 0) { + [params setObject:[NSNumber numberWithDouble:self.unixTimeStamp] + forKey:@"timestamp"]; + } + + if (self.longitude != 0 && self.latitude != 0) { + + NSNumber *longNum = [NSNumber numberWithDouble:self.longitude]; + NSNumber *latNum = [NSNumber numberWithDouble:self.latitude]; + + NSDictionary *coordinatesDict = [NSDictionary + dictionaryWithObjectsAndKeys:latNum, @"x", longNum, @"y", nil]; + + [params setObject:coordinatesDict forKey:@"coordinates"]; + } + + if (self.links.count > 0) { + NSMutableArray *linksArray = [NSMutableArray array]; + for (NSString *link in self.links) { + [linksArray + addObject:[NSDictionary + dictionaryWithObjectsAndKeys:link, @"url", nil]]; } - - if (self.photos.count > 0){ - NSMutableArray *photosArray = [NSMutableArray array]; - for (NSString *photo in self.photos){ - [photosArray addObject:[NSDictionary dictionaryWithObjectsAndKeys:photo, @"remote_url", nil]]; - } - - [params setObject:photosArray forKey:@"photos"]; + + [params setObject:linksArray forKey:@"links"]; + } + + if (self.photos.count > 0) { + NSMutableArray *photosArray = [NSMutableArray array]; + for (NSString *photo in self.photos) { + [photosArray + addObject:[NSDictionary dictionaryWithObjectsAndKeys:photo, + @"remote_url", + nil]]; } - - return params; - + + [params setObject:photosArray forKey:@"photos"]; + } + + return params; } - (NSData *)serializeParameters { - - NSDictionary *paramsDict = [self toDictionary]; - - NSError *error; - NSData *data = [NSJSONSerialization dataWithJSONObject:paramsDict options:kNilOptions error:&error]; - - if (error){ - - return nil; - } - - return data; - + + NSDictionary *paramsDict = [self toDictionary]; + + NSError *error; + NSData *data = [NSJSONSerialization dataWithJSONObject:paramsDict + options:kNilOptions + error:&error]; + + if (error) { + + return nil; + } + + return data; } - (NSString *)description { - - return [[NSString alloc] initWithData:self.serializeParameters encoding:NSUTF8StringEncoding]; - -} + return [[NSString alloc] initWithData:self.serializeParameters + encoding:NSUTF8StringEncoding]; +} @end diff --git a/Pod/BVCurations/BVCurationsFeedItem.h b/Pod/BVCurations/BVCurationsFeedItem.h index ed8c5a64..abebf916 100644 --- a/Pod/BVCurations/BVCurationsFeedItem.h +++ b/Pod/BVCurations/BVCurationsFeedItem.h @@ -6,8 +6,8 @@ // // -#import #import "BVDisplayableProductContent.h" +#import // Forward declarations: classes are contained in a BVCurationsFeedItem object. @class BVCurationsCoordinates; @@ -19,123 +19,125 @@ /// Top-level Curations model object in a Curations feed. @interface BVCurationsFeedItem : NSObject - @property NSString* channel; - @property NSString* contentId; - @property NSNumber* rating; - @property NSString* classification; - @property NSString* text; - @property NSNumber* identifier; - @property NSNumber* praises; - @property NSString* explicitPermissionStatus; - @property BVCurationsPostAuthor* author; - @property NSArray< NSString* >* links; - @property BVCurationsCoordinates* coordinates; - @property NSArray< NSString* >* featuredGroups; - @property NSArray< NSString* >* tags; - @property NSNumber* timestamp; - @property NSArray *photos; - @property NSArray *videos; - @property NSArray *referencedProducts; - @property NSDictionary *productDetails; - @property NSString* teaser; - @property NSArray< NSString* >* groups; - @property NSString* permalink; - @property NSString* productId; - @property NSString* language; - @property NSString* token; - @property NSString* place; - @property NSString* replyTo; - @property NSString* sourceClient; - - /// As provided by the external ID BVCurationsFeedParams value. When supplied, this is used as the product Id for analytics reporting. - @property NSString *externalId; - - -(id)initWithDict:(NSDictionary*)dict withReferencedProducts:(NSDictionary *)referencedProducts; - - -(id)initWithDict:(NSDictionary*)dict withReferencedProducts:(NSDictionary *)referencedProducts withExternalId:(NSString *)exernalId; - - -(void)recordTap; - - -(void)recordImpression; +@property NSString *channel; +@property NSString *contentId; +@property NSNumber *rating; +@property NSString *classification; +@property NSString *text; +@property NSNumber *identifier; +@property NSNumber *praises; +@property NSString *explicitPermissionStatus; +@property BVCurationsPostAuthor *author; +@property NSArray *links; +@property BVCurationsCoordinates *coordinates; +@property NSArray *featuredGroups; +@property NSArray *tags; +@property NSNumber *timestamp; +@property NSArray *photos; +@property NSArray *videos; +@property NSArray *referencedProducts; +@property NSDictionary *productDetails; +@property NSString *teaser; +@property NSArray *groups; +@property NSString *permalink; +@property NSString *productId; +@property NSString *language; +@property NSString *token; +@property NSString *place; +@property NSString *replyTo; +@property NSString *sourceClient; + +/// As provided by the external ID BVCurationsFeedParams value. When supplied, +/// this is used as the product Id for analytics reporting. +@property NSString *externalId; + +- (id)initWithDict:(NSDictionary *)dict + withReferencedProducts:(NSDictionary *)referencedProducts; + +- (id)initWithDict:(NSDictionary *)dict + withReferencedProducts:(NSDictionary *)referencedProducts + withExternalId:(NSString *)exernalId; + +- (void)recordTap; + +- (void)recordImpression; @end - -/// Lat/Long coordinates for a given BVCurationsFeedItem objects. Items may be nil. +/// Lat/Long coordinates for a given BVCurationsFeedItem objects. Items may be +/// nil. @interface BVCurationsCoordinates : NSObject - @property NSNumber* latitude; - @property NSNumber* longitude; +@property NSNumber *latitude; +@property NSNumber *longitude; - -(id)initWithDict:(NSDictionary*)dict; +- (id)initWithDict:(NSDictionary *)dict; @end - /// Attributes for a single photo in a BVCurationsFeedItem object. @interface BVCurationsPhoto : NSObject - @property NSString* origin; - @property NSString* permalink; - @property NSString* token; - @property NSString* role; - @property NSString* displayUrl; - @property NSString* url; - @property NSString* imageServiceUrl; - @property NSNumber* identifier; - @property NSString* localUrl; +@property NSString *origin; +@property NSString *permalink; +@property NSString *token; +@property NSString *role; +@property NSString *displayUrl; +@property NSString *url; +@property NSString *imageServiceUrl; +@property NSNumber *identifier; +@property NSString *localUrl; - -(id)initWithDict:(NSDictionary*)dict; +- (id)initWithDict:(NSDictionary *)dict; @end - /// Attributes for a single video in a BVCurationsFeedItem object. @interface BVCurationsVideo : NSObject - @property NSString* origin; - @property NSString* permalink; - @property NSString* code; - @property NSString* imageUrl; - @property NSString* token; - @property NSString* displayUrl; - @property NSString* imageServiceUrl; - @property NSNumber* identifier; - @property NSString* remoteUrl; - @property NSString* videoType; +@property NSString *origin; +@property NSString *permalink; +@property NSString *code; +@property NSString *imageUrl; +@property NSString *token; +@property NSString *displayUrl; +@property NSString *imageServiceUrl; +@property NSNumber *identifier; +@property NSString *remoteUrl; +@property NSString *videoType; - -(id)initWithDict:(NSDictionary*)dict; +- (id)initWithDict:(NSDictionary *)dict; @end - /// Attributes the author of a single BVCurationsFeedItem object. @interface BVCurationsPostAuthor : NSObject - @property NSString* profile; - @property NSString* username; - @property NSString* alias; - @property NSString* token; - @property NSString* avatar; - @property NSString* channel; +@property NSString *profile; +@property NSString *username; +@property NSString *alias; +@property NSString *token; +@property NSString *avatar; +@property NSString *channel; - -(id)initWithDict:(NSDictionary*)dict; +- (id)initWithDict:(NSDictionary *)dict; @end - -/// Attributes for a single product reference. This object is referenced by a single BVCurationsFeedItem and can be used to show the user the related items and how to buy it. -@interface BVCurationsProductDetail : NSObject - - @property NSString *productKey; - @property NSString *productId; - @property NSString *productImageUrl; - @property NSString *productName; - @property NSString *productPageUrl; - @property NSString *productDescription; - @property NSNumber *totalReviewCount; // int - @property NSNumber *avgRating; // float - - -(id)initWithDict:(NSDictionary*)dict withKey:(NSString *)key; +/// Attributes for a single product reference. This object is referenced by a +/// single BVCurationsFeedItem and can be used to show the user the related +/// items and how to buy it. +@interface BVCurationsProductDetail : NSObject + +@property NSString *productKey; +@property NSString *productId; +@property NSString *productImageUrl; +@property NSString *productName; +@property NSString *productPageUrl; +@property NSString *productDescription; +@property NSNumber *totalReviewCount; // int +@property NSNumber *avgRating; // float + +- (id)initWithDict:(NSDictionary *)dict withKey:(NSString *)key; @end diff --git a/Pod/BVCurations/BVCurationsFeedItem.m b/Pod/BVCurations/BVCurationsFeedItem.m index e3c9551c..41b126e4 100644 --- a/Pod/BVCurations/BVCurationsFeedItem.m +++ b/Pod/BVCurations/BVCurationsFeedItem.m @@ -6,10 +6,10 @@ // // -#import "BVCore.h" #import "BVCurationsFeedItem.h" +#import "BVCore.h" -@interface BVCurationsFeedItem() +@interface BVCurationsFeedItem () @property bool hasSentImpressionEvent; @@ -17,245 +17,252 @@ @interface BVCurationsFeedItem() @implementation BVCurationsFeedItem --(id)initWithDict:(NSDictionary*)dict withReferencedProducts:(NSDictionary *)referencedProducts withExternalId:(NSString *)externalId { +- (id)initWithDict:(NSDictionary *)dict + withReferencedProducts:(NSDictionary *)referencedProducts + withExternalId:(NSString *)externalId { - self = [self initWithDict:dict withReferencedProducts:referencedProducts]; - - if (self){ - - self.externalId = externalId; - - } - - return self; - + self = [self initWithDict:dict withReferencedProducts:referencedProducts]; + + if (self) { + + self.externalId = externalId; + } + + return self; } --(id)initWithDict:(NSDictionary*)dict withReferencedProducts:(NSDictionary *)referencedProducts { - self = [super init]; - if(self){ - - if (dict != nil){ - - self.channel = [dict objectForKey:@"channel"]; - self.contentId = [dict objectForKey:@"id"]; - self.rating = [dict objectForKey:@"rating"]; - self.classification = [dict objectForKey:@"classification"]; - self.text = [dict objectForKey:@"text"]; - self.identifier = [dict objectForKey:@"id"]; - self.praises = [dict objectForKey:@"praises"]; - self.explicitPermissionStatus = [dict objectForKey:@"explicit_permission_status"]; - self.author = [[BVCurationsPostAuthor alloc] initWithDict:[dict objectForKey:@"author"]]; - self.links = [dict objectForKey:@"links"]; - self.coordinates = [[BVCurationsCoordinates alloc] initWithDict:[dict objectForKey:@"coordinates"]]; - self.featuredGroups = [dict objectForKey:@"featured_groups"]; - self.videos = [self getVideos:[dict objectForKey:@"videos"]]; - self.tags = [dict objectForKey:@"tags"]; - self.timestamp = [dict objectForKey:@"timestamp"]; - self.photos = [self getPhotos:[dict objectForKey:@"photos"]]; - self.teaser = [dict objectForKey:@"teaser"]; - self.groups = [dict objectForKey:@"groups"]; - self.permalink = [dict objectForKey:@"permalink"]; - self.language = [dict objectForKey:@"language"]; - self.token = [dict objectForKey:@"token"]; - self.place = [dict objectForKey:@"place"]; - self.replyTo = [dict objectForKey:@"reply_to"]; - self.sourceClient = [dict objectForKey:@"sourceClient"]; - - // If we have tags and referenced products we can build the product dictionary model - - self.referencedProducts = [NSArray array]; - if (referencedProducts != nil && self.tags != nil && referencedProducts.allKeys.count > 0 && self.tags.count > 0){ - - NSMutableArray *tmpProducts = [NSMutableArray array]; - - for (NSString *tag in self.tags){ - - BVCurationsProductDetail *item = [referencedProducts objectForKey:tag]; - if (item){ - - [tmpProducts addObject:item]; - - } - - } - - self.referencedProducts = [NSArray arrayWithArray:tmpProducts]; - - } - +- (id)initWithDict:(NSDictionary *)dict + withReferencedProducts:(NSDictionary *)referencedProducts { + self = [super init]; + if (self) { + + if (dict != nil) { + + self.channel = [dict objectForKey:@"channel"]; + self.contentId = [dict objectForKey:@"id"]; + self.rating = [dict objectForKey:@"rating"]; + self.classification = [dict objectForKey:@"classification"]; + self.text = [dict objectForKey:@"text"]; + self.identifier = [dict objectForKey:@"id"]; + self.praises = [dict objectForKey:@"praises"]; + self.explicitPermissionStatus = + [dict objectForKey:@"explicit_permission_status"]; + self.author = [[BVCurationsPostAuthor alloc] + initWithDict:[dict objectForKey:@"author"]]; + self.links = [dict objectForKey:@"links"]; + self.coordinates = [[BVCurationsCoordinates alloc] + initWithDict:[dict objectForKey:@"coordinates"]]; + self.featuredGroups = [dict objectForKey:@"featured_groups"]; + self.videos = [self getVideos:[dict objectForKey:@"videos"]]; + self.tags = [dict objectForKey:@"tags"]; + self.timestamp = [dict objectForKey:@"timestamp"]; + self.photos = [self getPhotos:[dict objectForKey:@"photos"]]; + self.teaser = [dict objectForKey:@"teaser"]; + self.groups = [dict objectForKey:@"groups"]; + self.permalink = [dict objectForKey:@"permalink"]; + self.language = [dict objectForKey:@"language"]; + self.token = [dict objectForKey:@"token"]; + self.place = [dict objectForKey:@"place"]; + self.replyTo = [dict objectForKey:@"reply_to"]; + self.sourceClient = [dict objectForKey:@"sourceClient"]; + + // If we have tags and referenced products we can build the product + // dictionary model + + self.referencedProducts = [NSArray array]; + if (referencedProducts != nil && self.tags != nil && + referencedProducts.allKeys.count > 0 && self.tags.count > 0) { + + NSMutableArray *tmpProducts = [NSMutableArray array]; + + for (NSString *tag in self.tags) { + + BVCurationsProductDetail *item = + [referencedProducts objectForKey:tag]; + if (item) { + + [tmpProducts addObject:item]; + } } - + + self.referencedProducts = [NSArray arrayWithArray:tmpProducts]; + } } - return self; + } + return self; } --(NSArray*)getPhotos:(NSDictionary*)arrayOfPhotos { - NSMutableArray* photos = [NSMutableArray array]; - - for(NSDictionary* photo in arrayOfPhotos){ - [photos addObject:[[BVCurationsPhoto alloc] initWithDict:photo]]; - } - - return photos; +- (NSArray *)getPhotos:(NSDictionary *)arrayOfPhotos { + NSMutableArray *photos = [NSMutableArray array]; + + for (NSDictionary *photo in arrayOfPhotos) { + [photos addObject:[[BVCurationsPhoto alloc] initWithDict:photo]]; + } + + return photos; } --(NSArray*)getVideos:(NSDictionary*)arrayOfVideos { - NSMutableArray* videos = [NSMutableArray array]; - - for(NSDictionary* video in arrayOfVideos){ - [videos addObject:[[BVCurationsVideo alloc] initWithDict:video]]; - } - - return videos; +- (NSArray *)getVideos:(NSDictionary *)arrayOfVideos { + NSMutableArray *videos = [NSMutableArray array]; + + for (NSDictionary *video in arrayOfVideos) { + [videos addObject:[[BVCurationsVideo alloc] initWithDict:video]]; + } + + return videos; } --(void)recordImpression { - - if(self.hasSentImpressionEvent) { - return; - } - self.hasSentImpressionEvent = true; - - BVImpressionEvent *impression = [[BVImpressionEvent alloc] initWithProductId:self.externalId - withContentId:self.contentId - withCategoryId:nil - withProductType:BVPixelProductTypeCurations - withContentType:BVPixelImpressionContentCurationsFeedItem withBrand:nil - withAdditionalParams:@{@"syndicationSource":self.sourceClient}]; - - [BVPixel trackEvent:impression]; - +- (void)recordImpression { + + if (self.hasSentImpressionEvent) { + return; + } + self.hasSentImpressionEvent = true; + + BVImpressionEvent *impression = [[BVImpressionEvent alloc] + initWithProductId:self.externalId + withContentId:self.contentId + withCategoryId:nil + withProductType:BVPixelProductTypeCurations + withContentType:BVPixelImpressionContentCurationsFeedItem + withBrand:nil + withAdditionalParams:@{@"syndicationSource" : self.sourceClient}]; + + [BVPixel trackEvent:impression]; } --(void)recordTap { - - BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] initWithProductId:self.externalId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventContentClick withAdditionalParams:nil]; - - [BVPixel trackEvent:tapEvent]; +- (void)recordTap { + + BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:self.externalId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventContentClick + withAdditionalParams:nil]; + + [BVPixel trackEvent:tapEvent]; } +- (NSString *)description { -- (NSString *)description{ - - return [NSString stringWithFormat:@"Curations Feed Item: Channel: %@ - Text: %@", self.channel, self.text]; + return + [NSString stringWithFormat:@"Curations Feed Item: Channel: %@ - Text: %@", + self.channel, self.text]; } @end - @implementation BVCurationsCoordinates --(id)initWithDict:(NSDictionary*)dict { - self = [super init]; - if (self) { - - if (dict != nil){ - - SET_IF_NOT_NULL(self.latitude, [dict objectForKey:@"latitude"]) - SET_IF_NOT_NULL(self.longitude, [dict objectForKey:@"longitude"]) - - } +- (id)initWithDict:(NSDictionary *)dict { + self = [super init]; + if (self) { + + if (dict != nil) { + + SET_IF_NOT_NULL(self.latitude, [dict objectForKey:@"latitude"]) + SET_IF_NOT_NULL(self.longitude, [dict objectForKey:@"longitude"]) } - return self; + } + return self; } -- (NSString *)description{ - - return [NSString stringWithFormat:@"Locations: Lat:%@ / Long:%@", [NSString stringWithFormat:@"%@",self.latitude], [NSString stringWithFormat:@"%@",self.latitude]]; +- (NSString *)description { + + return [NSString + stringWithFormat:@"Locations: Lat:%@ / Long:%@", + [NSString stringWithFormat:@"%@", self.latitude], + [NSString stringWithFormat:@"%@", self.latitude]]; } @end @implementation BVCurationsPhoto --(id)initWithDict:(NSDictionary*)dict { - self = [super init]; - if (self) { - - if (dict != nil){ - - SET_IF_NOT_NULL(self.origin, [dict objectForKey:@"origin"]) - SET_IF_NOT_NULL(self.permalink, [dict objectForKey:@"permalink"]) - SET_IF_NOT_NULL(self.token, [dict objectForKey:@"dict"]) - SET_IF_NOT_NULL(self.role, [dict objectForKey:@"role"]) - SET_IF_NOT_NULL(self.displayUrl, [dict objectForKey:@"display_url"]) - SET_IF_NOT_NULL(self.url, [dict objectForKey:@"url"]) - SET_IF_NOT_NULL(self.imageServiceUrl, [dict objectForKey:@"image_service_url"]) - SET_IF_NOT_NULL(self.identifier, [dict objectForKey:@"id"]) - SET_IF_NOT_NULL(self.localUrl, [dict objectForKey:@"id"]) - - } +- (id)initWithDict:(NSDictionary *)dict { + self = [super init]; + if (self) { + + if (dict != nil) { + + SET_IF_NOT_NULL(self.origin, [dict objectForKey:@"origin"]) + SET_IF_NOT_NULL(self.permalink, [dict objectForKey:@"permalink"]) + SET_IF_NOT_NULL(self.token, [dict objectForKey:@"dict"]) + SET_IF_NOT_NULL(self.role, [dict objectForKey:@"role"]) + SET_IF_NOT_NULL(self.displayUrl, [dict objectForKey:@"display_url"]) + SET_IF_NOT_NULL(self.url, [dict objectForKey:@"url"]) + SET_IF_NOT_NULL(self.imageServiceUrl, + [dict objectForKey:@"image_service_url"]) + SET_IF_NOT_NULL(self.identifier, [dict objectForKey:@"id"]) + SET_IF_NOT_NULL(self.localUrl, [dict objectForKey:@"id"]) } - return self; + } + return self; } -- (NSString *)description{ - - return [NSString stringWithFormat:@"Photo: Origin: %@, URL: %@", self.origin, self.imageServiceUrl ]; +- (NSString *)description { + + return [NSString stringWithFormat:@"Photo: Origin: %@, URL: %@", self.origin, + self.imageServiceUrl]; } @end - @implementation BVCurationsVideo --(id)initWithDict:(NSDictionary*)dict { - self = [super init]; - if (self) { - - if (dict != nil){ - - SET_IF_NOT_NULL(self.origin, [dict objectForKey:@"origin"]) - SET_IF_NOT_NULL(self.permalink, [dict objectForKey:@"permalink"]) - SET_IF_NOT_NULL(self.code, [dict objectForKey:@"code"]) - SET_IF_NOT_NULL(self.imageUrl, [dict objectForKey:@"image_url"]) - SET_IF_NOT_NULL(self.token, [dict objectForKey:@"token"]) - SET_IF_NOT_NULL(self.displayUrl, [dict objectForKey:@"display_url"]) - SET_IF_NOT_NULL(self.imageServiceUrl, [dict objectForKey:@"image_service_url"]) - SET_IF_NOT_NULL(self.identifier, [dict objectForKey:@"id"]) - SET_IF_NOT_NULL(self.remoteUrl, [dict objectForKey:@"remote_url"]) - SET_IF_NOT_NULL(self.videoType, [dict objectForKey:@"video_type"]) - - } +- (id)initWithDict:(NSDictionary *)dict { + self = [super init]; + if (self) { + + if (dict != nil) { + + SET_IF_NOT_NULL(self.origin, [dict objectForKey:@"origin"]) + SET_IF_NOT_NULL(self.permalink, [dict objectForKey:@"permalink"]) + SET_IF_NOT_NULL(self.code, [dict objectForKey:@"code"]) + SET_IF_NOT_NULL(self.imageUrl, [dict objectForKey:@"image_url"]) + SET_IF_NOT_NULL(self.token, [dict objectForKey:@"token"]) + SET_IF_NOT_NULL(self.displayUrl, [dict objectForKey:@"display_url"]) + SET_IF_NOT_NULL(self.imageServiceUrl, + [dict objectForKey:@"image_service_url"]) + SET_IF_NOT_NULL(self.identifier, [dict objectForKey:@"id"]) + SET_IF_NOT_NULL(self.remoteUrl, [dict objectForKey:@"remote_url"]) + SET_IF_NOT_NULL(self.videoType, [dict objectForKey:@"video_type"]) } - return self; + } + return self; } -- (NSString *)description{ - - return [NSString stringWithFormat:@"Photo: Origin: %@, URL: %@", self.origin, self.imageServiceUrl ]; +- (NSString *)description { + + return [NSString stringWithFormat:@"Photo: Origin: %@, URL: %@", self.origin, + self.imageServiceUrl]; } @end - @implementation BVCurationsPostAuthor --(id)initWithDict:(NSDictionary*)dict { - self = [super init]; - if(self){ - - if (dict != nil){ - - SET_IF_NOT_NULL(self.profile, [dict objectForKey:@"profile"]) - SET_IF_NOT_NULL(self.username, [dict objectForKey:@"username"]) - SET_IF_NOT_NULL(self.alias, [dict objectForKey:@"alias"]) - SET_IF_NOT_NULL(self.token, [dict objectForKey:@"token"]) - SET_IF_NOT_NULL(self.avatar, [dict objectForKey:@"avatar"]) - SET_IF_NOT_NULL(self.channel, [dict objectForKey:@"channel"]) - - } +- (id)initWithDict:(NSDictionary *)dict { + self = [super init]; + if (self) { + + if (dict != nil) { + + SET_IF_NOT_NULL(self.profile, [dict objectForKey:@"profile"]) + SET_IF_NOT_NULL(self.username, [dict objectForKey:@"username"]) + SET_IF_NOT_NULL(self.alias, [dict objectForKey:@"alias"]) + SET_IF_NOT_NULL(self.token, [dict objectForKey:@"token"]) + SET_IF_NOT_NULL(self.avatar, [dict objectForKey:@"avatar"]) + SET_IF_NOT_NULL(self.channel, [dict objectForKey:@"channel"]) } - return self; + } + return self; } -- (NSString *)description{ - - return [NSString stringWithFormat:@"Photo: Usename: %@, Channel: %@", self.username, self.channel]; +- (NSString *)description { + + return [NSString stringWithFormat:@"Photo: Usename: %@, Channel: %@", + self.username, self.channel]; } @end @@ -267,54 +274,62 @@ @implementation BVCurationsProductDetail @synthesize displayName; @synthesize averageRating; --(id)initWithDict:(NSDictionary*)dict withKey:(NSString *)key { - - self = [super init]; - if(self){ - - if (dict != nil){ - - self.productKey = key; - SET_IF_NOT_NULL(self.productId, [dict objectForKey:@"Id"]) - SET_IF_NOT_NULL(self.productImageUrl, [dict objectForKey:@"ImageUrl"]) - SET_IF_NOT_NULL(self.productName, [dict objectForKey:@"Name"]) - SET_IF_NOT_NULL(self.productPageUrl, [dict objectForKey:@"ProductPageUrl"]) - SET_IF_NOT_NULL(self.productDescription, [dict objectForKey:@"Description"]) - - self.totalReviewCount = [NSNumber numberWithInteger:0]; - self.avgRating = [NSNumber numberWithFloat:0.0]; - - SET_IF_NOT_NULL(self.totalReviewCount, [dict objectForKey:@"TotalReviewCount"]); - - if (self.totalReviewCount > 0 && [dict objectForKey:@"ReviewStatistics"] != nil){ - SET_IF_NOT_NULL(self.avgRating, [[dict objectForKey:@"ReviewStatistics"] objectForKey:@"AverageOverallRating"]); - } - - } - +- (id)initWithDict:(NSDictionary *)dict withKey:(NSString *)key { + + self = [super init]; + if (self) { + + if (dict != nil) { + + self.productKey = key; + SET_IF_NOT_NULL(self.productId, [dict objectForKey:@"Id"]) + SET_IF_NOT_NULL(self.productImageUrl, [dict objectForKey:@"ImageUrl"]) + SET_IF_NOT_NULL(self.productName, [dict objectForKey:@"Name"]) + SET_IF_NOT_NULL(self.productPageUrl, + [dict objectForKey:@"ProductPageUrl"]) + SET_IF_NOT_NULL(self.productDescription, + [dict objectForKey:@"Description"]) + + self.totalReviewCount = [NSNumber numberWithInteger:0]; + self.avgRating = [NSNumber numberWithFloat:0.0]; + + SET_IF_NOT_NULL(self.totalReviewCount, + [dict objectForKey:@"TotalReviewCount"]); + + if (self.totalReviewCount > 0 && + [dict objectForKey:@"ReviewStatistics"] != nil) { + SET_IF_NOT_NULL(self.avgRating, + [[dict objectForKey:@"ReviewStatistics"] + objectForKey:@"AverageOverallRating"]); + } } - return self; + } + return self; } -- (NSString *)description{ - - return [NSString stringWithFormat:@"Key: %@, ID: %@ Title: %@, Rating: %.02f(%ld)", self.productKey, self.productId, self.productName, [self.avgRating floatValue], (long)[self.totalReviewCount integerValue]]; +- (NSString *)description { + + return [NSString + stringWithFormat:@"Key: %@, ID: %@ Title: %@, Rating: %.02f(%ld)", + self.productKey, self.productId, self.productName, + [self.avgRating floatValue], + (long)[self.totalReviewCount integerValue]]; } --(NSString*)displayName { - return _productName; +- (NSString *)displayName { + return _productName; } --(NSString*)displayImageUrl { - return _productImageUrl; +- (NSString *)displayImageUrl { + return _productImageUrl; } --(NSString*)identifier { - return _productId; +- (NSString *)identifier { + return _productId; } --(NSNumber*)averageRating { - return _avgRating; +- (NSNumber *)averageRating { + return _avgRating; } @end diff --git a/Pod/BVCurations/BVCurationsFeedLoader.h b/Pod/BVCurations/BVCurationsFeedLoader.h index bf671a3d..2c6cd24b 100644 --- a/Pod/BVCurations/BVCurationsFeedLoader.h +++ b/Pod/BVCurations/BVCurationsFeedLoader.h @@ -9,25 +9,26 @@ #import #import "BVCurations.h" -#import "BVCurationsFeedRequest.h" #import "BVCurationsFeedItem.h" +#import "BVCurationsFeedRequest.h" @class BVCurationsFeedRequest; - typedef void (^feedRequestCompletionHandler)(NSArray *); -typedef void (^feedRequestErrorHandler)(NSError*); +typedef void (^feedRequestErrorHandler)(NSError *); - -/// API helper class used for fetching a curations feed with desired parameters (BVCurationsFeedRequest) +/// API helper class used for fetching a curations feed with desired parameters +/// (BVCurationsFeedRequest) @interface BVCurationsFeedLoader : NSObject /** Fetch a curations feed. - + @param feedRequest The query string parameters object - @param completionHandler Called on a successful response with an array of BVCurationsFeedItem objects. Called on main thread. - @param failureHandler Called with a filled out NSError object for any error that does not yeild a valid Curations feed. Called on main thread. + @param completionHandler Called on a successful response with an array of + BVCurationsFeedItem objects. Called on main thread. + @param failureHandler Called with a filled out NSError object for any error + that does not yeild a valid Curations feed. Called on main thread. */ - (void)loadFeedWithRequest:(BVCurationsFeedRequest *)feedRequest completionHandler:(feedRequestCompletionHandler)completionHandler diff --git a/Pod/BVCurations/BVCurationsFeedLoader.m b/Pod/BVCurations/BVCurationsFeedLoader.m index 0d7456a2..e6f15208 100644 --- a/Pod/BVCurations/BVCurationsFeedLoader.m +++ b/Pod/BVCurations/BVCurationsFeedLoader.m @@ -12,152 +12,169 @@ @implementation BVCurationsFeedLoader - - (NSString *)urlRootCurations { - - return [BVSDKManager sharedManager].configuration.staging ? @"https://stg.api.bazaarvoice.com" : @"https://api.bazaarvoice.com"; - + + return [BVSDKManager sharedManager].configuration.staging + ? @"https://stg.api.bazaarvoice.com" + : @"https://api.bazaarvoice.com"; } - (void)loadFeedWithRequest:(BVCurationsFeedRequest *)feedRequest - completionHandler:(feedRequestCompletionHandler)completionHandler withFailure:(feedRequestErrorHandler)failureHandler;{ - - // check if apiKey is valid before loading any data. Will fail in debug only. -#pragma clang diagnostic push + completionHandler:(feedRequestCompletionHandler)completionHandler + withFailure:(feedRequestErrorHandler)failureHandler; +{ + + // check if apiKey is valid before loading any data. Will fail in debug only. +#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-variable" - NSString* apiKey = [BVSDKManager sharedManager].configuration.apiKeyCurations; - NSAssert(apiKey.length, @"apiKeyCurations must be set on BVSDKManager before using the Curations SDK."); + NSString *apiKey = [BVSDKManager sharedManager].configuration.apiKeyCurations; + NSAssert(apiKey.length, @"apiKeyCurations must be set on BVSDKManager before " + @"using the Curations SDK."); #pragma clang diagnostic pop - NSString *endPoint = [NSString stringWithFormat:@"%@/curations/content/get", [self urlRootCurations]]; - - NSURLComponents *components = [NSURLComponents componentsWithString:endPoint]; - - NSArray *queryItems = [feedRequest createQueryItems]; - - components.queryItems = queryItems; - NSURL *url = components.URL; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; - - NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession] - dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ - + NSString *endPoint = [NSString + stringWithFormat:@"%@/curations/content/get", [self urlRootCurations]]; + + NSURLComponents *components = [NSURLComponents componentsWithString:endPoint]; + + NSArray *queryItems = [feedRequest createQueryItems]; + + components.queryItems = queryItems; + NSURL *url = components.URL; + + [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; + + NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession] + dataTaskWithURL:url + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { + NSHTTPURLResponse *urlResp = (NSHTTPURLResponse *)response; - - if ((!error && urlResp.statusCode < 300) && data != nil){ - - NSError *errorJSON; - NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&errorJSON]; - - if (!errorJSON){ - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: %@", responseDict]]; - - // check response body status code first. Curations API will return a 200 response on failures, but put the HTTP status in the "code" value. - int status = 200; - - if ([responseDict objectForKey:@"code"] != nil) - { - status = (int)[[responseDict objectForKey:@"code"] integerValue]; + + if ((!error && urlResp.statusCode < 300) && data != nil) { + + NSError *errorJSON; + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&errorJSON]; + + if (!errorJSON) { + + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"RESPONSE: %@", responseDict]]; + + // check response body status code first. Curations API will return + // a 200 response on failures, but put the HTTP status in the "code" + // value. + int status = 200; + + if ([responseDict objectForKey:@"code"] != nil) { + status = (int)[[responseDict objectForKey:@"code"] integerValue]; + } + + if (status < 300) { + + NSDictionary *updates = [responseDict objectForKey:@"updates"]; + NSMutableArray *feedItemsArray = [NSMutableArray array]; + NSMutableDictionary *referencedProdcts = + [NSMutableDictionary dictionary]; + + // Check and see if product data is present + NSDictionary *productData = + [responseDict objectForKey:@"productData"]; + + if (productData != nil) { + + for (NSString *key in productData.allKeys) { + + NSDictionary *dictToParse = [productData objectForKey:key]; + + BVCurationsProductDetail *prod = + [[BVCurationsProductDetail alloc] initWithDict:dictToParse + withKey:key]; + + [referencedProdcts setObject:prod forKey:key]; } - - if (status < 300){ - - NSDictionary* updates = [responseDict objectForKey:@"updates"]; - NSMutableArray* feedItemsArray = [NSMutableArray array]; - NSMutableDictionary *referencedProdcts = [NSMutableDictionary dictionary]; - - // Check and see if product data is present - NSDictionary *productData = [responseDict objectForKey:@"productData"]; - - if (productData != nil){ - - for (NSString *key in productData.allKeys){ - - NSDictionary *dictToParse = [productData objectForKey:key]; - - BVCurationsProductDetail * prod = [[BVCurationsProductDetail alloc] initWithDict:dictToParse withKey:key]; - - [referencedProdcts setObject:prod forKey:key]; - - } - - } - - for(NSDictionary* update in updates) { - BVCurationsFeedItem * feedItem = [self getFeedItem:update withReferencedProducts:referencedProdcts withExternalId:feedRequest.externalId]; - - if(feedItem != nil){ - [feedItemsArray addObject:feedItem]; - } - } - - - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(feedItemsArray); - }); - - - - } else { - - // status indicates failure.... - NSString *reason = @"Unknown failure."; - if ([responseDict objectForKey:@"reason"] != nil){ - reason = [responseDict objectForKey:@"reason"]; - } - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: reason }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:status userInfo:userInfo]; - - dispatch_async(dispatch_get_main_queue(), ^{ - failureHandler(err); - }); + } + + for (NSDictionary *update in updates) { + BVCurationsFeedItem *feedItem = + [self getFeedItem:update + withReferencedProducts:referencedProdcts + withExternalId:feedRequest.externalId]; + + if (feedItem != nil) { + [feedItemsArray addObject:feedItem]; } - - - + } + + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(feedItemsArray); + }); + } else { - //serialization error - dispatch_async(dispatch_get_main_queue(), ^{ - failureHandler(errorJSON); - }); - return; + + // status indicates failure.... + NSString *reason = @"Unknown failure."; + if ([responseDict objectForKey:@"reason"] != nil) { + reason = [responseDict objectForKey:@"reason"]; + } + NSDictionary *userInfo = @{NSLocalizedDescriptionKey : reason}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:status + userInfo:userInfo]; + + dispatch_async(dispatch_get_main_queue(), ^{ + failureHandler(err); + }); } - + + } else { + // serialization error + dispatch_async(dispatch_get_main_queue(), ^{ + failureHandler(errorJSON); + }); + return; + } + } else { - - // request error - if (error){ - - dispatch_async(dispatch_get_main_queue(), ^{ - failureHandler(error); - }); - return; - - } else { - - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: urlResp.description }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:urlResp.statusCode userInfo:userInfo]; - dispatch_async(dispatch_get_main_queue(), ^{ - failureHandler(err); - }); - return; - - } - - } - - }]; - - [downloadTask resume]; - + + // request error + if (error) { + + dispatch_async(dispatch_get_main_queue(), ^{ + failureHandler(error); + }); + return; + + } else { + + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : urlResp.description}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:urlResp.statusCode + userInfo:userInfo]; + dispatch_async(dispatch_get_main_queue(), ^{ + failureHandler(err); + }); + return; + } + } + + }]; + + [downloadTask resume]; } --(BVCurationsFeedItem *)getFeedItem:(NSDictionary*)update withReferencedProducts:(NSDictionary *)referencedProducts withExternalId:(NSString *)externalId { - - NSDictionary* data = [update objectForKey:@"data"]; - return [[BVCurationsFeedItem alloc] initWithDict:data withReferencedProducts:referencedProducts withExternalId:externalId]; +- (BVCurationsFeedItem *)getFeedItem:(NSDictionary *)update + withReferencedProducts:(NSDictionary *)referencedProducts + withExternalId:(NSString *)externalId { + NSDictionary *data = [update objectForKey:@"data"]; + return [[BVCurationsFeedItem alloc] initWithDict:data + withReferencedProducts:referencedProducts + withExternalId:externalId]; } @end diff --git a/Pod/BVCurations/BVCurationsFeedRequest.h b/Pod/BVCurations/BVCurationsFeedRequest.h index b1c625f7..9e0774e8 100644 --- a/Pod/BVCurations/BVCurationsFeedRequest.h +++ b/Pod/BVCurations/BVCurationsFeedRequest.h @@ -11,113 +11,144 @@ #import "BVCurations.h" /*! - Use the BVCurationsFeedRequest object to construct query parameters used by the BVGetCurationsFeed API request. + Use the BVCurationsFeedRequest object to construct query parameters used by the + BVGetCurationsFeed API request. */ @interface BVCurationsFeedRequest : NSObject - /*! Initialize the parameters with the always required groups parameter - + @param groups Array of NSStrings */ -- (id)initWithGroups:(NSArray*)groups; - +- (id)initWithGroups:(NSArray *)groups; /// Unavailable, use the designated initializer. -- (instancetype)init __attribute__((unavailable("Use -initWithGroups: instead"))); - +- (instancetype)init + __attribute__((unavailable("Use -initWithGroups: instead"))); -/// Creates an array of NSURLQueryItem item objects for use in querying the Curations feed API. +/// Creates an array of NSURLQueryItem item objects for use in querying the +/// Curations feed API. - (NSArray *)createQueryItems; - -/// The administered groups which you want to query. This parameter is required. The values in the groups are configured in the Curations management console. -@property NSArray< NSString* >* groups; - +/// The administered groups which you want to query. This parameter is required. +/// The values in the groups are configured in the Curations management console. +@property NSArray *groups; /// Set the user's current location, to tailor the content to their location. - (void)setLatitude:(double)latitude longitude:(double)longitude; +/// Sending a UNIX timestamp for after returns content posted only on or after +/// that time. +@property NSNumber *after; -/// Sending a UNIX timestamp for after returns content posted only on or after that time. -@property NSNumber* after; - - -/// Sending a UNIX timestamp for before returns content posted only on or before that time. -@property NSNumber* before; - - - -/// Sending a value for author returns updates that match only that author. If this is set, Bazaarvoice will perform a lookup against either the token or alias of the author. Any author that matches either one is considered a match. -@property NSString* author; +/// Sending a UNIX timestamp for before returns content posted only on or before +/// that time. +@property NSNumber *before; +/// Sending a value for author returns updates that match only that author. If +/// this is set, Bazaarvoice will perform a lookup against either the token or +/// alias of the author. Any author that matches either one is considered a +/// match. +@property NSString *author; /*! - Sending a number for featured requires at least that many featured updates to come down in your feed. These featured updates count against the overall limit specified in limit. For example, setting limit to 25 and featured to 10 returns 10 featured updates and 15 other updates, which may be featured or non-featured). - - If you specify a number greater than the number of featured updates that you have in the feed, Bazaarvoice sends all the featured updates and counts that number against the total limit. Continuing the prior example, if you only have 7 featured updates in your feed, you'd get the 7 featured updates and then 18 non-featured updates.) + Sending a number for featured requires at least that many featured updates + to come down in your feed. These featured updates count against the overall + limit specified in limit. For example, setting limit to 25 and featured to 10 + returns 10 featured updates and 15 other updates, which may be featured or + non-featured). + + If you specify a number greater than the number of featured updates that you + have in the feed, Bazaarvoice sends all the featured updates and counts that + number against the total limit. Continuing the prior example, if you only + have 7 featured updates in your feed, you'd get the 7 featured updates and + then 18 non-featured updates.) */ @property NSUInteger featured; - /*! - Setting has_geotag causes the feed to be filtered based on the presence or absence of a geotag on the update. Set this to true to require a geotag or false to require the absence of one. - - Note that setting one or more has_geotag, has_link, has_photo, or has_video parameters yields the intersection (not the union) of the selected filters. - */ -@property NSNumber* hasGeotag; + Setting has_geotag causes the feed to be filtered based on the presence or + absence of a geotag on the update. Set this to true to require a geotag or + false to require the absence of one. + Note that setting one or more has_geotag, has_link, has_photo, or has_video + parameters yields the intersection (not the union) of the selected filters. + */ +@property NSNumber *hasGeotag; /*! - Setting has_link causes the feed to be filtered based on the presence or absence of a link on the update. Set this to true to require a link or false to require the absence of one. - - Note that setting one or more has_geotag, has_link, has_photo, or has_video parameters yields the intersection (not the union) of the selected filters. - */ -@property NSNumber* hasLink; + Setting has_link causes the feed to be filtered based on the presence or + absence of a link on the update. Set this to true to require a link or false + to require the absence of one. + Note that setting one or more has_geotag, has_link, has_photo, or has_video + parameters yields the intersection (not the union) of the selected filters. + */ +@property NSNumber *hasLink; -/// Setting has_photo causes the feed to be filtered based on the presence or absence of a photo on the update. Set this to true to require a photo or false to require the absence of one. +/// Setting has_photo causes the feed to be filtered based on the presence or +/// absence of a photo on the update. Set this to true to require a photo or +/// false to require the absence of one. /// -/// Note that setting one or more has_geotag, has_link, has_photo, or has_video parameters yields the intersection (not the union) of the selected filters. -@property NSNumber* hasPhoto; +/// Note that setting one or more has_geotag, has_link, has_photo, or has_video +/// parameters yields the intersection (not the union) of the selected filters. +@property NSNumber *hasPhoto; - -/// Setting has_video causes the feed to be filtered based on the presence or absence of a video on the update. Set this to true to require a video or false to require the absence of one. +/// Setting has_video causes the feed to be filtered based on the presence or +/// absence of a video on the update. Set this to true to require a video or +/// false to require the absence of one. /// -/// Note that setting one or more has_geotag, has_link, has_photo, or has_video parameters yields the intersection (not the union) of the selected filters. -@property NSNumber* hasVideo; - -/// Setting hasPhotoOrVideo causes the feed to be filtered based on the presence or absence of a photo or video on the update. Set this to true to require a video or false to require the absence of one. +/// Note that setting one or more has_geotag, has_link, has_photo, or +/// has_video parameters yields the intersection (not the union) of the +/// selected filters. +@property NSNumber *hasVideo; + +/// Setting hasPhotoOrVideo causes the feed to be filtered based on the presence +/// or absence of a photo or video on the update. Set this to true to require a +/// video or false to require the absence of one. /// -/// Note that setting one or more hasPhotoOrVideo, has_geotag, has_link, has_photo, or has_video parameters yields the intersection (not the union) of the selected filters. -@property NSNumber* hasPhotoOrVideo; - - -/// On some channels, Bazaarvoice sends comments about an update along with the update. This option controls whether comments are included in your feed. Be aware that including comments, especially in feeds where they are common, can drastically increase output size and reduce performance. Also, Bazaarvoice does not poll for comments indefinitely. When an update reaches a certain age, its comments are no longer updated. -@property NSNumber* includeComments; - - -/// This is the number of updates Bazaarvoice sends. You can request any number between 0 and 100. If you ask for more updates than exist in the feed, Bazaarvoice sends what is available. Asking for a large number of updates does not guarantee you will get that many updates. +/// Note that setting one or more hasPhotoOrVideo, has_geotag, has_link, +/// has_photo, or has_video parameters yields the intersection (not the +/// union) of the selected filters. +@property NSNumber *hasPhotoOrVideo; + +/// On some channels, Bazaarvoice sends comments about an update along with the +/// update. This option controls whether comments are included in your feed. Be +/// aware that including comments, especially in feeds where they are common, +/// can drastically increase output size and reduce performance. Also, +/// Bazaarvoice does not poll for comments indefinitely. When an update reaches +/// a certain age, its comments are no longer updated. +@property NSNumber *includeComments; + +/// This is the number of updates Bazaarvoice sends. You can request any number +/// between 0 and 100. If you ask for more updates than exist in the feed, +/// Bazaarvoice sends what is available. Asking for a large number of updates +/// does not guarantee you will get that many updates. @property NSUInteger limit; - -/// Sending the media property overrides default sizes for images and videos. Send media as a nested dictionary-like object. Keys should be the type of media, most often photo or video, although a few other keys such as icon will work. The values should be dictionaries themselves, with width and height keys, the values of which can be ints or strings. For example: {'video': {'width': 480, 'height': 360}}. -@property NSDictionary* media; - - -/// String Array where each element is a string representing a tag. Setting this causes the system to return only updates with at least one of the tags in the tag array. -/// If you want to include only one tag in your feed, you can send this as a string instead of an array with a single value, but either will work. -@property NSArray* tags; - - +/// Sending the media property overrides default sizes for images and videos. +/// Send media as a nested dictionary-like object. Keys should be the type of +/// media, most often photo or video, although a few other keys such as icon +/// will work. The values should be dictionaries themselves, with width and +/// height keys, the values of which can be ints or strings. For example: +/// {'video': {'width': 480, 'height': 360}}. +@property NSDictionary *media; + +/// String Array where each element is a string representing a tag. Setting this +/// causes the system to return only updates with at least one of the tags in +/// the tag array. If you want to include only one tag in your feed, you can +/// send this as a string instead of an array with a single value, but either +/// will work. +@property NSArray *tags; /// The external product id used for syndication. -/// Setting externalId returns both client content and syndicated content. If externalId is not valid, only client content will be returned. -@property NSString* externalId; - +/// Setting externalId returns both client content and syndicated content. If +/// externalId is not valid, only client content will be returned. +@property NSString *externalId; -/// Setting to true to include product data tagged in the Curations feed. May not be availale for all feeds. -@property NSNumber* withProductData; +/// Setting to true to include product data tagged in the Curations feed. May +/// not be availale for all feeds. +@property NSNumber *withProductData; @end diff --git a/Pod/BVCurations/BVCurationsFeedRequest.m b/Pod/BVCurations/BVCurationsFeedRequest.m index 818415df..de230aea 100644 --- a/Pod/BVCurations/BVCurationsFeedRequest.m +++ b/Pod/BVCurations/BVCurationsFeedRequest.m @@ -9,168 +9,203 @@ #import "BVCurationsFeedRequest.h" #import "BVSDKConfiguration.h" -@interface BVCurationsFeedRequest() +@interface BVCurationsFeedRequest () -@property NSNumber* latitude; -@property NSNumber* longitude; +@property NSNumber *latitude; +@property NSNumber *longitude; @end @implementation BVCurationsFeedRequest -- (id)initWithGroups:(NSArray*)groups { - - self = [super init]; - - if (self){ - _limit = 10; - _after = [NSNumber numberWithLong:0]; - _before = [NSNumber numberWithLong:0]; - _groups = groups; - } - - return self; +- (id)initWithGroups:(NSArray *)groups { + + self = [super init]; + + if (self) { + _limit = 10; + _after = [NSNumber numberWithLong:0]; + _before = [NSNumber numberWithLong:0]; + _groups = groups; + } + + return self; } - (void)setLatitude:(double)latitude longitude:(double)longitude { - - self.latitude = [NSNumber numberWithDouble:latitude]; - self.longitude = [NSNumber numberWithDouble:longitude]; - + + self.latitude = [NSNumber numberWithDouble:latitude]; + self.longitude = [NSNumber numberWithDouble:longitude]; } +- (NSArray *)createQueryItems { -- (NSArray *)createQueryItems{ - - // Check for the required params, fail if not present - NSAssert([self.groups count] > 0, @"You must supply at least one item in the groups parameter."); - - BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; - NSString *clientId = sdkMgr.configuration.clientId; - NSString *apiKey = sdkMgr.configuration.apiKeyCurations; - - NSAssert(apiKey.length, @"apiKeyCurations must be set on BVSDKManager before using the Curations SDK."); - - - // Build up the query parameters... - - if (self.limit > 100) self.limit = 20; - - NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:@"passkey" value:apiKey]; - NSURLQueryItem *client = [NSURLQueryItem queryItemWithName:@"client" value:clientId]; - NSURLQueryItem *limit = [NSURLQueryItem queryItemWithName:@"limit" value:[NSString stringWithFormat:@"%lu", (unsigned long)self.limit]]; - - // Add the required/default query params - - NSMutableArray* queryItems = [NSMutableArray arrayWithArray:@[ search, client, limit ]]; - - // groups array -- one parame per value (i.e. cannot be comma-delimited) - for (NSString* groupString in self.groups) { - NSURLQueryItem *group = [NSURLQueryItem queryItemWithName:@"groups" value:groupString]; - [queryItems addObject:group]; - } - - // Add the optional query params... - - // geolocation, if available - if (self.latitude != nil && self.longitude != nil) { - NSString* paramString = [NSString stringWithFormat:@"%@,%@", self.latitude, self.longitude]; - NSURLQueryItem *geolocation = [NSURLQueryItem queryItemWithName:@"geolocation" value:paramString]; - [queryItems addObject:geolocation]; - } - - // tags array - for (NSString* tagString in self.tags) { - NSURLQueryItem *tag = [NSURLQueryItem queryItemWithName:@"tags" value:tagString]; - [queryItems addObject:tag]; - } - - if ([self.before integerValue] > 0){ - NSURLQueryItem *beforeQI = [NSURLQueryItem queryItemWithName:@"before" value:[NSString stringWithFormat:@"%li", (long)[self.before integerValue]]]; - [queryItems addObject:beforeQI]; - } - - - if ([self.after integerValue] > 0){ - NSURLQueryItem *afterQI = [NSURLQueryItem queryItemWithName:@"after" value:[NSString stringWithFormat:@"%li", (long)[self.after integerValue]]]; - [queryItems addObject:afterQI]; - } - - if (self.author != nil){ - NSURLQueryItem *authorQI = [NSURLQueryItem queryItemWithName:@"author" value:self.author]; - [queryItems addObject:authorQI]; - } - - if (self.featured > 0){ - NSURLQueryItem *featuredQI = [NSURLQueryItem queryItemWithName:@"featured" value:[NSString stringWithFormat:@"%lu", (unsigned long)self.featured ]]; - [queryItems addObject:featuredQI]; - } - - if (self.hasGeotag){ - NSString *val = (_hasGeotag.boolValue)?@"true":@"false"; - NSURLQueryItem *hasGeoTagQI = [NSURLQueryItem queryItemWithName:@"has_geotag" value:val]; - [queryItems addObject:hasGeoTagQI]; - } - - if (self.hasLink){ - NSString *val = (_hasLink.boolValue)?@"true":@"false"; - NSURLQueryItem *hasLinkQI = [NSURLQueryItem queryItemWithName:@"has_link" value:val]; - [queryItems addObject:hasLinkQI]; - } - - if (self.hasPhoto){ - NSString *val = (_hasPhoto.boolValue)?@"true":@"false"; - NSURLQueryItem *hasPhotoQI = [NSURLQueryItem queryItemWithName:@"has_photo" value:val]; - [queryItems addObject:hasPhotoQI]; - } - - if (self.hasVideo){ - NSString *val = (_hasVideo.boolValue)?@"true":@"false"; - NSURLQueryItem *hasVideoQI = [NSURLQueryItem queryItemWithName:@"has_video" value:val]; - [queryItems addObject:hasVideoQI]; - } - - if (_hasPhotoOrVideo) { - NSString *val = (_hasPhotoOrVideo.boolValue)?@"true":@"false"; - NSURLQueryItem *hasPhotoOrVideoQI = [NSURLQueryItem queryItemWithName:@"has_photo_or_video" value:val]; - [queryItems addObject:hasPhotoOrVideoQI]; - } - - if (self.withProductData){ - NSString *val = (_withProductData.boolValue )?@"true":@"false"; - NSURLQueryItem *withProductDataQI = [NSURLQueryItem queryItemWithName:@"withProductData" value:val]; - [queryItems addObject:withProductDataQI]; - } - - if (self.includeComments){ - NSString *val = (_includeComments.boolValue)?@"true":@"false"; - NSURLQueryItem *includeCommentsQI = [NSURLQueryItem queryItemWithName:@"include_comments" value:val]; - [queryItems addObject:includeCommentsQI]; - } - - if (self.externalId){ - NSURLQueryItem *externalIdQI = [NSURLQueryItem queryItemWithName:@"externalId" value:self.externalId]; - [queryItems addObject:externalIdQI]; - } - - if (self.media) { - - NSError *error; - NSData *jsonMediaData = [NSJSONSerialization dataWithJSONObject:self.media - options:0 - error:&error]; - - if (!jsonMediaData || error != nil) { - [[BVLogger sharedLogger] error:@"Unable to parameterize media dictionary for curations request."]; - } else { - NSString *jsonString = [[NSString alloc] initWithData:jsonMediaData encoding:NSUTF8StringEncoding]; - NSURLQueryItem *mediaQueryQI = [NSURLQueryItem queryItemWithName:@"media" value:jsonString]; - [queryItems addObject:mediaQueryQI]; - } - - } - - return queryItems; + // Check for the required params, fail if not present + NSAssert([self.groups count] > 0, + @"You must supply at least one item in the groups parameter."); + + BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; + NSString *clientId = sdkMgr.configuration.clientId; + NSString *apiKey = sdkMgr.configuration.apiKeyCurations; + + NSAssert(apiKey.length, @"apiKeyCurations must be set on BVSDKManager before " + @"using the Curations SDK."); + + // Build up the query parameters... + + if (self.limit > 100) + self.limit = 20; + + NSURLQueryItem *search = + [NSURLQueryItem queryItemWithName:@"passkey" value:apiKey]; + NSURLQueryItem *client = + [NSURLQueryItem queryItemWithName:@"client" value:clientId]; + NSURLQueryItem *limit = [NSURLQueryItem + queryItemWithName:@"limit" + value:[NSString stringWithFormat:@"%lu", + (unsigned long)self.limit]]; + + // Add the required/default query params + + NSMutableArray *queryItems = + [NSMutableArray arrayWithArray:@[ search, client, limit ]]; + + // groups array -- one parame per value (i.e. cannot be comma-delimited) + for (NSString *groupString in self.groups) { + NSURLQueryItem *group = + [NSURLQueryItem queryItemWithName:@"groups" value:groupString]; + [queryItems addObject:group]; + } + + // Add the optional query params... + + // geolocation, if available + if (self.latitude != nil && self.longitude != nil) { + NSString *paramString = + [NSString stringWithFormat:@"%@,%@", self.latitude, self.longitude]; + NSURLQueryItem *geolocation = + [NSURLQueryItem queryItemWithName:@"geolocation" value:paramString]; + [queryItems addObject:geolocation]; + } + + // tags array + for (NSString *tagString in self.tags) { + NSURLQueryItem *tag = + [NSURLQueryItem queryItemWithName:@"tags" value:tagString]; + [queryItems addObject:tag]; + } + + if ([self.before integerValue] > 0) { + NSURLQueryItem *beforeQI = [NSURLQueryItem + queryItemWithName:@"before" + value:[NSString + stringWithFormat:@"%li", + (long) + [self.before integerValue]]]; + [queryItems addObject:beforeQI]; + } + + if ([self.after integerValue] > 0) { + NSURLQueryItem *afterQI = [NSURLQueryItem + queryItemWithName:@"after" + value:[NSString + stringWithFormat:@"%li", + (long) + [self.after integerValue]]]; + [queryItems addObject:afterQI]; + } + + if (self.author != nil) { + NSURLQueryItem *authorQI = + [NSURLQueryItem queryItemWithName:@"author" value:self.author]; + [queryItems addObject:authorQI]; + } + + if (self.featured > 0) { + NSURLQueryItem *featuredQI = [NSURLQueryItem + queryItemWithName:@"featured" + value:[NSString + stringWithFormat:@"%lu", + (unsigned long)self.featured]]; + [queryItems addObject:featuredQI]; + } + + if (self.hasGeotag) { + NSString *val = (_hasGeotag.boolValue) ? @"true" : @"false"; + NSURLQueryItem *hasGeoTagQI = + [NSURLQueryItem queryItemWithName:@"has_geotag" value:val]; + [queryItems addObject:hasGeoTagQI]; + } + + if (self.hasLink) { + NSString *val = (_hasLink.boolValue) ? @"true" : @"false"; + NSURLQueryItem *hasLinkQI = + [NSURLQueryItem queryItemWithName:@"has_link" value:val]; + [queryItems addObject:hasLinkQI]; + } + + if (self.hasPhoto) { + NSString *val = (_hasPhoto.boolValue) ? @"true" : @"false"; + NSURLQueryItem *hasPhotoQI = + [NSURLQueryItem queryItemWithName:@"has_photo" value:val]; + [queryItems addObject:hasPhotoQI]; + } + + if (self.hasVideo) { + NSString *val = (_hasVideo.boolValue) ? @"true" : @"false"; + NSURLQueryItem *hasVideoQI = + [NSURLQueryItem queryItemWithName:@"has_video" value:val]; + [queryItems addObject:hasVideoQI]; + } + + if (_hasPhotoOrVideo) { + NSString *val = (_hasPhotoOrVideo.boolValue) ? @"true" : @"false"; + NSURLQueryItem *hasPhotoOrVideoQI = + [NSURLQueryItem queryItemWithName:@"has_photo_or_video" value:val]; + [queryItems addObject:hasPhotoOrVideoQI]; + } + + if (self.withProductData) { + NSString *val = (_withProductData.boolValue) ? @"true" : @"false"; + NSURLQueryItem *withProductDataQI = + [NSURLQueryItem queryItemWithName:@"withProductData" value:val]; + [queryItems addObject:withProductDataQI]; + } + + if (self.includeComments) { + NSString *val = (_includeComments.boolValue) ? @"true" : @"false"; + NSURLQueryItem *includeCommentsQI = + [NSURLQueryItem queryItemWithName:@"include_comments" value:val]; + [queryItems addObject:includeCommentsQI]; + } + + if (self.externalId) { + NSURLQueryItem *externalIdQI = + [NSURLQueryItem queryItemWithName:@"externalId" value:self.externalId]; + [queryItems addObject:externalIdQI]; + } + + if (self.media) { + + NSError *error; + NSData *jsonMediaData = [NSJSONSerialization dataWithJSONObject:self.media + options:0 + error:&error]; + + if (!jsonMediaData || error != nil) { + [[BVLogger sharedLogger] error:@"Unable to parameterize media dictionary " + @"for curations request."]; + } else { + NSString *jsonString = + [[NSString alloc] initWithData:jsonMediaData + encoding:NSUTF8StringEncoding]; + NSURLQueryItem *mediaQueryQI = + [NSURLQueryItem queryItemWithName:@"media" value:jsonString]; + [queryItems addObject:mediaQueryQI]; + } + } + + return queryItems; } @end diff --git a/Pod/BVCurations/BVCurationsPhotoUploader.h b/Pod/BVCurations/BVCurationsPhotoUploader.h index cd242b7b..99787c06 100644 --- a/Pod/BVCurations/BVCurationsPhotoUploader.h +++ b/Pod/BVCurations/BVCurationsPhotoUploader.h @@ -18,15 +18,21 @@ typedef void (^uploadErrorHandler)(NSError *); */ @interface BVCurationsPhotoUploader : NSObject -/*! - This call wraps the Curations "Custom Post" API. Besides having your client ID and Curations API configured in the BVSDKManager, all you need to do is use one of the initializers in the BVCurationsAddPostRequest and make the API call. - +/*! + This call wraps the Curations "Custom Post" API. Besides having your client ID + and Curations API configured in the BVSDKManager, all you need to do is use one + of the initializers in the BVCurationsAddPostRequest and make the API call. + @param postParams - A valid initialized BVCurationsAddPostRequest object. - @param completionHandler - Only called when a custom post has succeeded. This handler contains no parameters and is invoked on the main thread. - @param failureHandler - Called when there is an API failure of some kind. Error code and error text are returned in an NSError object. This handler is invoked on the main thread. + @param completionHandler - Only called when a custom post has succeeded. This + handler contains no parameters and is invoked on the main thread. + @param failureHandler - Called when there is an API failure of some kind. Error + code and error text are returned in an NSError object. This handler is invoked + on the main thread. */ - (void)submitCurationsContentWithParams:(BVCurationsAddPostRequest *)postParams - completionHandler:(uploadCompletionHandler)completionHandler + completionHandler: + (uploadCompletionHandler)completionHandler withFailure:(uploadErrorHandler)failureHandler; @end diff --git a/Pod/BVCurations/BVCurationsPhotoUploader.m b/Pod/BVCurations/BVCurationsPhotoUploader.m index b08b1765..2303f9d8 100644 --- a/Pod/BVCurations/BVCurationsPhotoUploader.m +++ b/Pod/BVCurations/BVCurationsPhotoUploader.m @@ -13,205 +13,241 @@ @implementation BVCurationsPhotoUploader - (void)submitCurationsContentWithParams:(BVCurationsAddPostRequest *)postParams - completionHandler:(uploadCompletionHandler)completionHandler - withFailure:(uploadErrorHandler)failureHandler{ - - NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; - NSString *passKey = [BVSDKManager sharedManager].configuration.apiKeyCurations; - - NSAssert(passKey.length, @"apiKeyCurations is required!"); - - - if (!postParams){ - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"Required parameter postParams was nil." }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; - - [self errorOnMainThread:err handler:failureHandler]; - } - - NSString *endPoint = [NSString stringWithFormat:@"%@/curations/content/add/?client=%@&passkey=%@", [self urlRootCurations], clientId, passKey]; - NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; - NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; - NSURL *url = [NSURL URLWithString:endPoint]; - - NSString *boundary = [self generateBoundaryString]; - - // Create and configure the request - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; - [request setHTTPMethod:@"POST"]; - - // Set the content type - - NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; Boundary=%@", boundary]; - [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; - - // create body - - NSData *httpBody = [self createBodyWithBoundary:boundary withRequestParams:postParams]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"POST: %@ with BODY: %@", request, postParams.description]]; - - NSURLSessionUploadTask *taskUpload = [session uploadTaskWithRequest:request - fromData:httpBody - completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - //completion - if (error){ - - // Error from the task itself - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: %@", error.localizedDescription]]; - - [self errorOnMainThread:error handler:failureHandler]; - return; - } - - NSError *errorJSON; - NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&errorJSON]; - - if (!errorJSON){ - - int status = [self getStatusCodeFromResponse:responseDict]; - if (status < 299){ - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"Result: %@", responseDict]]; - - // success - [self completionOnMainThreadWithHandler:completionHandler]; - - } else { - - // fail - NSString *errorString = [self getErrorStringFromResponse:responseDict]; - - if (!errorString){ - errorString = @"Unknown error from server."; - } - - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: errorString }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:status userInfo:userInfo]; - - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: %@", err.localizedDescription]]; - - [self errorOnMainThread:err handler:failureHandler]; - - return; - } - + completionHandler: + (uploadCompletionHandler)completionHandler + withFailure:(uploadErrorHandler)failureHandler { + NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; + NSString *passKey = + [BVSDKManager sharedManager].configuration.apiKeyCurations; + + NSAssert(passKey.length, @"apiKeyCurations is required!"); + + if (!postParams) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey : @"Required parameter postParams was nil." + }; + NSError *err = + [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; + + [self errorOnMainThread:err handler:failureHandler]; + } + + NSString *endPoint = [NSString + stringWithFormat:@"%@/curations/content/add/?client=%@&passkey=%@", + [self urlRootCurations], clientId, passKey]; + NSURLSessionConfiguration *config = + [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; + NSURL *url = [NSURL URLWithString:endPoint]; + + NSString *boundary = [self generateBoundaryString]; + + // Create and configure the request + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"POST"]; + + // Set the content type + + NSString *contentType = + [NSString stringWithFormat:@"multipart/form-data; Boundary=%@", boundary]; + [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; + + // create body + + NSData *httpBody = + [self createBodyWithBoundary:boundary withRequestParams:postParams]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"POST: %@ with BODY: %@", request, + postParams.description]]; + + NSURLSessionUploadTask *taskUpload = [session + uploadTaskWithRequest:request + fromData:httpBody + completionHandler:^(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable error) { + + // completion + if (error) { + // Error from the task itself + [[BVLogger sharedLogger] + error:[NSString stringWithFormat:@"ERROR: %@", + error.localizedDescription]]; + + [self errorOnMainThread:error handler:failureHandler]; + return; + } + + NSError *errorJSON; + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&errorJSON]; + + if (!errorJSON) { + int status = [self getStatusCodeFromResponse:responseDict]; + if (status < 299) { + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"Result: %@", responseDict]]; + + // success + [self completionOnMainThreadWithHandler:completionHandler]; + } else { - - // json serialization error - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"ERROR: JSON Seriazation Error: %@", error.localizedDescription]]; - - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"JSON Serialization Error" }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; - - [self errorOnMainThread:err handler:failureHandler]; + // fail + NSString *errorString = + [self getErrorStringFromResponse:responseDict]; + + if (!errorString) { + errorString = @"Unknown error from server."; + } + + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : errorString}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:status + userInfo:userInfo]; + + [[BVLogger sharedLogger] + error:[NSString stringWithFormat:@"ERROR: %@", + err.localizedDescription]]; + + [self errorOnMainThread:err handler:failureHandler]; + + return; } - - }]; - - [taskUpload resume]; - -} + } else { + // json serialization error + [[BVLogger sharedLogger] + error:[NSString stringWithFormat: + @"ERROR: JSON Seriazation Error: %@", + error.localizedDescription]]; + + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : @"JSON Serialization Error"}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:-1 + userInfo:userInfo]; + + [self errorOnMainThread:err handler:failureHandler]; + } --(void)completionOnMainThreadWithHandler:(uploadCompletionHandler)completionHandler { - - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(); - }); - + }]; + + [taskUpload resume]; +} + +- (void)completionOnMainThreadWithHandler: + (uploadCompletionHandler)completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(); + }); } --(void)errorOnMainThread:(NSError*)error handler:(uploadErrorHandler)errorHandler { - - dispatch_async(dispatch_get_main_queue(), ^{ - errorHandler(error); - }); - +- (void)errorOnMainThread:(NSError *)error + handler:(uploadErrorHandler)errorHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + errorHandler(error); + }); } // Response code may be in either 'status' or 'code' as an integer - (int)getStatusCodeFromResponse:(NSDictionary *)response { - - long status = 200; // presume success unless we can dig out the exact failure - - if ([response objectForKey:@"code"] != nil && [[response objectForKey:@"code"] isKindOfClass:[NSNumber class]]){ - - // found integer in "code" - status = [[response objectForKey:@"code"] integerValue]; - - } else if ([response objectForKey:@"status"] != nil && [[response objectForKey:@"status"] isKindOfClass:[NSNumber class]]){ - - // found integer in "status" - status = [[response objectForKey:@"status"] integerValue]; - - } - - return (int)status; -} + long status = 200; // presume success unless we can dig out the exact failure + if ([response objectForKey:@"code"] != nil && + [[response objectForKey:@"code"] isKindOfClass:[NSNumber class]]) { + // found integer in "code" + status = [[response objectForKey:@"code"] integerValue]; -- (NSString *)getErrorStringFromResponse:(NSDictionary *)response { - - NSString * errorString = nil; - if ([response objectForKey:@"detail"] != nil){ - errorString = [response objectForKey:@"detail"]; - } else if ([response objectForKey:@"status"] != nil && [[response objectForKey:@"status"] isKindOfClass:[NSString class] ]){ - errorString = [response objectForKey:@"status"]; - } - - return errorString; + } else if ([response objectForKey:@"status"] != nil && + [[response objectForKey:@"status"] + isKindOfClass:[NSNumber class]]) { + // found integer in "status" + status = [[response objectForKey:@"status"] integerValue]; + } + + return (int)status; } -- (NSData *)createBodyWithBoundary:(NSString *)boundary withRequestParams:(BVCurationsAddPostRequest *)request -{ - NSMutableData *httpBody = [NSMutableData data]; - - // add params - - if (request){ - - // We need to send string representation in the body for the API to parse the params - NSString *jsonString = [[NSString alloc] initWithData:request.serializeParameters encoding:NSUTF8StringEncoding]; - - [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"jsonpayload"] dataUsingEncoding:NSUTF8StringEncoding]]; - [httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", jsonString] dataUsingEncoding:NSUTF8StringEncoding]];// JSON dictionary - } - - // add image data, if present and there are no photo links - if (request.image && request.photos.count == 0){ - NSString* base64 = [self encodeToBase64String:request.image]; - [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"image"] dataUsingEncoding:NSUTF8StringEncoding]]; - [httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", base64] dataUsingEncoding:NSUTF8StringEncoding]]; // JSON dictionary - } - - [httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; - - return httpBody; +- (NSString *)getErrorStringFromResponse:(NSDictionary *)response { + NSString *errorString = nil; + if ([response objectForKey:@"detail"] != nil) { + errorString = [response objectForKey:@"detail"]; + } else if ([response objectForKey:@"status"] != nil && + [[response objectForKey:@"status"] + isKindOfClass:[NSString class]]) { + errorString = [response objectForKey:@"status"]; + } + + return errorString; } +- (NSData *)createBodyWithBoundary:(NSString *)boundary + withRequestParams:(BVCurationsAddPostRequest *)request { + NSMutableData *httpBody = [NSMutableData data]; + + // add params + + if (request) { + // We need to send string representation in the body for the API to + // parse the params + NSString *jsonString = + [[NSString alloc] initWithData:request.serializeParameters + encoding:NSUTF8StringEncoding]; + + [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: " + @"form-data; " + @"name=\"%@\"\r\n\r\n", + @"jsonpayload"] + dataUsingEncoding:NSUTF8StringEncoding]]; + [httpBody + appendData:[[NSString stringWithFormat:@"%@\r\n", jsonString] + dataUsingEncoding:NSUTF8StringEncoding]]; // JSON + // dictionary + } + + // add image data, if present and there are no photo links + if (request.image && request.photos.count == 0) { + NSString *base64 = [self encodeToBase64String:request.image]; + [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: " + @"form-data; " + @"name=\"%@\"\r\n\r\n", + @"image"] + dataUsingEncoding:NSUTF8StringEncoding]]; + [httpBody + appendData:[[NSString stringWithFormat:@"%@\r\n", base64] + dataUsingEncoding:NSUTF8StringEncoding]]; // JSON + // dictionary + } + + [httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] + dataUsingEncoding:NSUTF8StringEncoding]]; + + return httpBody; +} - (NSString *)encodeToBase64String:(UIImage *)image { - - return [UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; - + return [UIImagePNGRepresentation(image) + base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; } -- (NSString *)generateBoundaryString -{ - return [NSString stringWithFormat:@"Boundary+%@", [[NSUUID UUID] UUIDString]]; - +- (NSString *)generateBoundaryString { + return [NSString stringWithFormat:@"Boundary+%@", [[NSUUID UUID] UUIDString]]; } - - (NSString *)urlRootCurations { - - return [BVSDKManager sharedManager].configuration.staging ? @"https://stg.api.bazaarvoice.com" : @"https://api.bazaarvoice.com"; - + return [BVSDKManager sharedManager].configuration.staging + ? @"https://stg.api.bazaarvoice.com" + : @"https://api.bazaarvoice.com"; } @end diff --git a/Pod/BVCurationsUI/BVCurationsPostViewController.h b/Pod/BVCurationsUI/BVCurationsPostViewController.h old mode 100755 new mode 100644 index 5e344b17..1d6a3c14 --- a/Pod/BVCurationsUI/BVCurationsPostViewController.h +++ b/Pod/BVCurationsUI/BVCurationsPostViewController.h @@ -6,38 +6,45 @@ // // -#import #import +#import @class BVCurationsAddPostRequest; @interface BVCurationsPostViewController : SLComposeServiceViewController --(instancetype _Nonnull) init __attribute__((unavailable("init not available"))); +- (nonnull instancetype)init __attribute__((unavailable("init not available"))); /** - @param postRequest Properly initialized request. textview.text will default to postRequest.text - @param logo Image to be presented in center of navBar. Will be proportionally scaled to fit height of navBar + @param postRequest Properly initialized request. textview.text will default to + postRequest.text + @param logo Image to be presented in center of navBar. Will be proportionally + scaled to fit height of navBar @param navBarColor desired color of navBar @param navBarTintColor desired tint color of navBar */ --(instancetype _Nonnull)initWithPostRequest:(BVCurationsAddPostRequest * _Nonnull)postRequest logoImage:(UIImage * _Nonnull)logo bavBarColor:(UIColor * _Nonnull)navBarColor navBarTintColor:(UIColor * _Nonnull)navBarTintColor; +- (nonnull instancetype)initWithPostRequest: + (nonnull BVCurationsAddPostRequest *)postRequest + logoImage:(nonnull UIImage *)logo + bavBarColor:(nonnull UIColor *)navBarColor + navBarTintColor:(nonnull UIColor *)navBarTintColor; /** Block used to respond to user pressing cancel button */ -@property (nonatomic, copy, nullable) void(^didPressCancel)(void); +@property(nonatomic, copy, nullable) void (^didPressCancel)(void); /** Block used to respond to user pressing post Will be called before network request is made Useful to display loading indicator */ -@property (nonatomic, copy, nullable) void(^didBeginPost)(void); +@property(nonatomic, copy, nullable) void (^didBeginPost)(void); /** Block used to respond to a network response Any errors that occur during request will be provided nil error indicates a successful post */ -@property (nonatomic, copy, nullable) void(^didCompletePost)(NSError * _Nullable error); +@property(nonatomic, copy, nullable) void (^didCompletePost) + (NSError *__nullable error); @end diff --git a/Pod/BVCurationsUI/BVCurationsPostViewController.m b/Pod/BVCurationsUI/BVCurationsPostViewController.m old mode 100755 new mode 100644 index d0ac2749..260bc3fd --- a/Pod/BVCurationsUI/BVCurationsPostViewController.m +++ b/Pod/BVCurationsUI/BVCurationsPostViewController.m @@ -9,168 +9,196 @@ #import "BVCurationsPostViewController.h" #import "BVCurationsAddPostRequest.h" #import "BVCurationsPhotoUploader.h" -#import "UIImage+BundleLocator.h" #import "BVPixel.h" +#import "UIImage+BundleLocator.h" @interface BVCurationsPostViewController () -@property (nonatomic, strong) BVCurationsAddPostRequest* postRequest; -@property (nonatomic, strong) UIImage * logo; -@property (nonatomic, strong) UIColor * navBarColor; -@property (nonatomic, strong) UIColor * navBarTintColor; +@property(nonatomic, strong) BVCurationsAddPostRequest *postRequest; +@property(nonatomic, strong) UIImage *logo; +@property(nonatomic, strong) UIColor *navBarColor; +@property(nonatomic, strong) UIColor *navBarTintColor; @end @implementation BVCurationsPostViewController --(instancetype _Nonnull)initWithPostRequest:(BVCurationsAddPostRequest * _Nonnull)postRequest logoImage:(UIImage * _Nonnull)logo bavBarColor:(UIColor * _Nonnull)navBarColor navBarTintColor:(UIColor * _Nonnull)navBarTintColor{ - if (self = [super init]) { - _postRequest = postRequest; - _logo = logo; - _navBarColor = navBarColor; - _navBarTintColor = navBarTintColor; - } - return self; +- (nonnull instancetype)initWithPostRequest: + (nonnull BVCurationsAddPostRequest *)postRequest + logoImage:(nonnull UIImage *)logo + bavBarColor:(nonnull UIColor *)navBarColor + navBarTintColor:(nonnull UIColor *)navBarTintColor { + if (self = [super init]) { + _postRequest = postRequest; + _logo = logo; + _navBarColor = navBarColor; + _navBarTintColor = navBarTintColor; + } + return self; } - (void)viewDidLoad { - [super viewDidLoad]; - [self setupNavBar]; - self.textView.text = _postRequest.text; - [self validateContent]; - - BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] initWithProductId:@"none" withBrand:nil withProductType:BVPixelProductTypeCurations withContainerId:@"CurationsSubmissionController" withAdditionalParams:nil]; - - [BVPixel trackEvent:inViewEvent]; + [super viewDidLoad]; + [self setupNavBar]; + self.textView.text = _postRequest.text; + [self validateContent]; + + BVInViewEvent *inViewEvent = + [[BVInViewEvent alloc] initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeCurations + withContainerId:@"CurationsSubmissionController" + withAdditionalParams:nil]; + + [BVPixel trackEvent:inViewEvent]; } - + - (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. } - (void)setupNavBar { - self.navigationController.navigationBar.tintColor = _navBarTintColor; - - CGFloat padding = 16; - CGSize size = self.navigationController.navigationBar.frame.size; - size.width -= padding * 2; - UIImage *bkgd = [self navBarBackgroundImage:_navBarColor logo:_logo size:size]; - [self.navigationController.navigationBar setBackgroundImage:bkgd forBarMetrics:UIBarMetricsDefault]; + self.navigationController.navigationBar.tintColor = _navBarTintColor; + + CGFloat padding = 16; + CGSize size = self.navigationController.navigationBar.frame.size; + size.width -= padding * 2; + UIImage *bkgd = + [self navBarBackgroundImage:_navBarColor logo:_logo size:size]; + [self.navigationController.navigationBar + setBackgroundImage:bkgd + forBarMetrics:UIBarMetricsDefault]; } - (void)didSelectCancel { - [super didSelectCancel]; - [self dismiss]; + [super didSelectCancel]; + [self dismiss]; } - (void)didSelectPost { - [super didSelectPost]; - _postRequest.text = self.textView.text; - - if (_didBeginPost) { - _didBeginPost(); - } - - BVCurationsPhotoUploader *uploader = [[BVCurationsPhotoUploader alloc] init]; - [uploader submitCurationsContentWithParams:_postRequest completionHandler:^{ + [super didSelectPost]; + _postRequest.text = self.textView.text; + + if (_didBeginPost) { + _didBeginPost(); + } + + BVCurationsPhotoUploader *uploader = [[BVCurationsPhotoUploader alloc] init]; + [uploader submitCurationsContentWithParams:_postRequest + completionHandler:^{ [self completePost:nil]; - }withFailure:^(NSError * error) { + } + withFailure:^(NSError *error) { [self completePost:error]; - }]; + }]; } --(void)completePost:(NSError *)error { - if (!error) { - - BVFeatureUsedEvent *submittedPostEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventNameWriteReview - withAdditionalParams:nil]; - - [BVPixel trackEvent:submittedPostEvent]; - - } - - [self dismissViewControllerAnimated:NO completion:^{ - if (_didCompletePost) { - _didCompletePost(error); - } - }]; +- (void)completePost:(NSError *)error { + if (!error) { + BVFeatureUsedEvent *submittedPostEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventNameWriteReview + withAdditionalParams:nil]; + + [BVPixel trackEvent:submittedPostEvent]; + } + + [self dismissViewControllerAnimated:NO + completion:^{ + if (_didCompletePost) { + _didCompletePost(error); + } + }]; } --(BOOL)isContentValid { - return self.textView.text.length && _postRequest.alias.length && _postRequest.token.length; +- (BOOL)isContentValid { + return self.textView.text.length && _postRequest.alias.length && + _postRequest.token.length; } --(UIView *)loadPreviewView { - UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)]; - imageView.contentMode = UIViewContentModeScaleAspectFit; - imageView.image = [self proportionallyScaledImageOf:_postRequest.image inBoundingDimensions:imageView.frame.size]; - - return imageView; +- (UIView *)loadPreviewView { + UIImageView *imageView = + [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.image = [self proportionallyScaledImageOf:_postRequest.image + inBoundingDimensions:imageView.frame.size]; + + return imageView; } -- (UIImage *)navBarBackgroundImage:(UIColor *)backgroundColor logo:(UIImage *)logo size:(CGSize)size { - CGRect frame = CGRectZero; - frame.size = size; - UIGraphicsBeginImageContextWithOptions(size, false, 0); - [backgroundColor setFill]; - UIRectFill(frame); - - CGFloat logoPadding = 2; - CGFloat dimension = size.height - logoPadding; - CGSize logoSize = [self proportionalSizeOf:logo.size inBoundingSize:CGSizeMake(CGFLOAT_MAX, dimension)]; - CGRect logoFrame = CGRectMake(size.width / 2 - logoSize.width / 2, logoPadding / 2, logoSize.width, logoSize.height); - [logo drawInRect:logoFrame]; - - UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return backgroundImage; +- (UIImage *)navBarBackgroundImage:(UIColor *)backgroundColor + logo:(UIImage *)logo + size:(CGSize)size { + CGRect frame = CGRectZero; + frame.size = size; + UIGraphicsBeginImageContextWithOptions(size, false, 0); + [backgroundColor setFill]; + UIRectFill(frame); + + CGFloat logoPadding = 2; + CGFloat dimension = size.height - logoPadding; + CGSize logoSize = + [self proportionalSizeOf:logo.size + inBoundingSize:CGSizeMake(CGFLOAT_MAX, dimension)]; + CGRect logoFrame = + CGRectMake(size.width / 2 - logoSize.width / 2, logoPadding / 2, + logoSize.width, logoSize.height); + [logo drawInRect:logoFrame]; + + UIImage *backgroundImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return backgroundImage; } - (NSArray *)configurationItems { - SLComposeSheetConfigurationItem *item = [SLComposeSheetConfigurationItem new]; - item.title = @"Username"; - item.value = _postRequest.alias; - - return @[item]; + SLComposeSheetConfigurationItem *item = [SLComposeSheetConfigurationItem new]; + item.title = @"Username"; + item.value = _postRequest.alias; + + return @[ item ]; } -- (UIImage *)proportionallyScaledImageOf:(UIImage *)image inBoundingDimensions:(CGSize)dimensions { - CGSize imgSize = image.size; - CGSize newSize = [self proportionalSizeOf:imgSize inBoundingSize:dimensions]; - - CGImageRef cgImage = image.CGImage; - size_t bpc = CGImageGetBitsPerComponent(cgImage); - size_t bpr = CGImageGetBytesPerRow(cgImage); - CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); - CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage); - - CGContextRef context = CGBitmapContextCreate(nil, newSize.width, newSize.height, bpc, bpr, colorSpace, bitmapInfo); - CGContextSetInterpolationQuality(context, kCGInterpolationHigh); - - CGContextDrawImage(context, CGRectMake(0, 0, newSize.width, newSize.height), cgImage); - return [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)]; +- (UIImage *)proportionallyScaledImageOf:(UIImage *)image + inBoundingDimensions:(CGSize)dimensions { + CGSize imgSize = image.size; + CGSize newSize = [self proportionalSizeOf:imgSize inBoundingSize:dimensions]; + + CGImageRef cgImage = image.CGImage; + size_t bpc = CGImageGetBitsPerComponent(cgImage); + size_t bpr = CGImageGetBytesPerRow(cgImage); + CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage); + + CGContextRef context = CGBitmapContextCreate( + nil, newSize.width, newSize.height, bpc, bpr, colorSpace, bitmapInfo); + CGContextSetInterpolationQuality(context, kCGInterpolationHigh); + + CGContextDrawImage(context, CGRectMake(0, 0, newSize.width, newSize.height), + cgImage); + return [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)]; } --(CGSize)proportionalSizeOf:(CGSize)size inBoundingSize:(CGSize)boundingSize { - CGFloat newW = boundingSize.width; - CGFloat newH = newW * size.height / size.width; - - if (newH > boundingSize.height) { - newH = boundingSize.height; - newW = newH * size.width / size.height; - } - return CGSizeMake(newW, newH); +- (CGSize)proportionalSizeOf:(CGSize)size inBoundingSize:(CGSize)boundingSize { + CGFloat newW = boundingSize.width; + CGFloat newH = newW * size.height / size.width; + + if (newH > boundingSize.height) { + newH = boundingSize.height; + newW = newH * size.width / size.height; + } + return CGSizeMake(newW, newH); } --(void)dismiss { - [self dismissViewControllerAnimated:YES completion:^{ - if (_didPressCancel) { - _didPressCancel(); - } - }]; +- (void)dismiss { + [self dismissViewControllerAnimated:YES + completion:^{ + if (_didPressCancel) { + _didPressCancel(); + } + }]; } + @end diff --git a/Pod/BVCurationsUI/BVCurationsUICollectionView.h b/Pod/BVCurationsUI/BVCurationsUICollectionView.h old mode 100755 new mode 100644 index 604a0457..07608e95 --- a/Pod/BVCurationsUI/BVCurationsUICollectionView.h +++ b/Pod/BVCurationsUI/BVCurationsUICollectionView.h @@ -10,101 +10,105 @@ @class BVCurationsFeedItem; typedef NS_ENUM(NSInteger, BVCurationsUILayout) { - BVCurationsUILayoutGrid, - BVCurationsUILayoutCarousel + BVCurationsUILayoutGrid, + BVCurationsUILayoutCarousel }; @protocol BVCurationsUICollectionViewDelegate; @interface BVCurationsUICollectionView : UICollectionView -@property (nonatomic, weak, nullable) id curationsDelegate; - +@property(nonatomic, weak, nullable) id + curationsDelegate; /** The groups to be included in the carousel Default is no groups, so no content */ -@property (nonatomic, strong, nonnull) NSArray* groups; +@property(nonatomic, strong, nonnull) NSArray *groups; /** Used to only show content that includes a particular product by ID Default is not to filter by product */ -@property (nonatomic, strong, nonnull) NSString* productId; +@property(nonatomic, strong, nonnull) NSString *productId; /** */ -@property (nonatomic, strong, readonly, nonnull) NSArray *feedItems; - +@property(nonatomic, strong, readonly, nonnull) + NSArray *feedItems; /** Amount of items to request per infinite scroll batch Default is 10 */ -@property (nonatomic, assign) NSUInteger fetchSize; - +@property(nonatomic, assign) NSUInteger fetchSize; /** Should more items load automatically as scrolled Default is YES */ -@property (nonatomic, assign) BOOL infiniteScrollEnabled; +@property(nonatomic, assign) BOOL infiniteScrollEnabled; /** Defines the layout of content Defaults to BVCurationsUILayoutGrid */ -@property (nonatomic, assign) BVCurationsUILayout bvCurationsUILayout; +@property(nonatomic, assign) BVCurationsUILayout bvCurationsUILayout; /** Number of content items in each row when bvCurationsUILayout == BVCurationsUILayoutGrid Defaults to 2 */ -@property (nonatomic, assign) NSUInteger itemsPerRow; +@property(nonatomic, assign) NSUInteger itemsPerRow; /** Begins loading content Should be called only once after setting the properties the first time */ --(void)loadFeed; +- (void)loadFeed; @end -typedef void (^BVCurationsLoadImageCompletion)(UIImage * _Nullable image, NSString * _Nonnull imageUrl); -typedef void (^BVCurationsIsImageCachedCompletion)(BOOL isCached, NSString * _Nonnull imageUrl); +typedef void (^BVCurationsLoadImageCompletion)(UIImage *__nullable image, + NSString *__nonnull imageUrl); +typedef void (^BVCurationsIsImageCachedCompletion)( + BOOL isCached, NSString *__nonnull imageUrl); @protocol BVCurationsUICollectionViewDelegate @required /** Used to handle image loading to allow for custom caching. - + @param imageUrl url that should be loaded @param completion Must be called with loaded image and url */ --(void)curationsLoadImage:(NSString * _Nonnull) imageUrl completion:(BVCurationsLoadImageCompletion _Nonnull) completion; +- (void)curationsLoadImage:(nonnull NSString *)imageUrl + completion:(nonnull BVCurationsLoadImageCompletion)completion; /** Used to let SDK know if image is cached to opitimize display. @param imageUrl url to verify if cached @param completion Must be called with BOOL isCached and url */ --(void)curationsImageIsCached:(NSString * _Nonnull)imageUrl completion:(BVCurationsIsImageCachedCompletion _Nonnull)completion; +- (void)curationsImageIsCached:(nonnull NSString *)imageUrl + completion: + (nonnull BVCurationsIsImageCachedCompletion)completion; @optional /** Called when a user taps on an item - + @param feedItem the item the user tapped */ --(void)curationsDidSelectFeedItem:(BVCurationsFeedItem * _Nonnull)feedItem; +- (void)curationsDidSelectFeedItem:(nonnull BVCurationsFeedItem *)feedItem; /** Called if fetching the feed failed @param error the error that occured */ --(void)curationsFailedToLoadFeed:(NSError * _Nonnull)error; +- (void)curationsFailedToLoadFeed:(nonnull NSError *)error; @end diff --git a/Pod/BVCurationsUI/BVCurationsUICollectionView.m b/Pod/BVCurationsUI/BVCurationsUICollectionView.m old mode 100755 new mode 100644 index 27a5b86d..48046f6b --- a/Pod/BVCurationsUI/BVCurationsUICollectionView.m +++ b/Pod/BVCurationsUI/BVCurationsUICollectionView.m @@ -7,333 +7,385 @@ // #import "BVCurationsUICollectionView.h" -#import "BVCurationsUICollectionViewCell.h" #import "BVCurationsFeedLoader.h" +#import "BVCurationsUICollectionViewCell.h" -@interface BVCurationsUICollectionView() -@property (nonatomic, strong) BVCurationsFeedLoader *curationsFeedLoader; -@property (nonatomic, strong) NSMutableArray *curationsFeedItems; -@property (nonatomic, strong) NSNumber *lastFetchedTimeStamp; -@property (nonatomic, assign) CGFloat totalHeight; -@property (nonatomic, assign) BOOL pendingUpdate; -@property (nonatomic, assign) BOOL allItemsFetched; -@property (nonatomic, strong) NSMutableSet *impressedItems; -@property (nonatomic, assign) CGFloat oldTimeStamp; -@property (nonatomic, assign) CGFloat oldBounds; -@property (nonatomic, strong) NSNumber* shouldRequestImageLoad; +@interface BVCurationsUICollectionView () < + UICollectionViewDelegate, UICollectionViewDataSource, + UICollectionViewDelegateFlowLayout, UIScrollViewDelegate> +@property(nonatomic, strong) BVCurationsFeedLoader *curationsFeedLoader; +@property(nonatomic, strong) + NSMutableArray *curationsFeedItems; +@property(nonatomic, strong) NSNumber *lastFetchedTimeStamp; +@property(nonatomic, assign) CGFloat totalHeight; +@property(nonatomic, assign) BOOL pendingUpdate; +@property(nonatomic, assign) BOOL allItemsFetched; +@property(nonatomic, strong) NSMutableSet *impressedItems; +@property(nonatomic, assign) CGFloat oldTimeStamp; +@property(nonatomic, assign) CGFloat oldBounds; +@property(nonatomic, strong) NSNumber *shouldRequestImageLoad; @end #define BVCurationCVCellID @"BVCurationsCollectionViewCell" #define BVCurationsDesiredPadding 3 #define BVCurationsRowsOffScreenBeforeReload 2.5 -#define BVCurationsVelocityThreshold 1000 //points per second +#define BVCurationsVelocityThreshold 1000 // points per second @implementation BVCurationsUICollectionView --(instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - if ([super initWithFrame:frame collectionViewLayout:layout]) { - [self setup]; - } - return self; +- (instancetype)initWithFrame:(CGRect)frame + collectionViewLayout:(UICollectionViewLayout *)layout { + if ([super initWithFrame:frame collectionViewLayout:layout]) { + [self setup]; + } + return self; } --(instancetype)initWithCoder:(NSCoder *)aDecoder { - if ([super initWithCoder:aDecoder]) { - [self setup]; - } - - return self; +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if ([super initWithCoder:aDecoder]) { + [self setup]; + } + + return self; } --(void)setup { - _curationsFeedLoader = [[BVCurationsFeedLoader alloc]init]; - _curationsFeedItems = [NSMutableArray new]; - _impressedItems = [NSMutableSet new]; - _fetchSize = 10; - _infiniteScrollEnabled = YES; - _itemsPerRow = 2; - _pendingUpdate = NO; - _shouldRequestImageLoad = @YES; - - self.delegate = self; - self.dataSource = self; - [self registerClass:[BVCurationsUICollectionViewCell class] forCellWithReuseIdentifier:BVCurationCVCellID]; - UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout*)self.collectionViewLayout; - layout.minimumLineSpacing = BVCurationsDesiredPadding; - layout.minimumInteritemSpacing = BVCurationsDesiredPadding; +- (void)setup { + _curationsFeedLoader = [[BVCurationsFeedLoader alloc] init]; + _curationsFeedItems = [NSMutableArray new]; + _impressedItems = [NSMutableSet new]; + _fetchSize = 10; + _infiniteScrollEnabled = YES; + _itemsPerRow = 2; + _pendingUpdate = NO; + _shouldRequestImageLoad = @YES; + + self.delegate = self; + self.dataSource = self; + [self registerClass:[BVCurationsUICollectionViewCell class] + forCellWithReuseIdentifier:BVCurationCVCellID]; + UICollectionViewFlowLayout *layout = + (UICollectionViewFlowLayout *)self.collectionViewLayout; + layout.minimumLineSpacing = BVCurationsDesiredPadding; + layout.minimumInteritemSpacing = BVCurationsDesiredPadding; } --(void)loadFeed { - NSUInteger fetchSize = _fetchSize; - - /* - If enabled we need to bump the initial fetch size to load just enough - items to enable scrolling (need a little bit off screen) - */ - if (_infiniteScrollEnabled) { - CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; - NSUInteger totalExpected = _curationsFeedItems.count + fetchSize; - CGFloat estimatedTotalHeight = oneCellHeight * ceil(totalExpected / _itemsPerRow); - if (estimatedTotalHeight < self.bounds.size.height) { - CGFloat diff = self.bounds.size.height - estimatedTotalHeight; - NSUInteger numRowOffSet = ceil(diff / oneCellHeight); - fetchSize += (numRowOffSet * _itemsPerRow); - } +- (void)loadFeed { + NSUInteger fetchSize = _fetchSize; + + /* + If enabled we need to bump the initial fetch size to load just enough + items to enable scrolling (need a little bit off screen) + */ + if (_infiniteScrollEnabled) { + CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; + NSUInteger totalExpected = _curationsFeedItems.count + fetchSize; + CGFloat estimatedTotalHeight = + oneCellHeight * ceil(totalExpected / _itemsPerRow); + if (estimatedTotalHeight < self.bounds.size.height) { + CGFloat diff = self.bounds.size.height - estimatedTotalHeight; + NSUInteger numRowOffSet = ceil(diff / oneCellHeight); + fetchSize += (numRowOffSet * _itemsPerRow); } - - [self loadCurationsFeed:fetchSize lastFetched:_lastFetchedTimeStamp]; + } + + [self loadCurationsFeed:fetchSize lastFetched:_lastFetchedTimeStamp]; } --(void)setDelegate:(id)delegate { - //placeholder may want to disallow - super.delegate = delegate; +- (void)setDelegate:(id)delegate { + // placeholder may want to disallow + super.delegate = delegate; } --(void)setDataSource:(id)dataSource { - //placeholder may want to disallow - super.dataSource = dataSource; +- (void)setDataSource:(id)dataSource { + // placeholder may want to disallow + super.dataSource = dataSource; } --(void)setBvCurationsUILayout:(BVCurationsUILayout)bvCurationsUILayout { - _bvCurationsUILayout = bvCurationsUILayout; - UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout*)self.collectionViewLayout; - switch (_bvCurationsUILayout) { - case BVCurationsUILayoutGrid: - layout.scrollDirection = UICollectionViewScrollDirectionVertical; - break; - case BVCurationsUILayoutCarousel: - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; - break; - default: - break; - } - [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count itemsPerRow:self.itemsPerRow]; - - [self reloadData]; +- (void)setBvCurationsUILayout:(BVCurationsUILayout)bvCurationsUILayout { + _bvCurationsUILayout = bvCurationsUILayout; + UICollectionViewFlowLayout *layout = + (UICollectionViewFlowLayout *)self.collectionViewLayout; + switch (_bvCurationsUILayout) { + case BVCurationsUILayoutGrid: + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + break; + case BVCurationsUILayoutCarousel: + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + break; + default: + break; + } + [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count + itemsPerRow:self.itemsPerRow]; + + [self reloadData]; } --(NSArray*)feedItems { - return [NSArray arrayWithArray:_curationsFeedItems]; +- (NSArray *)feedItems { + return [NSArray arrayWithArray:_curationsFeedItems]; } --(void)loadCurationsFeed:(NSUInteger)limit lastFetched:(NSNumber*)lastFetchedTimeStamp { - if (_pendingUpdate || _allItemsFetched) { - return; - } - - if (!_groups) { - [[BVLogger sharedLogger] error:@"Groups not set on BVCurationsUICollectionView. Unable to load feed"]; - return; - } - - _pendingUpdate = YES; - BVCurationsFeedRequest *req = [[BVCurationsFeedRequest alloc]initWithGroups:_groups]; - req.limit = limit; - req.hasPhotoOrVideo = @YES; - if (_lastFetchedTimeStamp) { - req.before = _lastFetchedTimeStamp; - } - - if (_productId.length) { - req.externalId = _productId; - } - - __weak typeof(self) weakSelf = self; - [_curationsFeedLoader loadFeedWithRequest:req completionHandler:^(NSArray *items){ - - BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] initWithProductId:req.externalId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withContainerId:@"CurationsUICollectionView" - withAdditionalParams:nil]; - +- (void)loadCurationsFeed:(NSUInteger)limit + lastFetched:(NSNumber *)lastFetchedTimeStamp { + if (_pendingUpdate || _allItemsFetched) { + return; + } + + if (!_groups) { + [[BVLogger sharedLogger] error:@"Groups not set on " + @"BVCurationsUICollectionView. Unable " + @"to load feed"]; + return; + } + + _pendingUpdate = YES; + BVCurationsFeedRequest *req = + [[BVCurationsFeedRequest alloc] initWithGroups:_groups]; + req.limit = limit; + req.hasPhotoOrVideo = @YES; + if (_lastFetchedTimeStamp) { + req.before = _lastFetchedTimeStamp; + } + + if (_productId.length) { + req.externalId = _productId; + } + + __weak typeof(self) weakSelf = self; + [_curationsFeedLoader loadFeedWithRequest:req + completionHandler:^(NSArray *items) { + + BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] + initWithProductId:req.externalId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withContainerId:@"CurationsUICollectionView" + withAdditionalParams:nil]; + [BVPixel trackEvent:inViewEvent]; - + weakSelf.allItemsFetched = !items.count; weakSelf.pendingUpdate = NO; weakSelf.lastFetchedTimeStamp = items.lastObject.timestamp; [weakSelf.curationsFeedItems addObjectsFromArray:items]; - [weakSelf updateInfiniteScrollMeasurements:weakSelf.curationsFeedItems.count itemsPerRow:weakSelf.itemsPerRow]; - - BOOL errorState = items.count > limit; //if for whatever reason the api returns more items than expected we'll just reload + [weakSelf + updateInfiniteScrollMeasurements:weakSelf.curationsFeedItems.count + itemsPerRow:weakSelf.itemsPerRow]; + + BOOL errorState = items.count > limit; // if for whatever reason the + // api returns more items than + // expected we'll just reload if (errorState) { - [self reloadData]; - }else { - NSMutableArray *indexPaths = [NSMutableArray new]; - NSUInteger inserted = (limit <= items.count)? limit : items.count; - for (int i = 0; i < inserted; i ++) { - [indexPaths addObject:[NSIndexPath indexPathForItem:weakSelf.curationsFeedItems.count - 1 - i inSection:0]]; - } - - [self insertItemsAtIndexPaths:indexPaths]; + [self reloadData]; + } else { + NSMutableArray *indexPaths = [NSMutableArray new]; + NSUInteger inserted = (limit <= items.count) ? limit : items.count; + for (int i = 0; i < inserted; i++) { + [indexPaths + addObject:[NSIndexPath + indexPathForItem:weakSelf.curationsFeedItems + .count - + 1 - i + inSection:0]]; + } + + [self insertItemsAtIndexPaths:indexPaths]; } - } withFailure:^(NSError *err) { + } + withFailure:^(NSError *err) { weakSelf.pendingUpdate = NO; - if ([_curationsDelegate respondsToSelector:@selector(curationsFailedToLoadFeed:)]) { - [_curationsDelegate curationsFailedToLoadFeed:err]; + if ([_curationsDelegate + respondsToSelector:@selector(curationsFailedToLoadFeed:)]) { + [_curationsDelegate curationsFailedToLoadFeed:err]; } - }]; + }]; } - --(void)setItemsPerRow:(NSUInteger)itemsPerRow{ - _itemsPerRow = itemsPerRow; - [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count itemsPerRow:self.itemsPerRow]; - [self loadFeed]; +- (void)setItemsPerRow:(NSUInteger)itemsPerRow { + _itemsPerRow = itemsPerRow; + [self updateInfiniteScrollMeasurements:self.curationsFeedItems.count + itemsPerRow:self.itemsPerRow]; + [self loadFeed]; } --(void)updateInfiniteScrollMeasurements:(NSInteger)itemCount itemsPerRow:(NSInteger)itemsPerRow { - _totalHeight = [self getTotalHeight:itemCount itemsPerRow:itemsPerRow]; +- (void)updateInfiniteScrollMeasurements:(NSInteger)itemCount + itemsPerRow:(NSInteger)itemsPerRow { + _totalHeight = [self getTotalHeight:itemCount itemsPerRow:itemsPerRow]; } --(CGFloat)getTotalHeight:(NSInteger)itemCount itemsPerRow:(NSInteger)itemsPerRow { - CGFloat oneCellHeight = [self getOneCellDim: itemsPerRow]; - NSInteger numRows; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - numRows = ceil(_curationsFeedItems.count / _itemsPerRow); - }else { - numRows = _curationsFeedItems.count; - } - return oneCellHeight * numRows; +- (CGFloat)getTotalHeight:(NSInteger)itemCount + itemsPerRow:(NSInteger)itemsPerRow { + CGFloat oneCellHeight = [self getOneCellDim:itemsPerRow]; + NSInteger numRows; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + numRows = ceil(_curationsFeedItems.count / _itemsPerRow); + } else { + numRows = _curationsFeedItems.count; + } + return oneCellHeight * numRows; } --(CGFloat)getOneCellDim:(NSInteger)itemsPerRow { - CGFloat dim; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - dim = (self.frame.size.width / itemsPerRow) - BVCurationsDesiredPadding; - }else { - dim = self.frame.size.height - BVCurationsDesiredPadding; - } - return dim; +- (CGFloat)getOneCellDim:(NSInteger)itemsPerRow { + CGFloat dim; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + dim = (self.frame.size.width / itemsPerRow) - BVCurationsDesiredPadding; + } else { + dim = self.frame.size.height - BVCurationsDesiredPadding; + } + return dim; } --(void)setFrame:(CGRect)frame { - super.frame = frame; - if (_itemsPerRow) { - [self updateInfiniteScrollMeasurements:_curationsFeedItems.count itemsPerRow:_itemsPerRow]; - } +- (void)setFrame:(CGRect)frame { + super.frame = frame; + if (_itemsPerRow) { + [self updateInfiniteScrollMeasurements:_curationsFeedItems.count + itemsPerRow:_itemsPerRow]; + } } -#pragma mark: UICollectionViewDataSource -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return _curationsFeedItems.count; +#pragma mark : UICollectionViewDataSource +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return _curationsFeedItems.count; } -- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - BVCurationsUICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:BVCurationCVCellID forIndexPath:indexPath]; - cell.shouldLoadObject = self; - cell.shouldLoadKeypath = @"shouldRequestImageLoad"; - cell.loadImageHandler = ^(NSString * _Nonnull imageUrl, BVCurationsLoadImageCompletion _Nonnull completion) { +- (__kindof UICollectionViewCell *)collectionView: + (UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + BVCurationsUICollectionViewCell *cell = + [collectionView dequeueReusableCellWithReuseIdentifier:BVCurationCVCellID + forIndexPath:indexPath]; + cell.shouldLoadObject = self; + cell.shouldLoadKeypath = @"shouldRequestImageLoad"; + cell.loadImageHandler = + ^(NSString *__nonnull imageUrl, + BVCurationsLoadImageCompletion __nonnull completion) { [_curationsDelegate curationsLoadImage:imageUrl completion:completion]; - }; - cell.loadImageIsCachedHandler = ^(NSString * _Nonnull imageUrl, BVCurationsIsImageCachedCompletion completion) { - [_curationsDelegate curationsImageIsCached:imageUrl completion:^(BOOL isCached, NSString * _Nonnull imageUrl) { - completion(isCached, imageUrl); - }]; - }; - cell.curationsFeedItem = _curationsFeedItems[indexPath.item]; - - if (![_impressedItems containsObject:cell.curationsFeedItem]) { - [_impressedItems addObject:cell.curationsFeedItem]; - - BVCurationsFeedItem *item = cell.curationsFeedItem; - - BVImpressionEvent *impression = [[BVImpressionEvent alloc] initWithProductId:item.externalId - withContentId:item.contentId - withCategoryId:nil - withProductType:BVPixelProductTypeCurations - withContentType:BVPixelImpressionContentCurationsFeedItem withBrand:nil - withAdditionalParams:@{@"syndicationSource":item.sourceClient}]; - - [BVPixel trackEvent:impression]; - } - return cell; + }; + cell.loadImageIsCachedHandler = ^( + NSString *__nonnull imageUrl, + BVCurationsIsImageCachedCompletion completion) { + [_curationsDelegate + curationsImageIsCached:imageUrl + completion:^(BOOL isCached, NSString *__nonnull imageUrl) { + completion(isCached, imageUrl); + }]; + }; + cell.curationsFeedItem = _curationsFeedItems[indexPath.item]; + + if (![_impressedItems containsObject:cell.curationsFeedItem]) { + [_impressedItems addObject:cell.curationsFeedItem]; + + BVCurationsFeedItem *item = cell.curationsFeedItem; + + BVImpressionEvent *impression = [[BVImpressionEvent alloc] + initWithProductId:item.externalId + withContentId:item.contentId + withCategoryId:nil + withProductType:BVPixelProductTypeCurations + withContentType:BVPixelImpressionContentCurationsFeedItem + withBrand:nil + withAdditionalParams:@{@"syndicationSource" : item.sourceClient}]; + + [BVPixel trackEvent:impression]; + } + return cell; } -#pragma mark: UICollectionViewDelegateFlowLayout -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - CGFloat oneCellDim = [self getOneCellDim: _itemsPerRow]; - return CGSizeMake(oneCellDim, oneCellDim); +#pragma mark : UICollectionViewDelegateFlowLayout +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + CGFloat oneCellDim = [self getOneCellDim:_itemsPerRow]; + return CGSizeMake(oneCellDim, oneCellDim); } -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { - CGFloat padding = BVCurationsDesiredPadding / 2; - return UIEdgeInsetsMake(padding, padding, padding, padding); +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + insetForSectionAtIndex:(NSInteger)section { + CGFloat padding = BVCurationsDesiredPadding / 2; + return UIEdgeInsetsMake(padding, padding, padding, padding); } -#pragma mark: UICollectionViewDelegate -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { - [collectionView deselectItemAtIndexPath:indexPath animated:YES]; - BVCurationsFeedItem *item = _curationsFeedItems[indexPath.item]; - - BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] initWithProductId:item.externalId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventContentClick - withAdditionalParams:nil]; - - [BVPixel trackEvent:tapEvent]; - - if ([_curationsDelegate respondsToSelector:@selector(curationsDidSelectFeedItem:)]) { - [_curationsDelegate curationsDidSelectFeedItem:item]; - } +#pragma mark : UICollectionViewDelegate +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + [collectionView deselectItemAtIndexPath:indexPath animated:YES]; + BVCurationsFeedItem *item = _curationsFeedItems[indexPath.item]; + + BVFeatureUsedEvent *tapEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:item.externalId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventContentClick + withAdditionalParams:nil]; + + [BVPixel trackEvent:tapEvent]; + + if ([_curationsDelegate + respondsToSelector:@selector(curationsDidSelectFeedItem:)]) { + [_curationsDelegate curationsDidSelectFeedItem:item]; + } } -#pragma mark: UIScrollViewDelegate +#pragma mark : UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - if (!_infiniteScrollEnabled || _allItemsFetched) { - return; - } - - CGFloat timestamp = [[NSDate date] timeIntervalSince1970]; - CGFloat newBounds = (_bvCurationsUILayout == BVCurationsUILayoutGrid)? scrollView.bounds.origin.y : scrollView.bounds.origin.x; - CGFloat boundsDiff = fabs(_oldBounds - newBounds); - CGFloat timeDiff = timestamp - _oldTimeStamp; - _oldTimeStamp = timestamp; - _oldBounds = newBounds; - CGFloat velocity = boundsDiff / timeDiff; - - if (velocity < BVCurationsVelocityThreshold) { - [self attemptToTransitionToLoadImages]; - }else { - _shouldRequestImageLoad = @NO; - } - - CGFloat oneCellHeight = [self getOneCellDim: _itemsPerRow]; - CGFloat lowerBounds; - if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { - lowerBounds = scrollView.bounds.origin.y + scrollView.bounds.size.height; - }else { - lowerBounds = scrollView.bounds.origin.x + scrollView.bounds.size.width; - } - - CGFloat totalHeigthWithReloadOffest = (_totalHeight - (oneCellHeight * BVCurationsRowsOffScreenBeforeReload)); - if (lowerBounds > totalHeigthWithReloadOffest) { - [self loadCurationsFeed:_fetchSize lastFetched:_lastFetchedTimeStamp]; - } + if (!_infiniteScrollEnabled || _allItemsFetched) { + return; + } + + CGFloat timestamp = [[NSDate date] timeIntervalSince1970]; + CGFloat newBounds = (_bvCurationsUILayout == BVCurationsUILayoutGrid) + ? scrollView.bounds.origin.y + : scrollView.bounds.origin.x; + CGFloat boundsDiff = fabs(_oldBounds - newBounds); + CGFloat timeDiff = timestamp - _oldTimeStamp; + _oldTimeStamp = timestamp; + _oldBounds = newBounds; + CGFloat velocity = boundsDiff / timeDiff; + + if (velocity < BVCurationsVelocityThreshold) { + [self attemptToTransitionToLoadImages]; + } else { + _shouldRequestImageLoad = @NO; + } + + CGFloat oneCellHeight = [self getOneCellDim:_itemsPerRow]; + CGFloat lowerBounds; + if (_bvCurationsUILayout == BVCurationsUILayoutGrid) { + lowerBounds = scrollView.bounds.origin.y + scrollView.bounds.size.height; + } else { + lowerBounds = scrollView.bounds.origin.x + scrollView.bounds.size.width; + } + + CGFloat totalHeigthWithReloadOffest = + (_totalHeight - (oneCellHeight * BVCurationsRowsOffScreenBeforeReload)); + if (lowerBounds > totalHeigthWithReloadOffest) { + [self loadCurationsFeed:_fetchSize lastFetched:_lastFetchedTimeStamp]; + } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - [self attemptToTransitionToLoadImages]; - - BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] initWithProductId:_productId - withBrand:nil - withProductType:BVPixelProductTypeCurations - withEventName:BVPixelFeatureUsedEventNameScrolled - withAdditionalParams:nil]; - - [BVPixel trackEvent:scrollEvent]; - + [self attemptToTransitionToLoadImages]; + + BVFeatureUsedEvent *scrollEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:_productId + withBrand:nil + withProductType:BVPixelProductTypeCurations + withEventName:BVPixelFeatureUsedEventNameScrolled + withAdditionalParams:nil]; + + [BVPixel trackEvent:scrollEvent]; } --(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { - [self attemptToTransitionToLoadImages]; +- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { + [self attemptToTransitionToLoadImages]; } --(void)attemptToTransitionToLoadImages { - BOOL transition = !_shouldRequestImageLoad.boolValue; - if (transition) { - self.shouldRequestImageLoad = @YES; - } +- (void)attemptToTransitionToLoadImages { + BOOL transition = !_shouldRequestImageLoad.boolValue; + if (transition) { + self.shouldRequestImageLoad = @YES; + } } @end diff --git a/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.h b/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.h old mode 100755 new mode 100644 index 32bb7c47..1a201476 --- a/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.h +++ b/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.h @@ -6,21 +6,27 @@ // // -#import #import "BVCurationsUICollectionView.h" +#import @class BVCurationsFeedItem; -typedef void (^BVCurationsLoadImageHandler)(NSString * _Nonnull imageUrl, BVCurationsLoadImageCompletion _Nonnull completion); -typedef void (^BVCurationsImageIsCachedHandler)(NSString * _Nonnull imageUrl, BVCurationsIsImageCachedCompletion _Nonnull completion); +typedef void (^BVCurationsLoadImageHandler)( + NSString *__nonnull imageUrl, + BVCurationsLoadImageCompletion __nonnull completion); +typedef void (^BVCurationsImageIsCachedHandler)( + NSString *__nonnull imageUrl, + BVCurationsIsImageCachedCompletion __nonnull completion); @interface BVCurationsUICollectionViewCell : UICollectionViewCell -@property (nonatomic, copy, nonnull) BVCurationsLoadImageHandler loadImageHandler; -@property (nonatomic, copy, nonnull) BVCurationsImageIsCachedHandler loadImageIsCachedHandler; +@property(nonatomic, copy, nonnull) + BVCurationsLoadImageHandler loadImageHandler; +@property(nonatomic, copy, nonnull) + BVCurationsImageIsCachedHandler loadImageIsCachedHandler; -@property (nonatomic, strong, nonnull) BVCurationsFeedItem *curationsFeedItem; -@property (nonatomic, assign, nonnull) id shouldLoadObject; -@property (nonatomic, strong, nonnull) NSString *shouldLoadKeypath; +@property(nonatomic, strong, nonnull) BVCurationsFeedItem *curationsFeedItem; +@property(nonatomic, assign, nonnull) id shouldLoadObject; +@property(nonatomic, strong, nonnull) NSString *shouldLoadKeypath; @end diff --git a/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.m b/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.m old mode 100755 new mode 100644 index cded6e31..239c47dc --- a/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.m +++ b/Pod/BVCurationsUI/BVCurationsUICollectionViewCell.m @@ -12,145 +12,261 @@ #import "BVLogger.h" #import "UIImage+BundleLocator.h" -@interface BVCurationsUICollectionViewCell() +@interface BVCurationsUICollectionViewCell () -@property (nonatomic, strong) UIImageView *socialImageView; -@property (nonatomic, strong) UIImageView *sourceIconImageView; -@property (nonatomic, strong) UIImageView *playIconImageView; +@property(nonatomic, strong) UIImageView *socialImageView; +@property(nonatomic, strong) UIImageView *sourceIconImageView; +@property(nonatomic, strong) UIImageView *playIconImageView; @end @implementation BVCurationsUICollectionViewCell --(instancetype)initWithFrame:(CGRect)frame { - if ([super initWithFrame:frame]) { - [self setup]; - } - - return self; +- (instancetype)initWithFrame:(CGRect)frame { + if ([super initWithFrame:frame]) { + [self setup]; + } + + return self; } --(void)dealloc { - [_shouldLoadObject removeObserver:self forKeyPath:_shouldLoadKeypath]; +- (void)dealloc { + [_shouldLoadObject removeObserver:self forKeyPath:_shouldLoadKeypath]; } --(void)setup { - _socialImageView = [[UIImageView alloc] init]; - _socialImageView.translatesAutoresizingMaskIntoConstraints = NO; - [self.contentView addSubview:_socialImageView]; - NSLayoutConstraint *heightSocial = [NSLayoutConstraint constraintWithItem:_socialImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]; - NSLayoutConstraint *widthSocial = [NSLayoutConstraint constraintWithItem:_socialImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]; - NSLayoutConstraint *centerXSocial = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_socialImageView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]; - NSLayoutConstraint *centerYSocial = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_socialImageView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]; - - _playIconImageView = [[UIImageView alloc] init]; - _playIconImageView.translatesAutoresizingMaskIntoConstraints = NO; - _playIconImageView.image = [UIImage bundledImageNamed:@"play"]; - _playIconImageView.contentMode = UIViewContentModeScaleAspectFit; - _playIconImageView.hidden = true; - [self.contentView addSubview: _playIconImageView]; - NSLayoutConstraint *centerXPlay = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_playIconImageView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]; - NSLayoutConstraint *centerYPlay = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_playIconImageView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]; - NSLayoutConstraint *heightPlay = [NSLayoutConstraint constraintWithItem:_playIconImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:45.0]; - heightSocial.priority = 1000; - NSLayoutConstraint *widthPlay = [NSLayoutConstraint constraintWithItem:_playIconImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_playIconImageView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0]; - widthPlay.priority = 1000; - NSLayoutConstraint *proportionalDimPlay = [NSLayoutConstraint constraintWithItem:_playIconImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeHeight multiplier:0.3 constant:1]; - proportionalDimPlay.priority = 999; - - _sourceIconImageView = [[UIImageView alloc] init]; - _sourceIconImageView.translatesAutoresizingMaskIntoConstraints = NO; - _sourceIconImageView.contentMode = UIViewContentModeScaleAspectFit; - [self.contentView addSubview:_sourceIconImageView]; - NSLayoutConstraint *heightMaxIcon = [NSLayoutConstraint constraintWithItem:_sourceIconImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:30.0]; - heightSocial.priority = 1000; - NSLayoutConstraint *widthMaxIcon = [NSLayoutConstraint constraintWithItem:_sourceIconImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:_sourceIconImageView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0]; - widthSocial.priority = 1000; - NSLayoutConstraint *proportionalDim = [NSLayoutConstraint constraintWithItem:_sourceIconImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeHeight multiplier:0.25 constant:1]; - proportionalDim.priority = 999; - NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeRightMargin relatedBy:NSLayoutRelationEqual toItem:_sourceIconImageView attribute:NSLayoutAttributeRightMargin multiplier:1.0 constant:6.0]; - NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeBottomMargin relatedBy:NSLayoutRelationEqual toItem:_sourceIconImageView attribute:NSLayoutAttributeBottomMargin multiplier:1.0 constant:6.0]; - - [NSLayoutConstraint activateConstraints:@[heightSocial, widthSocial, centerXSocial, centerYSocial, - right, bottom, heightMaxIcon, widthMaxIcon,proportionalDim, - centerXPlay, centerYPlay, widthPlay, heightPlay, proportionalDimPlay]]; +- (void)setup { + _socialImageView = [[UIImageView alloc] init]; + _socialImageView.translatesAutoresizingMaskIntoConstraints = NO; + [self.contentView addSubview:_socialImageView]; + NSLayoutConstraint *heightSocial = + [NSLayoutConstraint constraintWithItem:_socialImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.contentView + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *widthSocial = + [NSLayoutConstraint constraintWithItem:_socialImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:self.contentView + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *centerXSocial = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:_socialImageView + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *centerYSocial = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:_socialImageView + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:0.0]; + + _playIconImageView = [[UIImageView alloc] init]; + _playIconImageView.translatesAutoresizingMaskIntoConstraints = NO; + _playIconImageView.image = [UIImage bundledImageNamed:@"play"]; + _playIconImageView.contentMode = UIViewContentModeScaleAspectFit; + _playIconImageView.hidden = true; + [self.contentView addSubview:_playIconImageView]; + NSLayoutConstraint *centerXPlay = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:_playIconImageView + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *centerYPlay = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:_playIconImageView + attribute:NSLayoutAttributeCenterY + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *heightPlay = + [NSLayoutConstraint constraintWithItem:_playIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationLessThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:45.0]; + heightSocial.priority = 1000; + NSLayoutConstraint *widthPlay = + [NSLayoutConstraint constraintWithItem:_playIconImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:_playIconImageView + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:1.0]; + widthPlay.priority = 1000; + NSLayoutConstraint *proportionalDimPlay = + [NSLayoutConstraint constraintWithItem:_playIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.contentView + attribute:NSLayoutAttributeHeight + multiplier:0.3 + constant:1]; + proportionalDimPlay.priority = 999; + + _sourceIconImageView = [[UIImageView alloc] init]; + _sourceIconImageView.translatesAutoresizingMaskIntoConstraints = NO; + _sourceIconImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.contentView addSubview:_sourceIconImageView]; + NSLayoutConstraint *heightMaxIcon = + [NSLayoutConstraint constraintWithItem:_sourceIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationLessThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:30.0]; + heightSocial.priority = 1000; + NSLayoutConstraint *widthMaxIcon = + [NSLayoutConstraint constraintWithItem:_sourceIconImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:_sourceIconImageView + attribute:NSLayoutAttributeHeight + multiplier:1.0 + constant:1.0]; + widthSocial.priority = 1000; + NSLayoutConstraint *proportionalDim = + [NSLayoutConstraint constraintWithItem:_sourceIconImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.contentView + attribute:NSLayoutAttributeHeight + multiplier:0.25 + constant:1]; + proportionalDim.priority = 999; + NSLayoutConstraint *right = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeRightMargin + relatedBy:NSLayoutRelationEqual + toItem:_sourceIconImageView + attribute:NSLayoutAttributeRightMargin + multiplier:1.0 + constant:6.0]; + NSLayoutConstraint *bottom = + [NSLayoutConstraint constraintWithItem:self.contentView + attribute:NSLayoutAttributeBottomMargin + relatedBy:NSLayoutRelationEqual + toItem:_sourceIconImageView + attribute:NSLayoutAttributeBottomMargin + multiplier:1.0 + constant:6.0]; + + [NSLayoutConstraint activateConstraints:@[ + heightSocial, widthSocial, centerXSocial, centerYSocial, right, bottom, + heightMaxIcon, widthMaxIcon, proportionalDim, centerXPlay, centerYPlay, + widthPlay, heightPlay, proportionalDimPlay + ]]; } --(void)setCurationsFeedItem:(BVCurationsFeedItem *)curationsFeedItem { - _curationsFeedItem = curationsFeedItem; - [self updateUI]; +- (void)setCurationsFeedItem:(BVCurationsFeedItem *)curationsFeedItem { + _curationsFeedItem = curationsFeedItem; + [self updateUI]; } --(void)updateUI { - NSString *imgUrl = [self getThumbnailUrlForItem:_curationsFeedItem]; - - _playIconImageView.hidden = YES; - if (!_curationsFeedItem.photos.count && _curationsFeedItem.videos.count) { - _playIconImageView.hidden = NO; - } - - if (!imgUrl) { - _socialImageView.image = [UIImage bundledImageNamed:@"placeholder"]; +- (void)updateUI { + NSString *imgUrl = [self getThumbnailUrlForItem:_curationsFeedItem]; + + _playIconImageView.hidden = YES; + if (!_curationsFeedItem.photos.count && _curationsFeedItem.videos.count) { + _playIconImageView.hidden = NO; + } + + if (!imgUrl) { + _socialImageView.image = [UIImage bundledImageNamed:@"placeholder"]; + return; + } + + if (_loadImageHandler) { + BOOL shouldLoadImage = + [[_shouldLoadObject valueForKeyPath:_shouldLoadKeypath] boolValue]; + __weak typeof(self) weakSelf = self; + _loadImageIsCachedHandler(imgUrl, ^(BOOL cached, NSString *cacheUrl) { + if (!cached) { + + weakSelf.socialImageView.image = + [UIImage bundledImageNamed:@"placeholder"]; + } + + if (!cached && !shouldLoadImage) { return; - } - - if (_loadImageHandler) { - BOOL shouldLoadImage = [[_shouldLoadObject valueForKeyPath:_shouldLoadKeypath] boolValue]; - __weak typeof(self) weakSelf = self; - _loadImageIsCachedHandler(imgUrl, ^(BOOL cached, NSString *cacheUrl) { - if (!cached) { - - weakSelf.socialImageView.image = [UIImage bundledImageNamed:@"placeholder"]; - } - - if (!cached && !shouldLoadImage) { - return; - } - - weakSelf.loadImageHandler(imgUrl, ^(UIImage *image, NSString* url){ - if ([url isEqualToString:[weakSelf getThumbnailUrlForItem:weakSelf.curationsFeedItem]]) { - dispatch_async(dispatch_get_main_queue(), ^{ - weakSelf.socialImageView.image = image; - }); - } - }); - }); - }else { - [[BVLogger sharedLogger] error:@"Curations item not loaded due to nil BVCurationsFeedItemPressedHandler on BVCurationsUICollectionView"]; - } - - _sourceIconImageView.image = [self imageForChannel:_curationsFeedItem.channel]; + } + + weakSelf.loadImageHandler(imgUrl, ^(UIImage *image, NSString *url) { + if ([url isEqualToString:[weakSelf getThumbnailUrlForItem: + weakSelf.curationsFeedItem]]) { + dispatch_async(dispatch_get_main_queue(), ^{ + weakSelf.socialImageView.image = image; + }); + } + }); + }); + } else { + [[BVLogger sharedLogger] error:@"Curations item not loaded due to nil " + @"BVCurationsFeedItemPressedHandler on " + @"BVCurationsUICollectionView"]; + } + + _sourceIconImageView.image = + [self imageForChannel:_curationsFeedItem.channel]; } --(NSString *)getThumbnailUrlForItem:(BVCurationsFeedItem *)feedItem { - CGFloat screenScale = [[UIScreen mainScreen] scale]; - NSInteger dim = ceil(self.frame.size.height * screenScale); - NSString *format = @"%@&width=%ld&height=%ld&exact=true"; - - if (_curationsFeedItem.photos.count) { - return [NSString stringWithFormat:format ,_curationsFeedItem.photos[0].imageServiceUrl, dim, dim]; - }else if (_curationsFeedItem.videos.count) { - return [NSString stringWithFormat:format ,_curationsFeedItem.videos[0].imageServiceUrl, dim, dim]; - } - - return nil; +- (NSString *)getThumbnailUrlForItem:(BVCurationsFeedItem *)feedItem { + CGFloat screenScale = [[UIScreen mainScreen] scale]; + NSInteger dim = ceil(self.frame.size.height * screenScale); + NSString *format = @"%@&width=%ld&height=%ld&exact=true"; + + if (_curationsFeedItem.photos.count) { + return [NSString + stringWithFormat:format, _curationsFeedItem.photos[0].imageServiceUrl, + dim, dim]; + } else if (_curationsFeedItem.videos.count) { + return [NSString + stringWithFormat:format, _curationsFeedItem.videos[0].imageServiceUrl, + dim, dim]; + } + + return nil; } --(UIImage*)imageForChannel:(NSString*)channel { - return [UIImage bundledImageNamed:[NSString stringWithFormat:@"%@", channel]]; +- (UIImage *)imageForChannel:(NSString *)channel { + return [UIImage bundledImageNamed:[NSString stringWithFormat:@"%@", channel]]; } --(void)setShouldLoadKeypath:(NSString *)shouldLoadKeypath { - if (!_shouldLoadKeypath) { - _shouldLoadKeypath = shouldLoadKeypath; - [_shouldLoadObject addObserver:self forKeyPath:shouldLoadKeypath options:NSKeyValueObservingOptionNew context:nil]; - } +- (void)setShouldLoadKeypath:(NSString *)shouldLoadKeypath { + if (!_shouldLoadKeypath) { + _shouldLoadKeypath = shouldLoadKeypath; + [_shouldLoadObject addObserver:self + forKeyPath:shouldLoadKeypath + options:NSKeyValueObservingOptionNew + context:nil]; + } } --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (object == _shouldLoadObject) { - [self updateUI]; - } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (object == _shouldLoadObject) { + [self updateUI]; + } } @end diff --git a/Pod/BVExtensionNotifications/BVNotificationViewController.h b/Pod/BVExtensionNotifications/BVNotificationViewController.h index 5ea8ba86..95e0aa4e 100644 --- a/Pod/BVExtensionNotifications/BVNotificationViewController.h +++ b/Pod/BVExtensionNotifications/BVNotificationViewController.h @@ -11,6 +11,7 @@ #import "BVNotificationConstants.h" @protocol BVNotificationHandler; -@interface BVNotificationViewController : UIViewController +@interface BVNotificationViewController + : UIViewController @end diff --git a/Pod/BVExtensionNotifications/BVNotificationViewController.m b/Pod/BVExtensionNotifications/BVNotificationViewController.m index 58d16f52..aaba9e1a 100644 --- a/Pod/BVExtensionNotifications/BVNotificationViewController.m +++ b/Pod/BVExtensionNotifications/BVNotificationViewController.m @@ -5,58 +5,87 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. #import "BVNotificationViewController.h" -#import "BVNotificationsAnalyticsHelper.h" #import "BVAnalyticEventManager.h" +#import "BVNotificationsAnalyticsHelper.h" @interface BVNotificationViewController (Private) --(ProductType)getProductType; +- (ProductType)getProductType; @end @implementation BVNotificationViewController - (void)viewDidLoad { - [super viewDidLoad]; + [super viewDidLoad]; } - (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. } -- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion { - NSString *Id = response.notification.request.content.userInfo[USER_INFO_PROD_ID]; - - if ([[response actionIdentifier] isEqualToString:ID_REPLY] || - [response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]){ - - NSString *urlscheme = [NSString stringWithFormat:@"%@://www.bvsdk.com?type=review&subtype=%@&id=%@", response.notification.request.content.userInfo[USER_INFO_URL_SCHEME], ([self getProductType] == ProductTypeStore)? @"store" : @"product", Id]; - [self.extensionContext openURL:[NSURL URLWithString: urlscheme] completionHandler:nil]; - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REPLY withId:Id andProductType:[self getProductType]]; - - } else if ([[response actionIdentifier] isEqualToString:ID_REMIND]){ - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REMIND withId:Id andProductType:[self getProductType]]; - - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:response.notification.request withCompletionHandler:^(NSError *error) { - - }]; - - } else if ([[response actionIdentifier] isEqualToString:ID_DISMISS] || - [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]){ - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_DISMISS withId:Id andProductType:[self getProductType]]; - } - - completion(UNNotificationContentExtensionResponseOptionDismissAndForwardAction); +- (void) +didReceiveNotificationResponse:(UNNotificationResponse *)response + completionHandler: + (void (^)(UNNotificationContentExtensionResponseOption option)) + completion { + NSString *Id = + response.notification.request.content.userInfo[USER_INFO_PROD_ID]; + + if ([[response actionIdentifier] isEqualToString:ID_REPLY] || + [response.actionIdentifier + isEqualToString:UNNotificationDefaultActionIdentifier]) { + + NSString *urlscheme = [NSString + stringWithFormat:@"%@://www.bvsdk.com?type=review&subtype=%@&id=%@", + response.notification.request.content + .userInfo[USER_INFO_URL_SCHEME], + ([self getProductType] == ProductTypeStore) + ? @"store" + : @"product", + Id]; + [self.extensionContext openURL:[NSURL URLWithString:urlscheme] + completionHandler:nil]; + + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REPLY + withId:Id + andProductType:[self getProductType]]; + + } else if ([[response actionIdentifier] isEqualToString:ID_REMIND]) { + + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REMIND + withId:Id + andProductType:[self getProductType]]; + + [[UNUserNotificationCenter currentNotificationCenter] + addNotificationRequest:response.notification.request + withCompletionHandler:^(NSError *error){ + + }]; + + } else if ([[response actionIdentifier] isEqualToString:ID_DISMISS] || + [response.actionIdentifier + isEqualToString:UNNotificationDismissActionIdentifier]) { + + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_DISMISS + withId:Id + andProductType:[self getProductType]]; + } + + completion( + UNNotificationContentExtensionResponseOptionDismissAndForwardAction); } -- (void)didReceiveNotification:(UNNotification *)notification{ - [[BVAnalyticEventManager sharedManager] setClientId:notification.request.content.userInfo[USER_INFO_CLIENTID]]; +- (void)didReceiveNotification:(UNNotification *)notification { + [[BVAnalyticEventManager sharedManager] + setClientId:notification.request.content.userInfo[USER_INFO_CLIENTID]]; } --(void)launchAppWithType:(NSString*)type subtype:(NSString*)subtype ID:(NSString*)ID { - +- (void)launchAppWithType:(NSString *)type + subtype:(NSString *)subtype + ID:(NSString *)ID { } @end diff --git a/Pod/BVExtensionNotifications/BVNotifications.h b/Pod/BVExtensionNotifications/BVNotifications.h index 028e5996..3a04a6e7 100644 --- a/Pod/BVExtensionNotifications/BVNotifications.h +++ b/Pod/BVExtensionNotifications/BVNotifications.h @@ -10,18 +10,18 @@ #define BVNotifications_h // BVCommon -#import "BVSDKConstants.h" -#import "BVLogger.h" #import "BVBaseAnalyticsHelper.h" +#import "BVLogger.h" +#import "BVSDKConstants.h" // BVAnalytics #import "BVAnalyticsManager.h" -//BVNotifications -#import "BVProductReviewNotificationProperties.h" -#import "BVStoreReviewNotificationProperties.h" +// BVNotifications #import "BVNotificationProperties.h" #import "BVNotificationsAnalyticsHelper.h" +#import "BVProductReviewNotificationProperties.h" +#import "BVStoreReviewNotificationProperties.h" // BVExtensionNotifiations #import "BVNotificationViewController.h" diff --git a/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.h b/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.h index bdb2c414..80fbb12b 100644 --- a/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.h +++ b/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.h @@ -6,9 +6,10 @@ // // -#import #import "BVNotificationViewController.h" +#import -@interface BVProductReviewNotificationViewController : BVNotificationViewController +@interface BVProductReviewNotificationViewController + : BVNotificationViewController @end diff --git a/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.m b/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.m index b0aec10c..5ba62351 100644 --- a/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.m +++ b/Pod/BVExtensionNotifications/BVProductReviewNotificationViewController.m @@ -9,56 +9,127 @@ #import "BVProductReviewNotificationViewController.h" #import "BVNotificationsAnalyticsHelper.h" -@interface BVProductReviewNotificationViewController() +@interface BVProductReviewNotificationViewController () -@property (nonatomic, strong) UIImageView *productImageView; -@property (nonatomic, strong) UILabel *productNameLbl; +@property(nonatomic, strong) UIImageView *productImageView; +@property(nonatomic, strong) UILabel *productNameLbl; @end @implementation BVProductReviewNotificationViewController --(void)viewDidLoad { - [super viewDidLoad]; - _productNameLbl = [[UILabel alloc] init]; - _productNameLbl.translatesAutoresizingMaskIntoConstraints = NO; - _productNameLbl.font = [UIFont systemFontOfSize:18.0]; - _productNameLbl.textAlignment = NSTextAlignmentCenter; - [self.view addSubview:_productNameLbl]; - - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productNameLbl attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTopMargin multiplier:1.0f constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productNameLbl attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeftMargin multiplier:1 constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productNameLbl attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRightMargin multiplier:1 constant:0]]; - [_productNameLbl addConstraint:[NSLayoutConstraint constraintWithItem:_productNameLbl attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:25]]; - [_productNameLbl setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - - _productImageView = [[UIImageView alloc]init]; - _productImageView.translatesAutoresizingMaskIntoConstraints = NO; - _productImageView.contentMode = UIViewContentModeScaleAspectFit; - [self.view addSubview:_productImageView]; - - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productNameLbl attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_productImageView attribute:NSLayoutAttributeTop multiplier:1 constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0]]; - [_productImageView addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:175]]; +- (void)viewDidLoad { + [super viewDidLoad]; + _productNameLbl = [[UILabel alloc] init]; + _productNameLbl.translatesAutoresizingMaskIntoConstraints = NO; + _productNameLbl.font = [UIFont systemFontOfSize:18.0]; + _productNameLbl.textAlignment = NSTextAlignmentCenter; + [self.view addSubview:_productNameLbl]; + + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTopMargin + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeftMargin + multiplier:1 + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeRightMargin + multiplier:1 + constant:0]]; + [_productNameLbl + addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0f + constant:25]]; + [_productNameLbl + setContentCompressionResistancePriority:UILayoutPriorityRequired + forAxis:UILayoutConstraintAxisVertical]; + + _productImageView = [[UIImageView alloc] init]; + _productImageView.translatesAutoresizingMaskIntoConstraints = NO; + _productImageView.contentMode = UIViewContentModeScaleAspectFit; + [self.view addSubview:_productImageView]; + + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productNameLbl + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:_productImageView + attribute:NSLayoutAttributeTop + multiplier:1 + constant:0]]; + [self.view + addConstraint:[NSLayoutConstraint constraintWithItem:_productImageView + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeft + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeWidth + multiplier:1 + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeBottom + multiplier:1 + constant:0]]; + [_productImageView + addConstraint:[NSLayoutConstraint + constraintWithItem:_productImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0f + constant:175]]; } --(void)didReceiveNotification:(UNNotification *)notification { - [super didReceiveNotification:notification]; +- (void)didReceiveNotification:(UNNotification *)notification { + [super didReceiveNotification:notification]; - _productNameLbl.text = notification.request.content.userInfo[USER_INFO_PROD_NAME]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSURL *url = [NSURL URLWithString:notification.request.content.userInfo[USER_INFO_PROD_IMAGE_URL]]; - NSData *data = [NSData dataWithContentsOfURL:url]; - dispatch_async(dispatch_get_main_queue(), ^{ - _productImageView.image = [UIImage imageWithData:data]; - }); - }); + _productNameLbl.text = + notification.request.content.userInfo[USER_INFO_PROD_NAME]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + NSURL *url = [NSURL + URLWithString:notification.request.content + .userInfo[USER_INFO_PROD_IMAGE_URL]]; + NSData *data = [NSData dataWithContentsOfURL:url]; + dispatch_async(dispatch_get_main_queue(), ^{ + _productImageView.image = [UIImage imageWithData:data]; + }); + }); } -- (ProductType)getProductType{ - return ProductTypeProduct; +- (ProductType)getProductType { + return ProductTypeProduct; } @end diff --git a/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.h b/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.h index dc8ff176..e71b6a64 100644 --- a/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.h +++ b/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.h @@ -6,8 +6,9 @@ // // -#import #import "BVNotificationViewController.h" -@interface BVStoreReviewNotificationViewController : BVNotificationViewController +#import +@interface BVStoreReviewNotificationViewController + : BVNotificationViewController @end diff --git a/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.m b/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.m index 27fe7bfb..6fc8b5e6 100644 --- a/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.m +++ b/Pod/BVExtensionNotifications/BVStoreReviewNotificationViewController.m @@ -7,59 +7,100 @@ // // - #import "BVStoreReviewNotificationViewController.h" -#import #import "BVNotificationsAnalyticsHelper.h" #import "BVStoreReviewNotificationProperties.h" +#import @interface BVStoreReviewNotificationViewController () -@property (nonatomic, strong) MKMapView *mapView; -@property (nonatomic, strong) UIView *constrainingView; -@property (nonatomic, strong) BVStoreReviewNotificationProperties *notificationProps; +@property(nonatomic, strong) MKMapView *mapView; +@property(nonatomic, strong) UIView *constrainingView; +@property(nonatomic, strong) + BVStoreReviewNotificationProperties *notificationProps; @end @implementation BVStoreReviewNotificationViewController - - (void)viewDidLoad { - [super viewDidLoad]; - _constrainingView = [[UIView alloc]initWithFrame:self.view.bounds]; - _constrainingView.translatesAutoresizingMaskIntoConstraints = NO; - [self.view addSubview:_constrainingView]; - [_constrainingView setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0f constant:0]]; - [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0]]; - NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:_constrainingView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:215]; - [_constrainingView addConstraint:height]; + [super viewDidLoad]; + _constrainingView = [[UIView alloc] initWithFrame:self.view.bounds]; + _constrainingView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:_constrainingView]; + [_constrainingView setContentHuggingPriority:UILayoutPriorityRequired + forAxis:UILayoutConstraintAxisVertical]; + [self.view + addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeTop + multiplier:1.0f + constant:0]]; + [self.view + addConstraint:[NSLayoutConstraint constraintWithItem:_constrainingView + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeLeft + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_constrainingView + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeRight + multiplier:1.0f + constant:0]]; + [self.view addConstraint:[NSLayoutConstraint + constraintWithItem:_constrainingView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self.view + attribute:NSLayoutAttributeBottom + multiplier:1.0f + constant:0]]; + NSLayoutConstraint *height = + [NSLayoutConstraint constraintWithItem:_constrainingView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0f + constant:215]; + [_constrainingView addConstraint:height]; } - (void)didReceiveNotification:(UNNotification *)notification { - [super didReceiveNotification:notification]; - _notificationProps = [[BVStoreReviewNotificationProperties alloc] initWithDictionary:notification.request.content.userInfo[USER_INFO_CONFIG_PROPS]]; - MKCoordinateRegion region; - region.center.latitude = [notification.request.content.userInfo[USER_INFO_LAT] floatValue]; - region.center.longitude = [notification.request.content.userInfo[USER_INFO_LONG] floatValue]; - region.span.latitudeDelta = [notification.request.content.userInfo[USER_INFO_LAT_DELTA] floatValue]; - region.span.latitudeDelta = [notification.request.content.userInfo[USER_INFO_LONG_DELTA] floatValue]; - - _mapView.region = region; - MKPointAnnotation *ptAnn = [[MKPointAnnotation alloc]init]; - ptAnn.coordinate = region.center; - [_mapView addAnnotation: ptAnn]; + [super didReceiveNotification:notification]; + _notificationProps = [[BVStoreReviewNotificationProperties alloc] + initWithDictionary:notification.request.content + .userInfo[USER_INFO_CONFIG_PROPS]]; + MKCoordinateRegion region; + region.center.latitude = + [notification.request.content.userInfo[USER_INFO_LAT] floatValue]; + region.center.longitude = + [notification.request.content.userInfo[USER_INFO_LONG] floatValue]; + region.span.latitudeDelta = + [notification.request.content.userInfo[USER_INFO_LAT_DELTA] floatValue]; + region.span.latitudeDelta = + [notification.request.content.userInfo[USER_INFO_LONG_DELTA] floatValue]; + + _mapView.region = region; + MKPointAnnotation *ptAnn = [[MKPointAnnotation alloc] init]; + ptAnn.coordinate = region.center; + [_mapView addAnnotation:ptAnn]; } --(void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - _mapView = [[MKMapView alloc]initWithFrame:_constrainingView.frame]; - [self.view addSubview:_mapView]; - _mapView.showsPointsOfInterest = YES; +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + _mapView = [[MKMapView alloc] initWithFrame:_constrainingView.frame]; + [self.view addSubview:_mapView]; + _mapView.showsPointsOfInterest = YES; } -- (ProductType)getProductType{ - return ProductTypeStore; +- (ProductType)getProductType { + return ProductTypeStore; } @end diff --git a/Pod/BVLocation/BVLocation.h b/Pod/BVLocation/BVLocation.h index 7aa2fe78..dc4fe9eb 100644 --- a/Pod/BVLocation/BVLocation.h +++ b/Pod/BVLocation/BVLocation.h @@ -5,7 +5,6 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #ifndef BVLocation_h #define BVLocation_h diff --git a/Pod/BVLocation/BVLocationAnalyticsHelper.m b/Pod/BVLocation/BVLocationAnalyticsHelper.m index 4914dc09..7073a035 100644 --- a/Pod/BVLocation/BVLocationAnalyticsHelper.m +++ b/Pod/BVLocation/BVLocationAnalyticsHelper.m @@ -12,20 +12,23 @@ @implementation BVLocationAnalyticsHelper + (void)queueAnalyticsEventForGimbalVisit:(GMBLVisit *)visit { - NSMutableDictionary* event = [NSMutableDictionary dictionary]; - [event setValue:@"Visit" forKey:@"type"]; - [event setValue:@"Location" forKey:@"cl"]; - [event setValue:@"native-mobile-sdk" forKey:@"source"]; - [event setValue:[visit.place.attributes stringForKey:@"id"] forKey:@"locationId"]; - - if (visit.departureDate) { - [event setValue:@"Exit" forKey:@"transition"]; - [event setValue:@([visit.departureDate timeIntervalSinceDate:visit.arrivalDate]) forKey:@"durationSecs"]; - }else { - [event setValue:@"Entry" forKey:@"transition"]; - } - - [[BVAnalyticsManager sharedManager] queueEvent:event]; + NSMutableDictionary *event = [NSMutableDictionary dictionary]; + [event setValue:@"Visit" forKey:@"type"]; + [event setValue:@"Location" forKey:@"cl"]; + [event setValue:@"native-mobile-sdk" forKey:@"source"]; + [event setValue:[visit.place.attributes stringForKey:@"id"] + forKey:@"locationId"]; + + if (visit.departureDate) { + [event setValue:@"Exit" forKey:@"transition"]; + [event setValue:@([visit.departureDate + timeIntervalSinceDate:visit.arrivalDate]) + forKey:@"durationSecs"]; + } else { + [event setValue:@"Entry" forKey:@"transition"]; + } + + [[BVAnalyticsManager sharedManager] queueEvent:event]; } @end diff --git a/Pod/BVLocation/BVLocationManager.h b/Pod/BVLocation/BVLocationManager.h index 46ffb906..019cffdf 100644 --- a/Pod/BVLocation/BVLocationManager.h +++ b/Pod/BVLocation/BVLocationManager.h @@ -5,7 +5,6 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #import @protocol BVLocationManagerDelegate; @@ -17,35 +16,39 @@ Register a delegate to BVLocationManagerDelegate callbacks Can register multiple delegates to receive callbacks */ -+(void)registerForLocationUpdates:(id _Nonnull)delegate; ++ (void)registerForLocationUpdates: + (nonnull id)delegate; /** Delegate will be unregistered so it will stop receiving callbacks */ -+(void)unregisterForLocationUpdates:(id _Nonnull)delegate; ++ (void)unregisterForLocationUpdates: + (nonnull id)delegate; /** Start updating location; All registered delegates will receive callbacks */ -+(void)startLocationUpdates; ++ (void)startLocationUpdates; /** - Stop updating location; All registered delegates will stop receiving callbacks + Stop updating location; All registered delegates will stop receiving + callbacks */ -+(void)stopLocationUpdates; ++ (void)stopLocationUpdates; @end - @class BVVisit; @protocol BVLocationManagerDelegate -/// All registered delegates will receive this callback whenever a user enters a store belonging to the client of the SDK. +/// All registered delegates will receive this callback whenever a user enters a +/// store belonging to the client of the SDK. @optional --(void)didBeginVisit:(BVVisit* _Nonnull)visit; +- (void)didBeginVisit:(nonnull BVVisit *)visit; -/// All registered delegates will receive this callback whenever a user exits a store belonging to the client of the SDK. +/// All registered delegates will receive this callback whenever a user exits a +/// store belonging to the client of the SDK. @optional --(void)didEndVisit:(BVVisit* _Nonnull)visit; +- (void)didEndVisit:(nonnull BVVisit *)visit; @end diff --git a/Pod/BVLocation/BVLocationManager.m b/Pod/BVLocation/BVLocationManager.m index b51e9883..80eae5fa 100644 --- a/Pod/BVLocation/BVLocationManager.m +++ b/Pod/BVLocation/BVLocationManager.m @@ -6,254 +6,255 @@ // #import "BVLocationManager.h" +#import "BVLocationAnalyticsHelper.h" #import "BVLogger.h" -#import "BVSDKManager.h" -#import -#import "BVVisit.h" #import "BVPlaceAttributes.h" -#import "BVLocationAnalyticsHelper.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVVisit.h" +#import @interface DelegateContainer : NSObject -@property (nonatomic, weak) id delegate; +@property(nonatomic, weak) id delegate; @end @implementation DelegateContainer @end -@interface BVLocationManager() +@interface BVLocationManager () -@property (nonatomic, strong) GMBLPlaceManager *placeManager; -@property (nonatomic, strong) NSString *apiKey; -@property (nonatomic, strong) NSMutableArray *registeredDelegates; +@property(nonatomic, strong) GMBLPlaceManager *placeManager; +@property(nonatomic, strong) NSString *apiKey; +@property(nonatomic, strong) NSMutableArray *registeredDelegates; @end @implementation BVLocationManager -+(void)load{ - [self sharedManager]; ++ (void)load { + [self sharedManager]; } -+(id)sharedManager { - static BVLocationManager *shared; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - shared = [[self alloc]init]; - }); - - return shared; ++ (id)sharedManager { + static BVLocationManager *shared; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + shared = [[self alloc] init]; + }); + + return shared; } - (id)init { - if (self = [super init]) { - _registeredDelegates = [NSMutableArray new]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(receiveLocationAPIKey:) - name:LOCATION_API_KEY_SET_NOTIFICATION - object:nil]; + if (self = [super init]) { + _registeredDelegates = [NSMutableArray new]; - } - - return self; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveLocationAPIKey:) + name:LOCATION_API_KEY_SET_NOTIFICATION + object:nil]; + } + + return self; } - (void)initGimbal { - if ([self.class isValidUUID:_apiKey]) { - [[BVLogger sharedLogger] verbose:@"Initializing Location Manager"]; - - [Gimbal setAPIKey:_apiKey options:nil]; - _placeManager = [[GMBLPlaceManager alloc]init]; - _placeManager.delegate = (id)self; - }else { - NSAssert(NO, @"You must provide a valid Location API Key before using BVLocation"); - } -} + if ([self.class isValidUUID:_apiKey]) { + [[BVLogger sharedLogger] verbose:@"Initializing Location Manager"]; -- (void) receiveLocationAPIKey:(NSNotification *) notification -{ - // [notification name] should always be LOCATION_API_KEY_SET_NOTIFICATION - // unless you use this method for observation of other notifications - // as well. - - if ([[notification name] isEqualToString:LOCATION_API_KEY_SET_NOTIFICATION]){ - - [[BVLogger sharedLogger] verbose:@"Recieved notifcation for location configuration"]; - - _apiKey = [notification.userInfo valueForKeyPath:LOCATION_API_KEY_SET_NOTIFICATION]; - [self initGimbal]; - - } + [Gimbal setAPIKey:_apiKey options:nil]; + _placeManager = [[GMBLPlaceManager alloc] init]; + _placeManager.delegate = (id)self; + } else { + NSAssert(NO, @"You must provide a valid Location API Key before using " + @"BVLocation"); + } } -+ (BOOL)isValidUUID:(NSString *)UUIDString -{ - NSUUID* UUID = [[NSUUID alloc] initWithUUIDString:UUIDString]; - if(UUID) - return true; - else - return false; +- (void)receiveLocationAPIKey:(NSNotification *)notification { + // [notification name] should always be LOCATION_API_KEY_SET_NOTIFICATION + // unless you use this method for observation of other notifications + // as well. + + if ([[notification name] isEqualToString:LOCATION_API_KEY_SET_NOTIFICATION]) { + [[BVLogger sharedLogger] + verbose:@"Recieved notifcation for location configuration"]; + + _apiKey = [notification.userInfo + valueForKeyPath:LOCATION_API_KEY_SET_NOTIFICATION]; + [self initGimbal]; + } } ++ (BOOL)isValidUUID:(NSString *)UUIDString { + NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:UUIDString]; + if (UUID) + return true; + else + return false; +} ++ (void)startLocationUpdates { + if ([CLLocationManager authorizationStatus] == + kCLAuthorizationStatusAuthorizedAlways) { + NSAssert([[self sharedManager] apiKey], + @"Unable to start updating location. BVLocation API Key not set"); + [GMBLPlaceManager startMonitoring]; + [GMBLCommunicationManager startReceivingCommunications]; + [[BVLogger sharedLogger] + verbose:@"Successfully started updating location."]; -+ (void)startLocationUpdates{ - if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways){ - - NSAssert([[self sharedManager] apiKey], @"Unable to start updating location. BVLocation API Key not set"); - [GMBLPlaceManager startMonitoring]; - [GMBLCommunicationManager startReceivingCommunications]; - [[BVLogger sharedLogger] verbose:@"Successfully started updating location."]; - - }else{ - [[BVLogger sharedLogger] verbose:@"Unable to start updating location. Insufficent permission"]; - } + } else { + [[BVLogger sharedLogger] + verbose:@"Unable to start updating location. Insufficent permission"]; + } } + (void)stopLocationUpdates { - [GMBLPlaceManager stopMonitoring]; - [GMBLCommunicationManager stopReceivingCommunications]; + [GMBLPlaceManager stopMonitoring]; + [GMBLCommunicationManager stopReceivingCommunications]; } #pragma mark GMBLPlaceManagerDelegate -- (void)placeManager:(GMBLPlaceManager *)manager didBeginVisit:(GMBLVisit *)visit { - - NSDictionary *attributeDictionary = [self gimbalVisitToDictionary:visit]; - - if ([self gimbalPlaceIsValid:visit.place]) { - [BVLocationAnalyticsHelper queueAnalyticsEventForGimbalVisit:visit]; - } - - [self callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributeDictionary]; +- (void)placeManager:(GMBLPlaceManager *)manager + didBeginVisit:(GMBLVisit *)visit { + NSDictionary *attributeDictionary = [self gimbalVisitToDictionary:visit]; + + if ([self gimbalPlaceIsValid:visit.place]) { + [BVLocationAnalyticsHelper queueAnalyticsEventForGimbalVisit:visit]; + } + + [self callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributeDictionary]; } -- (void)placeManager:(GMBLPlaceManager *)manager didEndVisit:(GMBLVisit *)visit { - - NSDictionary *attributeDictionary = [self gimbalVisitToDictionary:visit]; - - if ([self gimbalPlaceIsValid:visit.place]) { - [BVLocationAnalyticsHelper queueAnalyticsEventForGimbalVisit:visit]; - } - - [self callbackToDelegates:@selector(didEndVisit:) withAttributes:attributeDictionary]; +- (void)placeManager:(GMBLPlaceManager *)manager + didEndVisit:(GMBLVisit *)visit { + NSDictionary *attributeDictionary = [self gimbalVisitToDictionary:visit]; + + if ([self gimbalPlaceIsValid:visit.place]) { + [BVLocationAnalyticsHelper queueAnalyticsEventForGimbalVisit:visit]; + } + + [self callbackToDelegates:@selector(didEndVisit:) + withAttributes:attributeDictionary]; } --(BOOL)gimbalPlaceIsValid:(GMBLPlace*)place { - return [place.attributes stringForKey:@"id"] != nil; +- (BOOL)gimbalPlaceIsValid:(GMBLPlace *)place { + return [place.attributes stringForKey:@"id"] != nil; } --(NSDictionary*)gimbalVisitToDictionary:(GMBLVisit *)visit { - - GMBLAttributes *attributes = visit.place.attributes; - - NSMutableDictionary *attributeDictionary = [NSMutableDictionary new]; - - for (NSString *key in attributes.allKeys) { - [attributeDictionary setObject:[attributes stringForKey:key] forKey:key]; - } - - if (visit.arrivalDate){ - [attributeDictionary setObject:visit.arrivalDate forKey:ARRIVAL_DATE]; - } - - if (visit.departureDate){ - [attributeDictionary setObject:visit.departureDate forKey:DEPARTURE_DATE]; - } - - return [NSDictionary dictionaryWithDictionary:attributeDictionary]; +- (NSDictionary *)gimbalVisitToDictionary:(GMBLVisit *)visit { + GMBLAttributes *attributes = visit.place.attributes; + + NSMutableDictionary *attributeDictionary = [NSMutableDictionary new]; + + for (NSString *key in attributes.allKeys) { + [attributeDictionary setObject:[attributes stringForKey:key] forKey:key]; + } + + if (visit.arrivalDate) { + [attributeDictionary setObject:visit.arrivalDate forKey:ARRIVAL_DATE]; + } + + if (visit.departureDate) { + [attributeDictionary setObject:visit.departureDate forKey:DEPARTURE_DATE]; + } + + return [NSDictionary dictionaryWithDictionary:attributeDictionary]; } -- (void)callbackToDelegates:(SEL)selector withAttributes:(NSDictionary *)attributes{ - - if(!_registeredDelegates.count) { - return; - } - - NSString *type = [attributes objectForKey:PLACE_TYPE_KEY]; - - if ([BVPlaceAttributes typeFromString:type] != PlaceTypeGeofence) { - return; - } - - NSString *clientId = [attributes objectForKey:PLACE_CLIENT_ID]; - - if (![clientId isEqualToString:[BVSDKManager sharedManager].configuration.clientId]) { - return; - } - - - NSArray *delegates = [NSArray arrayWithArray:_registeredDelegates]; - for ( DelegateContainer *container in delegates){ - - if (container.delegate) { - - BVVisit *visit = [self attibutesToBVVisit:attributes]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"Visit Recorded: %@", visit.description]]; - - if ([container.delegate respondsToSelector:selector]) { +- (void)callbackToDelegates:(SEL)selector + withAttributes:(NSDictionary *)attributes { + if (!_registeredDelegates.count) { + return; + } + + NSString *type = [attributes objectForKey:PLACE_TYPE_KEY]; + + if ([BVPlaceAttributes typeFromString:type] != PlaceTypeGeofence) { + return; + } + + NSString *clientId = [attributes objectForKey:PLACE_CLIENT_ID]; + + if (![clientId isEqualToString:[BVSDKManager sharedManager] + .configuration.clientId]) { + return; + } + + NSArray *delegates = [NSArray arrayWithArray:_registeredDelegates]; + for (DelegateContainer *container in delegates) { + if (container.delegate) { + BVVisit *visit = [self attibutesToBVVisit:attributes]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"Visit Recorded: %@", + visit.description]]; + + if ([container.delegate respondsToSelector:selector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [container.delegate performSelector:selector withObject:visit]; + [container.delegate performSelector:selector withObject:visit]; #pragma clang diagnostic pop - } - }else{ - [_registeredDelegates removeObject:container]; - } + } + } else { + [_registeredDelegates removeObject:container]; } + } } --(BVVisit*)attibutesToBVVisit:(NSDictionary*)attributes { - NSString *name = [attributes objectForKey:PLACE_NAME]; - NSString *address = [attributes objectForKey:PLACE_ADDRESS]; - NSString *city = [attributes objectForKey:PLACE_CITY]; - NSString *state = [attributes objectForKey:PLACE_STATE]; - NSString *zipCode = [attributes objectForKey:PLACE_ZIP]; - NSString *storeId = [attributes objectForKey:PLACE_STORE_ID]; - NSDate *arrivalDate = [attributes objectForKey:ARRIVAL_DATE]; - NSDate *departureDate = [attributes objectForKey:DEPARTURE_DATE]; - - return [[BVVisit alloc] initWithName:name - address:address - city:city - state:state - zipCode:zipCode - storeId:storeId - arrivalDate:arrivalDate - departureDate:departureDate]; - +- (BVVisit *)attibutesToBVVisit:(NSDictionary *)attributes { + NSString *name = [attributes objectForKey:PLACE_NAME]; + NSString *address = [attributes objectForKey:PLACE_ADDRESS]; + NSString *city = [attributes objectForKey:PLACE_CITY]; + NSString *state = [attributes objectForKey:PLACE_STATE]; + NSString *zipCode = [attributes objectForKey:PLACE_ZIP]; + NSString *storeId = [attributes objectForKey:PLACE_STORE_ID]; + NSDate *arrivalDate = [attributes objectForKey:ARRIVAL_DATE]; + NSDate *departureDate = [attributes objectForKey:DEPARTURE_DATE]; + + return [[BVVisit alloc] initWithName:name + address:address + city:city + state:state + zipCode:zipCode + storeId:storeId + arrivalDate:arrivalDate + departureDate:departureDate]; } + (void)registerForLocationUpdates:(id)delegate { - - if (!delegate){ - return; - } - - if (![self getContainerForDelegate:delegate]) { - DelegateContainer *container = [[DelegateContainer alloc]init]; - container.delegate = delegate; - [[[self sharedManager] registeredDelegates] addObject:container]; - } + if (!delegate) { + return; + } + + if (![self getContainerForDelegate:delegate]) { + DelegateContainer *container = [[DelegateContainer alloc] init]; + container.delegate = delegate; + [[[self sharedManager] registeredDelegates] addObject:container]; + } } + (void)unregisterForLocationUpdates:(id)delegate { - DelegateContainer *container = [self getContainerForDelegate:delegate]; - if (container) { - [[[self sharedManager] registeredDelegates] removeObject:container]; - } + DelegateContainer *container = [self getContainerForDelegate:delegate]; + if (container) { + [[[self sharedManager] registeredDelegates] removeObject:container]; + } } -+(DelegateContainer *)getContainerForDelegate:(id)delegate { - for ( DelegateContainer *container in [[self sharedManager] registeredDelegates] ){ - if (container.delegate == delegate) { - return container; - } ++ (DelegateContainer *)getContainerForDelegate: + (id)delegate { + for (DelegateContainer *container in + [[self sharedManager] registeredDelegates]) { + if (container.delegate == delegate) { + return container; } - - return nil; + } + + return nil; } @end diff --git a/Pod/BVLocation/BVPlaceAttributes.h b/Pod/BVLocation/BVPlaceAttributes.h index 75dfc1d3..23ae51ac 100644 --- a/Pod/BVLocation/BVPlaceAttributes.h +++ b/Pod/BVLocation/BVPlaceAttributes.h @@ -5,29 +5,23 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #import +#define PLACE_TYPE_KEY @"type" +#define PLACE_CLIENT_ID @"clientId" +#define PLACE_NAME @"name" +#define PLACE_CITY @"city" +#define PLACE_STATE @"state" +#define PLACE_ZIP @"zip" +#define PLACE_STORE_ID @"storeId" +#define PLACE_ADDRESS @"address" +#define ARRIVAL_DATE @"arrivalDate" +#define DEPARTURE_DATE @"departureDate" -#define PLACE_TYPE_KEY @"type" -#define PLACE_CLIENT_ID @"clientId" -#define PLACE_NAME @"name" -#define PLACE_CITY @"city" -#define PLACE_STATE @"state" -#define PLACE_ZIP @"zip" -#define PLACE_STORE_ID @"storeId" -#define PLACE_ADDRESS @"address" -#define ARRIVAL_DATE @"arrivalDate" -#define DEPARTURE_DATE @"departureDate" - -typedef enum{ - PlaceTypeGeofence, - PlaceTypeBeacon, - PlaceTypeUnkown -}PlaceType; +typedef enum { PlaceTypeGeofence, PlaceTypeBeacon, PlaceTypeUnkown } PlaceType; @interface BVPlaceAttributes : NSObject -+ (PlaceType)typeFromString:(NSString*)typeString; ++ (PlaceType)typeFromString:(NSString *)typeString; @end diff --git a/Pod/BVLocation/BVPlaceAttributes.m b/Pod/BVLocation/BVPlaceAttributes.m index 3ad7c900..2a55bc6e 100644 --- a/Pod/BVLocation/BVPlaceAttributes.m +++ b/Pod/BVLocation/BVPlaceAttributes.m @@ -5,20 +5,18 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #import "BVPlaceAttributes.h" @implementation BVPlaceAttributes -+ (PlaceType)typeFromString:(NSString*)typeString{ - - if ([typeString isEqualToString: @"geofence"]){ - return PlaceTypeGeofence; - }else if ([typeString isEqualToString: @"beacon"]){ - return PlaceTypeBeacon; - }else{ - return PlaceTypeUnkown; - } ++ (PlaceType)typeFromString:(NSString *)typeString { + if ([typeString isEqualToString:@"geofence"]) { + return PlaceTypeGeofence; + } else if ([typeString isEqualToString:@"beacon"]) { + return PlaceTypeBeacon; + } else { + return PlaceTypeUnkown; + } } @end diff --git a/Pod/BVLocation/BVVisit.h b/Pod/BVLocation/BVVisit.h index 2b825811..6760a552 100644 --- a/Pod/BVLocation/BVVisit.h +++ b/Pod/BVLocation/BVVisit.h @@ -8,22 +8,22 @@ @interface BVVisit : NSObject --(id)initWithName:(NSString*)name - address:(NSString*)address - city:(NSString*)city - state:(NSString*)state - zipCode:(NSString*)zipCode - storeId:(NSString*)storeId - arrivalDate:(NSDate *)arrivalDate - departureDate:(NSDate *)departureDate; +- (id)initWithName:(NSString *)name + address:(NSString *)address + city:(NSString *)city + state:(NSString *)state + zipCode:(NSString *)zipCode + storeId:(NSString *)storeId + arrivalDate:(NSDate *)arrivalDate + departureDate:(NSDate *)departureDate; -@property (nonatomic, strong, readonly) NSString *name; -@property (nonatomic, strong, readonly) NSString *address; -@property (nonatomic, strong, readonly) NSString *city; -@property (nonatomic, strong, readonly) NSString *state; -@property (nonatomic, strong, readonly) NSString *zipCode; -@property (nonatomic, strong, readonly) NSString *storeId; -@property (nonatomic, strong, readonly) NSDate *arrivalDate; -@property (nonatomic, strong, readonly) NSDate *departureDate; +@property(nonatomic, strong, readonly) NSString *name; +@property(nonatomic, strong, readonly) NSString *address; +@property(nonatomic, strong, readonly) NSString *city; +@property(nonatomic, strong, readonly) NSString *state; +@property(nonatomic, strong, readonly) NSString *zipCode; +@property(nonatomic, strong, readonly) NSString *storeId; +@property(nonatomic, strong, readonly) NSDate *arrivalDate; +@property(nonatomic, strong, readonly) NSDate *departureDate; @end diff --git a/Pod/BVLocation/BVVisit.m b/Pod/BVLocation/BVVisit.m index e24ef381..4ec85dfc 100644 --- a/Pod/BVLocation/BVVisit.m +++ b/Pod/BVLocation/BVVisit.m @@ -9,31 +9,34 @@ @implementation BVVisit --(id)initWithName:(NSString*)name - address:(NSString*)address - city:(NSString*)city - state:(NSString*)state - zipCode:(NSString*)zipCode - storeId:(NSString*)storeId - arrivalDate:(NSDate *)arrivalDate - departureDate:(NSDate *)departureDate{ - self = [super init]; - - if(self) { - _name = name; - _address = address; - _city = city; - _state = state; - _zipCode = zipCode; - _storeId = storeId; - _arrivalDate = arrivalDate; - _departureDate = departureDate; - } - - return self; +- (id)initWithName:(NSString *)name + address:(NSString *)address + city:(NSString *)city + state:(NSString *)state + zipCode:(NSString *)zipCode + storeId:(NSString *)storeId + arrivalDate:(NSDate *)arrivalDate + departureDate:(NSDate *)departureDate { + self = [super init]; + + if (self) { + _name = name; + _address = address; + _city = city; + _state = state; + _zipCode = zipCode; + _storeId = storeId; + _arrivalDate = arrivalDate; + _departureDate = departureDate; + } + + return self; } -- (NSString *)description{ - return [NSString stringWithFormat:@"BVLocation:\nName: %@\nStore Id: %@\nAddress: %@\nCity:%@\nState:%@\nZip:%@", _name, _storeId,_address, _city, _state, _zipCode]; +- (NSString *)description { + return [NSString + stringWithFormat:@"BVLocation:\nName: %@\nStore Id: %@\nAddress: " + @"%@\nCity:%@\nState:%@\nZip:%@", + _name, _storeId, _address, _city, _state, _zipCode]; } @end diff --git a/Pod/BVNotifications/BVNotificationCenterObject.h b/Pod/BVNotifications/BVNotificationCenterObject.h index c7f8f6ef..c62226f3 100644 --- a/Pod/BVNotifications/BVNotificationCenterObject.h +++ b/Pod/BVNotifications/BVNotificationCenterObject.h @@ -5,29 +5,35 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // +#define ATTACHMENT_FILE_DIRECTORY \ + [NSTemporaryDirectory() stringByAppendingString:@"BVSDK"] +#define ATTACHMENT_FILE_PATH \ + [ATTACHMENT_FILE_DIRECTORY stringByAppendingString:@"/temp.png"] +#define ATTACHMENT_THUMB_ID @"attachmentThumb" +#define ATTACHMENT_LL_ID @"attachmentLatLong" +#define STORE_NOTIFICATION_CUSTOM_CATEGORY \ + [BVSDKManager sharedManager].configuration.storeReviewContentExtensionCategory +#define PIN_CUSTOM_CATEGORY \ + [BVSDKManager sharedManager].configuration.PINContentExtensionCategory -#define ATTACHMENT_FILE_DIRECTORY [NSTemporaryDirectory() stringByAppendingString:@"BVSDK"] -#define ATTACHMENT_FILE_PATH [ATTACHMENT_FILE_DIRECTORY stringByAppendingString:@"/temp.png"] -#define ATTACHMENT_THUMB_ID @"attachmentThumb" -#define ATTACHMENT_LL_ID @"attachmentLatLong" -#define STORE_NOTIFICATION_CUSTOM_CATEGORY [BVSDKManager sharedManager].configuration.storeReviewContentExtensionCategory -#define PIN_CUSTOM_CATEGORY [BVSDKManager sharedManager].configuration.PINContentExtensionCategory - -#import #import +#import -#import "BVStoreReviewNotificationProperties.h" -#import "BVStore.h" #import "BVPIN.h" +#import "BVStore.h" +#import "BVStoreReviewNotificationProperties.h" @protocol BVNotificationCenterObject @optional -- (void)addNotificationCategories:(BVNotificationProperties * _Nonnull)notificationProperties; +- (void)addNotificationCategories: + (nonnull BVNotificationProperties *)notificationProperties; -- (void)userNotificationCenter:(UNUserNotificationCenter* _Nonnull)center didReceiveNotificationResponse:(UNNotificationResponse * _Nonnull)response; +- (void)userNotificationCenter:(nonnull UNUserNotificationCenter *)center + didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response; -- (void)handleActionWithIdentifier:(NSString * _Nullable)identifier forLocalNotification:(UILocalNotification * _Nonnull)notification; +- (void)handleActionWithIdentifier:(nullable NSString *)identifier + forLocalNotification:(nonnull UILocalNotification *)notification; @end diff --git a/Pod/BVNotifications/BVNotificationConfiguration.h b/Pod/BVNotifications/BVNotificationConfiguration.h index e6485994..e85bf02a 100644 --- a/Pod/BVNotifications/BVNotificationConfiguration.h +++ b/Pod/BVNotifications/BVNotificationConfiguration.h @@ -6,16 +6,24 @@ // // -#import -#import "BVStoreReviewNotificationProperties.h" #import "BVProductReviewNotificationProperties.h" +#import "BVStoreReviewNotificationProperties.h" +#import #define S3_API_VERSION @"v1" #define NOTIFICATION_CONFIG_ROOT @"https://s3.amazonaws.com" @interface BVNotificationConfiguration : NSObject -+ (void)loadGeofenceConfiguration:(NSURL * _Nonnull)url completion:(void (^ _Nonnull)(BVStoreReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull errors))failure; -+ (void)loadPINConfiguration:(NSURL * _Nonnull)url completion:(void (^ _Nonnull)(BVProductReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull errors))failure; ++ (void) +loadGeofenceConfiguration:(nonnull NSURL *)url + completion:(nonnull void (^)(BVStoreReviewNotificationProperties + *__nonnull response))completion + failure:(nonnull void (^)(NSError *__nonnull errors))failure; ++ (void) +loadPINConfiguration:(nonnull NSURL *)url + completion:(nonnull void (^)(BVProductReviewNotificationProperties + *__nonnull response))completion + failure:(nonnull void (^)(NSError *__nonnull errors))failure; @end diff --git a/Pod/BVNotifications/BVNotificationConfiguration.m b/Pod/BVNotifications/BVNotificationConfiguration.m index b9e8d178..067436be 100644 --- a/Pod/BVNotifications/BVNotificationConfiguration.m +++ b/Pod/BVNotifications/BVNotificationConfiguration.m @@ -11,77 +11,113 @@ @implementation BVNotificationConfiguration -+ (void)loadGeofenceConfiguration:(NSURL * _Nonnull)url completion:(void (^ _Nonnull)(BVStoreReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull errors))failure { - - [self loadConfiguration:url completion:^(NSDictionary *response, NSError* error) { - - if (response == nil && error == nil){ - NSString *errorMessage = @"No config found for geofence notifications. Will not post notification."; - [[BVLogger sharedLogger] error:errorMessage]; - [NSError errorWithDomain:BVErrDomain code:-1 userInfo:@{@"message" : errorMessage}]; - return failure(error); - } - - if (!error) { - BVStoreReviewNotificationProperties *props = [[BVStoreReviewNotificationProperties alloc] initWithDictionary:response]; - completion(props); - - }else { - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"Failed to fetch store notification configuration: %@", error.localizedDescription]]; - failure(error); - } - }]; ++ (void) +loadGeofenceConfiguration:(nonnull NSURL *)url + completion:(nonnull void (^)(BVStoreReviewNotificationProperties + *__nonnull response))completion + failure:(nonnull void (^)(NSError *__nonnull errors))failure { + [self loadConfiguration:url + completion:^(NSDictionary *response, NSError *error) { + + if (response == nil && error == nil) { + NSString *errorMessage = @"No config found for geofence " + @"notifications. Will not post " + @"notification."; + [[BVLogger sharedLogger] error:errorMessage]; + [NSError errorWithDomain:BVErrDomain + code:-1 + userInfo:@{@"message" : errorMessage}]; + return failure(error); + } + + if (!error) { + BVStoreReviewNotificationProperties *props = + [[BVStoreReviewNotificationProperties alloc] + initWithDictionary:response]; + completion(props); + + } else { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat:@"Failed to fetch store " + @"notification " + @"configuration: %@", + error.localizedDescription]]; + failure(error); + } + }]; } -+ (void)loadPINConfiguration:(NSURL * _Nonnull)url completion:(void (^ _Nonnull)(BVProductReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull errors))failure { - [self loadConfiguration:url completion:^(NSDictionary *response, NSError* error) { - - if (response == nil && error == nil){ - NSString *errorMessage = @"No notification config found for PIN. Will not post notifications."; - [[BVLogger sharedLogger] error:errorMessage]; - [NSError errorWithDomain:BVErrDomain code:-1 userInfo:@{@"message" : errorMessage}]; - return failure(error); - } - - if (!error) { - BVProductReviewNotificationProperties *props = [[BVProductReviewNotificationProperties alloc] initWithDictionary:response]; - completion(props); - }else { - [[BVLogger sharedLogger] error:[NSString stringWithFormat:@"Failed to fetch store notification configuration: %@", error.localizedDescription]]; - failure(error); - } - }]; ++ (void) +loadPINConfiguration:(nonnull NSURL *)url + completion:(nonnull void (^)(BVProductReviewNotificationProperties + *__nonnull response))completion + failure:(nonnull void (^)(NSError *__nonnull errors))failure { + [self loadConfiguration:url + completion:^(NSDictionary *response, NSError *error) { + + if (response == nil && error == nil) { + NSString *errorMessage = @"No notification config found " + @"for PIN. Will not post " + @"notifications."; + [[BVLogger sharedLogger] error:errorMessage]; + [NSError errorWithDomain:BVErrDomain + code:-1 + userInfo:@{@"message" : errorMessage}]; + return failure(error); + } + + if (!error) { + BVProductReviewNotificationProperties *props = + [[BVProductReviewNotificationProperties alloc] + initWithDictionary:response]; + completion(props); + } else { + [[BVLogger sharedLogger] + error:[NSString + stringWithFormat:@"Failed to fetch store " + @"notification " + @"configuration: %@", + error.localizedDescription]]; + failure(error); + } + }]; } -+ (void)loadConfiguration:(NSURL * _Nonnull)url completion:(void (^ _Nonnull)(NSDictionary * _Nullable response, NSError *error))completion { - - NSURLRequest* urlRequest = [NSURLRequest requestWithURL:url]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; - - NSURLSession* session = [NSURLSession sharedSession]; - NSURLSessionDataTask* task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; - - if (!error && httpResponse.statusCode <300 ){ - ++ (void)loadConfiguration:(nonnull NSURL *)url + completion:(nonnull void (^)(NSDictionary *__nullable response, + NSError *error))completion { + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + + [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; + + NSURLSession *session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session + dataTaskWithRequest:urlRequest + completionHandler:^(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable error) { + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + if (!error && httpResponse.statusCode < 300) { NSError *jsonError; - NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&jsonError]; - if (jsonDict){ - completion(jsonDict, nil); + NSDictionary *jsonDict = [NSJSONSerialization + JSONObjectWithData:data + options:NSJSONReadingMutableLeaves + error:&jsonError]; + if (jsonDict) { + completion(jsonDict, nil); } - - } else { + + } else { completion(nil, error); - } - - }]; - - // start the request - [task resume]; - -} + } + + }]; + // start the request + [task resume]; +} @end diff --git a/Pod/BVNotifications/BVNotificationConstants.h b/Pod/BVNotifications/BVNotificationConstants.h index 58920158..5fecfefe 100644 --- a/Pod/BVNotifications/BVNotificationConstants.h +++ b/Pod/BVNotifications/BVNotificationConstants.h @@ -6,23 +6,22 @@ // // - #ifndef BVNotificationConstants_h #define BVNotificationConstants_h -#define ID_REPLY @"idactionreply" -#define ID_REMIND @"idactionremind" -#define ID_DISMISS @"idactiondismiss" -#define USER_INFO_LAT @"lat" -#define USER_INFO_LONG @"long" -#define USER_INFO_LAT_DELTA @"latDelta" -#define USER_INFO_LONG_DELTA @"longDelta" -#define USER_INFO_URL_SCHEME @"urlScheme" -#define USER_INFO_PROD_ID @"productId" -#define USER_INFO_PROD_NAME @"productName" -#define USER_INFO_PROD_IMAGE_URL @"productImageUrl" -#define USER_INFO_API_KEY_CONVERSATIONS @"apiConversations" -#define USER_INFO_CLIENTID @"clientId" -#define USER_INFO_CONFIG_PROPS @"configProps" +#define ID_REPLY @"idactionreply" +#define ID_REMIND @"idactionremind" +#define ID_DISMISS @"idactiondismiss" +#define USER_INFO_LAT @"lat" +#define USER_INFO_LONG @"long" +#define USER_INFO_LAT_DELTA @"latDelta" +#define USER_INFO_LONG_DELTA @"longDelta" +#define USER_INFO_URL_SCHEME @"urlScheme" +#define USER_INFO_PROD_ID @"productId" +#define USER_INFO_PROD_NAME @"productName" +#define USER_INFO_PROD_IMAGE_URL @"productImageUrl" +#define USER_INFO_API_KEY_CONVERSATIONS @"apiConversations" +#define USER_INFO_CLIENTID @"clientId" +#define USER_INFO_CONFIG_PROPS @"configProps" #endif /* BVNotificationConstants_h */ diff --git a/Pod/BVNotifications/BVNotificationProperties.h b/Pod/BVNotifications/BVNotificationProperties.h index 141b0192..c060790e 100644 --- a/Pod/BVNotifications/BVNotificationProperties.h +++ b/Pod/BVNotifications/BVNotificationProperties.h @@ -10,23 +10,22 @@ @interface BVNotificationProperties : NSObject -@property (nonatomic, assign, readonly) NSTimeInterval notificationDelay; +@property(nonatomic, assign, readonly) NSTimeInterval notificationDelay; -@property (nonatomic, strong, readonly) NSString *reviewPromtDispayText; -@property (nonatomic, strong, readonly) NSString *reviewPromptSubtitleText; -@property (nonatomic, strong, readonly) NSString *reviewPromptYesReview; -@property (nonatomic, strong, readonly) NSString *reviewPromtNoReview; -@property (nonatomic, strong, readonly) NSString *reviewPromptRemindText; -@property (nonatomic, assign, readonly) BOOL notificationsEnabled; -@property (nonatomic, strong, readonly) NSString *customUrlScheme; -@property (nonatomic, assign, readonly) BOOL requestReviewOnAppOpen; -@property (nonatomic, assign, readonly) NSTimeInterval visitDuration; -@property (nonatomic, assign, readonly) NSTimeInterval remindMeLaterDuration; -@property (nonatomic, strong, readonly) NSDate *allowableTimePeriodStart; -@property (nonatomic, strong, readonly) NSDate *allowableTimePeriodEnd; +@property(nonatomic, strong, readonly) NSString *reviewPromtDispayText; +@property(nonatomic, strong, readonly) NSString *reviewPromptSubtitleText; +@property(nonatomic, strong, readonly) NSString *reviewPromptYesReview; +@property(nonatomic, strong, readonly) NSString *reviewPromtNoReview; +@property(nonatomic, strong, readonly) NSString *reviewPromptRemindText; +@property(nonatomic, assign, readonly) BOOL notificationsEnabled; +@property(nonatomic, strong, readonly) NSString *customUrlScheme; +@property(nonatomic, assign, readonly) BOOL requestReviewOnAppOpen; +@property(nonatomic, assign, readonly) NSTimeInterval visitDuration; +@property(nonatomic, assign, readonly) NSTimeInterval remindMeLaterDuration; +@property(nonatomic, strong, readonly) NSDate *allowableTimePeriodStart; +@property(nonatomic, strong, readonly) NSDate *allowableTimePeriodEnd; - (id)initWithDictionary:(NSDictionary *)configDict; -@property (nonatomic, strong, readonly) NSDictionary *configDict; +@property(nonatomic, strong, readonly) NSDictionary *configDict; @end - diff --git a/Pod/BVNotifications/BVNotificationProperties.m b/Pod/BVNotifications/BVNotificationProperties.m index 1ae94939..6b36ba9d 100644 --- a/Pod/BVNotifications/BVNotificationProperties.m +++ b/Pod/BVNotifications/BVNotificationProperties.m @@ -7,61 +7,71 @@ // #import "BVNotificationProperties.h" -#import "BVNullHelper.h" #import "BVLogger.h" +#import "BVNullHelper.h" @implementation BVNotificationProperties @synthesize reviewPromtNoReview = _reviewPromtNoReview; -- (id)initWithDictionary:(NSDictionary *)configDict{ - - self = [super init]; - _configDict = configDict; - _notificationsEnabled = [[configDict objectForKey:@"notificationsEnabled"] boolValue]; - if ([configDict objectForKey:@"notificationDelay"]) { - _notificationDelay = [[configDict objectForKey:@"notificationDelay"] integerValue]; - } - - SET_IF_NOT_NULL(_reviewPromtDispayText, [configDict objectForKey:@"reviewPromptDisplayText"]); - SET_IF_NOT_NULL(_reviewPromptSubtitleText, [configDict objectForKey:@"reviewPromptSubtitleText"]); - SET_DEFAULT_IF_NULL(_reviewPromptYesReview, [configDict objectForKey:@"reviewPromptYesReview"], @"Leave Review Now"); - SET_DEFAULT_IF_NULL(_reviewPromtNoReview, [configDict objectForKey:@"reviewPromtNoReview"], @"No Thanks."); - SET_DEFAULT_IF_NULL(_reviewPromptRemindText, [configDict objectForKey:@"reviewPromptRemindText"], @"Remind Me Later"); - SET_IF_NOT_NULL(_customUrlScheme, [configDict objectForKey:@"urlScheme"]); - - _requestReviewOnAppOpen = [[configDict objectForKey:@"requestReviewOnAppOpen"] boolValue]; - - if ([configDict objectForKey:@"visitDuration"] != nil) { - _visitDuration = [[configDict objectForKey:@"visitDuration"] integerValue]; - } else { - _visitDuration = 300; // Default 5 minutes - } - - - if ([configDict objectForKey:@"reviewRemindLaterDuration"]) { - _remindMeLaterDuration = [[configDict objectForKey:@"reviewRemindLaterDuration"] integerValue]; - } - - if ([configDict objectForKey:@"allowableTimePeriodStart"]){ - NSDateComponents *comps = [[NSDateComponents alloc] init]; - comps.hour = 17; - comps.minute = 0; - NSCalendar *calendar = [NSCalendar currentCalendar]; - [calendar setTimeZone: [NSTimeZone systemTimeZone]]; - _allowableTimePeriodStart = [calendar dateFromComponents:comps]; - } - - if ([configDict objectForKey:@"allowableTimePeriodEnd"]){ - NSDateComponents *comps = [[NSDateComponents alloc] init]; - comps.hour = 19; - comps.minute = 0; - NSCalendar *calendar = [NSCalendar currentCalendar]; - [calendar setTimeZone: [NSTimeZone systemTimeZone]]; - _allowableTimePeriodEnd = [calendar dateFromComponents:comps]; - } +- (id)initWithDictionary:(NSDictionary *)configDict { + + self = [super init]; + _configDict = configDict; + _notificationsEnabled = + [[configDict objectForKey:@"notificationsEnabled"] boolValue]; + if ([configDict objectForKey:@"notificationDelay"]) { + _notificationDelay = + [[configDict objectForKey:@"notificationDelay"] integerValue]; + } + + SET_IF_NOT_NULL(_reviewPromtDispayText, + [configDict objectForKey:@"reviewPromptDisplayText"]); + SET_IF_NOT_NULL(_reviewPromptSubtitleText, + [configDict objectForKey:@"reviewPromptSubtitleText"]); + SET_DEFAULT_IF_NULL(_reviewPromptYesReview, + [configDict objectForKey:@"reviewPromptYesReview"], + @"Leave Review Now"); + SET_DEFAULT_IF_NULL(_reviewPromtNoReview, + [configDict objectForKey:@"reviewPromtNoReview"], + @"No Thanks."); + SET_DEFAULT_IF_NULL(_reviewPromptRemindText, + [configDict objectForKey:@"reviewPromptRemindText"], + @"Remind Me Later"); + SET_IF_NOT_NULL(_customUrlScheme, [configDict objectForKey:@"urlScheme"]); + + _requestReviewOnAppOpen = + [[configDict objectForKey:@"requestReviewOnAppOpen"] boolValue]; + + if ([configDict objectForKey:@"visitDuration"] != nil) { + _visitDuration = [[configDict objectForKey:@"visitDuration"] integerValue]; + } else { + _visitDuration = 300; // Default 5 minutes + } + + if ([configDict objectForKey:@"reviewRemindLaterDuration"]) { + _remindMeLaterDuration = + [[configDict objectForKey:@"reviewRemindLaterDuration"] integerValue]; + } + + if ([configDict objectForKey:@"allowableTimePeriodStart"]) { + NSDateComponents *comps = [[NSDateComponents alloc] init]; + comps.hour = 17; + comps.minute = 0; + NSCalendar *calendar = [NSCalendar currentCalendar]; + [calendar setTimeZone:[NSTimeZone systemTimeZone]]; + _allowableTimePeriodStart = [calendar dateFromComponents:comps]; + } + + if ([configDict objectForKey:@"allowableTimePeriodEnd"]) { + NSDateComponents *comps = [[NSDateComponents alloc] init]; + comps.hour = 19; + comps.minute = 0; + NSCalendar *calendar = [NSCalendar currentCalendar]; + [calendar setTimeZone:[NSTimeZone systemTimeZone]]; + _allowableTimePeriodEnd = [calendar dateFromComponents:comps]; + } - - return self; + return self; } @end diff --git a/Pod/BVNotifications/BVNotificationsAnalyticsHelper.h b/Pod/BVNotifications/BVNotificationsAnalyticsHelper.h index eb42193b..ed8bea70 100644 --- a/Pod/BVNotifications/BVNotificationsAnalyticsHelper.h +++ b/Pod/BVNotifications/BVNotificationsAnalyticsHelper.h @@ -6,18 +6,19 @@ // // -#import #import "BVBaseAnalyticsHelper.h" +#import -typedef enum { - ProductTypeStore, - ProductTypeProduct -}ProductType; +typedef enum { ProductTypeStore, ProductTypeProduct } ProductType; @interface BVNotificationsAnalyticsHelper : BVBaseAnalyticsHelper -+(void)queueAnalyticEventForReviewNotificationInView:(NSString *)viewName withId:(NSString *)Id andProductType:(ProductType)type; ++ (void)queueAnalyticEventForReviewNotificationInView:(NSString *)viewName + withId:(NSString *)Id + andProductType:(ProductType)type; -+(void)queueAnalyticEventForReviewUsedFeature:(NSString *)actionDetail withId:(NSString *)Id andProductType:(ProductType)type; ++ (void)queueAnalyticEventForReviewUsedFeature:(NSString *)actionDetail + withId:(NSString *)Id + andProductType:(ProductType)type; @end diff --git a/Pod/BVNotifications/BVNotificationsAnalyticsHelper.m b/Pod/BVNotifications/BVNotificationsAnalyticsHelper.m index 9a85325e..27497e52 100644 --- a/Pod/BVNotifications/BVNotificationsAnalyticsHelper.m +++ b/Pod/BVNotifications/BVNotificationsAnalyticsHelper.m @@ -11,33 +11,39 @@ @implementation BVNotificationsAnalyticsHelper -+(void)queueAnalyticEventForReviewNotificationInView:(NSString *)viewName withId:(NSString *)Id andProductType:(ProductType)type{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithDictionary:[self getFeatureUsedInViewParams]]; - [eventDict addEntriesFromDictionary:[self getCommonParams:Id type:type]]; - [eventDict setObject:viewName forKey:@"detail1"]; - [[BVAnalyticsManager sharedManager] queueEvent:eventDict]; ++ (void)queueAnalyticEventForReviewNotificationInView:(NSString *)viewName + withId:(NSString *)Id + andProductType:(ProductType)type { + + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithDictionary:[self getFeatureUsedInViewParams]]; + [eventDict addEntriesFromDictionary:[self getCommonParams:Id type:type]]; + [eventDict setObject:viewName forKey:@"detail1"]; + [[BVAnalyticsManager sharedManager] queueEvent:eventDict]; } -+(void)queueAnalyticEventForReviewUsedFeature:(NSString *)actionDetail withId:(NSString *)Id andProductType:(ProductType)type{ - - NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithDictionary:[self getFeatureUsedParams]]; - [eventDict addEntriesFromDictionary:[self getCommonParams:Id type:type]]; - [eventDict setObject:@"PushNotification" forKey:@"name"]; - [eventDict setObject:actionDetail forKey:@"detail1"]; - [[BVAnalyticsManager sharedManager] queueEvent:eventDict]; ++ (void)queueAnalyticEventForReviewUsedFeature:(NSString *)actionDetail + withId:(NSString *)Id + andProductType:(ProductType)type { + + NSMutableDictionary *eventDict = [NSMutableDictionary + dictionaryWithDictionary:[self getFeatureUsedParams]]; + [eventDict addEntriesFromDictionary:[self getCommonParams:Id type:type]]; + [eventDict setObject:@"PushNotification" forKey:@"name"]; + [eventDict setObject:actionDetail forKey:@"detail1"]; + [[BVAnalyticsManager sharedManager] queueEvent:eventDict]; } -+(NSDictionary*)getCommonParams:(NSString*)Id type:(ProductType)type { - NSMutableDictionary *eventDict = [NSMutableDictionary new]; - [eventDict setObject:Id forKey:@"productId"]; - [eventDict setObject:@"RatingsAndReviews" forKey:@"bvProduct"]; - if (type == ProductTypeStore) { - [eventDict setObject:@"store" forKey:@"detail2"]; - }else { - [eventDict setObject:@"product" forKey:@"detail2"]; - } - return eventDict; ++ (NSDictionary *)getCommonParams:(NSString *)Id type:(ProductType)type { + NSMutableDictionary *eventDict = [NSMutableDictionary new]; + [eventDict setObject:Id forKey:@"productId"]; + [eventDict setObject:@"RatingsAndReviews" forKey:@"bvProduct"]; + if (type == ProductTypeStore) { + [eventDict setObject:@"store" forKey:@"detail2"]; + } else { + [eventDict setObject:@"product" forKey:@"detail2"]; + } + return eventDict; } @end diff --git a/Pod/BVNotifications/BVOpenURLMetaData.h b/Pod/BVNotifications/BVOpenURLMetaData.h index cdc5d5f5..7e096c5a 100644 --- a/Pod/BVNotifications/BVOpenURLMetaData.h +++ b/Pod/BVNotifications/BVOpenURLMetaData.h @@ -8,26 +8,22 @@ #import -typedef enum { - BVSenderTypeNotification -}BVSenderType; +typedef enum { BVSenderTypeNotification } BVSenderType; -typedef enum { - BVContentTypeReview -}BVContentType; +typedef enum { BVContentTypeReview } BVContentType; typedef enum { - BVContentSubtypeStore, - BVContentSubtypeProduct -}BVContentSubtype; + BVContentSubtypeStore, + BVContentSubtypeProduct +} BVContentSubtype; @interface BVOpenURLMetaData : NSObject --( instancetype)initWithURL:(NSURL*)url; +- (instancetype)initWithURL:(NSURL *)url; -@property (nonatomic, assign, readonly) BVSenderType sender; -@property (nonatomic, assign, readonly) BVContentType type; -@property (nonatomic, assign, readonly) BVContentSubtype subtype; -@property (nonatomic, strong, readonly) NSString *ID; +@property(nonatomic, assign, readonly) BVSenderType sender; +@property(nonatomic, assign, readonly) BVContentType type; +@property(nonatomic, assign, readonly) BVContentSubtype subtype; +@property(nonatomic, strong, readonly) NSString *ID; @end diff --git a/Pod/BVNotifications/BVOpenURLMetaData.m b/Pod/BVNotifications/BVOpenURLMetaData.m index d8ba710a..ce8ad0ac 100644 --- a/Pod/BVNotifications/BVOpenURLMetaData.m +++ b/Pod/BVNotifications/BVOpenURLMetaData.m @@ -11,36 +11,40 @@ @implementation BVOpenURLMetaData --(instancetype)initWithURL:(NSURL*)url { - - if (![url.scheme isEqualToString:[[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] customUrlScheme]]) { - return nil; - } - - if (self = [super init]) { - NSArray *queries = [url.query componentsSeparatedByString:@"&"]; - for (NSString *query in queries) { - NSArray *comps = [query componentsSeparatedByString:@"="]; - if ([@"sender" isEqualToString:comps[0]]) { - if ([@"notification" isEqualToString:comps[1]]) { - _sender = BVSenderTypeNotification; - } - }else if ([@"type" isEqualToString:comps[0]]) { - if ([@"review" isEqualToString:comps[1]]) { - _type = BVContentTypeReview; - } - }else if ([@"subtype" isEqualToString:comps[0]]) { - if ([@"store" isEqualToString:comps[1]]) { - _subtype = BVContentSubtypeStore; - }else if ([@"product" isEqualToString:comps[1]]) { - _subtype = BVContentSubtypeProduct; - } - }else if ([@"id" isEqualToString:comps[0]]) { - _ID = comps[1]; - } +- (instancetype)initWithURL:(NSURL *)url { + + if (![url.scheme + isEqualToString:[[[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + customUrlScheme]]) { + return nil; + } + + if (self = [super init]) { + NSArray *queries = [url.query componentsSeparatedByString:@"&"]; + for (NSString *query in queries) { + NSArray *comps = [query componentsSeparatedByString:@"="]; + if ([@"sender" isEqualToString:comps[0]]) { + if ([@"notification" isEqualToString:comps[1]]) { + _sender = BVSenderTypeNotification; + } + } else if ([@"type" isEqualToString:comps[0]]) { + if ([@"review" isEqualToString:comps[1]]) { + _type = BVContentTypeReview; + } + } else if ([@"subtype" isEqualToString:comps[0]]) { + if ([@"store" isEqualToString:comps[1]]) { + _subtype = BVContentSubtypeStore; + } else if ([@"product" isEqualToString:comps[1]]) { + _subtype = BVContentSubtypeProduct; } + } else if ([@"id" isEqualToString:comps[0]]) { + _ID = comps[1]; + } } - return self; + } + return self; } @end diff --git a/Pod/BVNotifications/BVProductReviewNotificationCenter.h b/Pod/BVNotifications/BVProductReviewNotificationCenter.h index b947911a..54368212 100644 --- a/Pod/BVNotifications/BVProductReviewNotificationCenter.h +++ b/Pod/BVNotifications/BVProductReviewNotificationCenter.h @@ -5,15 +5,16 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // // -#import #import "BVNotificationCenterObject.h" #import "BVPIN.h" #import "BVProduct.h" +#import -@interface BVProductReviewNotificationCenter : NSObject +@interface BVProductReviewNotificationCenter + : NSObject --(id _Null_unspecified)init __attribute__((unavailable("Use sharedCenter"))); +- (id _Null_unspecified)init __attribute__((unavailable("Use sharedCenter"))); -+(instancetype _Nonnull)sharedCenter; ++ (nonnull instancetype)sharedCenter; @end diff --git a/Pod/BVNotifications/BVProductReviewNotificationCenter.m b/Pod/BVNotifications/BVProductReviewNotificationCenter.m index 34d88f4b..5469fae0 100644 --- a/Pod/BVNotifications/BVProductReviewNotificationCenter.m +++ b/Pod/BVNotifications/BVProductReviewNotificationCenter.m @@ -13,20 +13,22 @@ @implementation BVProductReviewNotificationCenter -+(instancetype _Nonnull)sharedCenter; ++ (nonnull instancetype)sharedCenter; { - static dispatch_once_t p = 0; - - __strong static id _sharedObject = nil; - dispatch_once(&p, ^{ - if ([[[UIDevice currentDevice] systemVersion] compare:@"10.0" options:NSNumericSearch] != NSOrderedAscending) { - _sharedObject = [[BVProductReviewRichNotificationCenter alloc] init]; - }else { - _sharedObject = [[BVProductReviewSimpleNotificationCenter alloc] init]; - } - }); - - return _sharedObject; + static dispatch_once_t p = 0; + + __strong static id _sharedObject = nil; + dispatch_once(&p, ^{ + if ([[[UIDevice currentDevice] systemVersion] + compare:@"10.0" + options:NSNumericSearch] != NSOrderedAscending) { + _sharedObject = [[BVProductReviewRichNotificationCenter alloc] init]; + } else { + _sharedObject = [[BVProductReviewSimpleNotificationCenter alloc] init]; + } + }); + + return _sharedObject; } @end diff --git a/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.h b/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.h index 9b2e537c..a9dbf13c 100644 --- a/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.h +++ b/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.h @@ -12,10 +12,12 @@ @interface BVProductReviewNotificationConfigurationLoader : NSObject /** - Configuration properties that determine display text and delay vaues for a product review notification. + Configuration properties that determine display text and delay vaues for a + product review notification. */ -@property (strong, readonly) BVProductReviewNotificationProperties * _Nonnull bvProductReviewNotificationProperties; +@property(nonnull, strong, readonly) BVProductReviewNotificationProperties + *bvProductReviewNotificationProperties; -+(id _Nonnull)sharedManager; ++ (nonnull id)sharedManager; @end diff --git a/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.m b/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.m index 95a05c96..33bc6fb4 100644 --- a/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.m +++ b/Pod/BVNotifications/BVProductReviewNotificationConfigurationLoader.m @@ -1,3 +1,5 @@ + + // // BVProductReviewNotificationConfigurationLoader.m // BVSDK @@ -7,72 +9,84 @@ #import "BVProductReviewNotificationConfigurationLoader.h" #import "BVNotificationConfiguration.h" -#import "BVSDKManager.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" @implementation BVProductReviewNotificationConfigurationLoader -+(void)load{ - [self sharedManager]; ++ (void)load { + [self sharedManager]; } -+(id)sharedManager { - static BVProductReviewNotificationConfigurationLoader *shared; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - shared = [[self alloc]init]; - }); - - return shared; ++ (id)sharedManager { + static BVProductReviewNotificationConfigurationLoader *shared; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + shared = [[self alloc] init]; + }); + + return shared; } - (id)init { - if (self = [super init]) { - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(receivePINAPIKey:) - name:PIN_API_KEY_SET_NOTIFICATION - object:nil]; - - } - - return self; + if (self = [super init]) { + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receivePINAPIKey:) + name:PIN_API_KEY_SET_NOTIFICATION + object:nil]; + } + + return self; } +- (void)receivePINAPIKey:(NSNotification *)notification { + // [notification name] should always be PIN_API_KEY_SET_NOTIFICATION + // unless you use this method for observation of other notifications + // as well. + + if ([[notification name] isEqualToString:PIN_API_KEY_SET_NOTIFICATION]) { + [[BVLogger sharedLogger] + verbose:@"Recieved notifcation for PIN configuration"]; -- (void) receivePINAPIKey:(NSNotification *) notification -{ - // [notification name] should always be PIN_API_KEY_SET_NOTIFICATION - // unless you use this method for observation of other notifications - // as well. - - if ([[notification name] isEqualToString:PIN_API_KEY_SET_NOTIFICATION]){ - - [[BVLogger sharedLogger] verbose:@"Recieved notifcation for PIN configuration"]; - - [self loadPINConfiguration:^(BVProductReviewNotificationProperties * _Nonnull response) { - // success - } failure:^(NSError * _Nonnull error) { - // failed - }]; - + [self loadPINConfiguration:^( + BVProductReviewNotificationProperties *__nonnull response) { + // success } + failure:^(NSError *__nonnull error){ + // failed + }]; + } } - --(void)loadPINConfiguration:(void (^ _Nonnull)(BVProductReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull error))failure { - - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/%@/pin/pinConfig.json", NOTIFICATION_CONFIG_ROOT, S3_API_VERSION, [[[BVSDKManager sharedManager] configuration] clientId]]]; - [BVNotificationConfiguration loadPINConfiguration:url completion:^(BVProductReviewNotificationProperties * _Nonnull response) { - [[BVLogger sharedLogger] verbose:@"Successfully loaded BVProductReviewNotificationProperties"]; +- (void)loadPINConfiguration: + (nonnull void (^)(BVProductReviewNotificationProperties *__nonnull + response))completion + failure: + (nonnull void (^)(NSError *__nonnull error))failure { + NSURL *url = [NSURL + URLWithString:[NSString stringWithFormat:@"%@/incubator-mobile-apps/" + @"sdk/%@/ios/%@/pin/" + @"pinConfig.json", + NOTIFICATION_CONFIG_ROOT, + S3_API_VERSION, + [[[BVSDKManager sharedManager] + configuration] clientId]]]; + [BVNotificationConfiguration loadPINConfiguration:url + completion:^(BVProductReviewNotificationProperties *__nonnull response) { + [[BVLogger sharedLogger] + verbose: + @"Successfully loaded BVProductReviewNotificationProperties"]; _bvProductReviewNotificationProperties = response; completion(response); - - } failure:^(NSError * _Nonnull error) { - [[BVLogger sharedLogger] error:@"ERROR: Failed to load BVProductReviewNotificationProperties"]; + + } + failure:^(NSError *__nonnull error) { + [[BVLogger sharedLogger] error:@"ERROR: Failed to load " + @"BVProductReviewNotificationProperti" + @"es"]; failure(error); - }]; - + }]; } @end diff --git a/Pod/BVNotifications/BVProductReviewNotificationProperties.h b/Pod/BVNotifications/BVProductReviewNotificationProperties.h index d07fec64..f1d54058 100644 --- a/Pod/BVNotifications/BVProductReviewNotificationProperties.h +++ b/Pod/BVNotifications/BVProductReviewNotificationProperties.h @@ -6,8 +6,8 @@ // // -#import #import "BVNotificationProperties.h" +#import @interface BVProductReviewNotificationProperties : BVNotificationProperties diff --git a/Pod/BVNotifications/BVProductReviewRichNotificationCenter.h b/Pod/BVNotifications/BVProductReviewRichNotificationCenter.h index 2d8b10cb..bdc4c8fd 100644 --- a/Pod/BVNotifications/BVProductReviewRichNotificationCenter.h +++ b/Pod/BVNotifications/BVProductReviewRichNotificationCenter.h @@ -6,9 +6,11 @@ // // -#import #import "BVNotificationCenterObject.h" +#import -@interface BVProductReviewRichNotificationCenter : NSObject +@interface BVProductReviewRichNotificationCenter + : NSObject @end diff --git a/Pod/BVNotifications/BVProductReviewRichNotificationCenter.m b/Pod/BVNotifications/BVProductReviewRichNotificationCenter.m index 6b6dda98..53765dd4 100644 --- a/Pod/BVNotifications/BVProductReviewRichNotificationCenter.m +++ b/Pod/BVNotifications/BVProductReviewRichNotificationCenter.m @@ -7,114 +7,166 @@ // #import "BVProductReviewRichNotificationCenter.h" +#import "BVConversationsInclude.h" #import "BVNotificationConstants.h" -#import "BVSDKManager.h" -#import "BVProduct.h" -#import "BVProductDisplayPageRequest.h" #import "BVNotificationsAnalyticsHelper.h" #import "BVPIN.h" -#import "BVConversationsInclude.h" -#import "BVProductReviewNotificationProperties.h" +#import "BVProduct.h" +#import "BVProductDisplayPageRequest.h" #import "BVProductReviewNotificationConfigurationLoader.h" +#import "BVProductReviewNotificationProperties.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" @implementation BVProductReviewRichNotificationCenter -- (void)addNotificationCategories:(BVProductReviewNotificationProperties *)notificationProperties { - - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - UNNotificationAction *replyAction = [UNNotificationAction actionWithIdentifier:ID_REPLY title:notificationProperties.reviewPromptYesReview options: UNNotificationActionOptionNone]; - UNNotificationAction *remindAction = [UNNotificationAction actionWithIdentifier:ID_REMIND title:notificationProperties.reviewPromptRemindText options:UNNotificationActionOptionNone]; - UNNotificationAction *dismissAction = [UNNotificationAction actionWithIdentifier:ID_DISMISS title:notificationProperties.reviewPromtNoReview options:UNNotificationActionOptionNone]; - - NSArray *notificationActions = @[ replyAction, remindAction, dismissAction ]; - - UNNotificationCategory *reviewStoreCategory = [UNNotificationCategory categoryWithIdentifier:PIN_CUSTOM_CATEGORY actions:notificationActions intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; - - NSSet *categories = [NSSet setWithObject:reviewStoreCategory]; - - [center setNotificationCategories:categories]; -} +- (void)addNotificationCategories: + (BVProductReviewNotificationProperties *)notificationProperties { + UNUserNotificationCenter *center = + [UNUserNotificationCenter currentNotificationCenter]; + UNNotificationAction *replyAction = [UNNotificationAction + actionWithIdentifier:ID_REPLY + title:notificationProperties.reviewPromptYesReview + options:UNNotificationActionOptionNone]; + UNNotificationAction *remindAction = [UNNotificationAction + actionWithIdentifier:ID_REMIND + title:notificationProperties.reviewPromptRemindText + options:UNNotificationActionOptionNone]; + UNNotificationAction *dismissAction = [UNNotificationAction + actionWithIdentifier:ID_DISMISS + title:notificationProperties.reviewPromtNoReview + options:UNNotificationActionOptionNone]; + + NSArray *notificationActions = @[ replyAction, remindAction, dismissAction ]; + UNNotificationCategory *reviewStoreCategory = [UNNotificationCategory + categoryWithIdentifier:PIN_CUSTOM_CATEGORY + actions:notificationActions + intentIdentifiers:@[] + options:UNNotificationCategoryOptionCustomDismissAction]; + + NSSet *categories = [NSSet setWithObject:reviewStoreCategory]; + + [center setNotificationCategories:categories]; +} - (void)queueReviewWithProductId:(nonnull NSString *)productId { - [self loadProductInfo:productId completion:^(BVProduct * _Nullable product) { - if (product) { - [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] product:product]; - } - }]; + [self loadProductInfo:productId + completion:^(BVProduct *__nullable product) { + if (product) { + [self queueProductReview: + [[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + product:product]; + } + }]; } --(void)queueReviewWithProduct:(BVProduct *)product { - [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] product:product]; +- (void)queueReviewWithProduct:(BVProduct *)product { + [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + product:product]; } --(void)queuePIN:(BVPIN *)pin { - [self loadProductInfo:pin.identifier completion:^(BVProduct * _Nullable product) { - if (product) { - [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] product:product]; - } - }]; +- (void)queuePIN:(BVPIN *)pin { + [self loadProductInfo:pin.identifier + completion:^(BVProduct *__nullable product) { + if (product) { + [self queueProductReview: + [[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + product:product]; + } + }]; } -- (void)rescheduleNotification:(NSDictionary *)productDict includes:(NSDictionary *)includes noteProps:(BVNotificationProperties *)noteProps { - BVConversationsInclude *include = [[BVConversationsInclude alloc] initWithApiResponse:includes]; - BVProduct *product = [[BVProduct alloc] initWithApiResponse:productDict includes:include]; - [self queueProductReview:(BVProductReviewNotificationProperties*)noteProps product:product]; +- (void)rescheduleNotification:(NSDictionary *)productDict + includes:(NSDictionary *)includes + noteProps:(BVNotificationProperties *)noteProps { + BVConversationsInclude *include = + [[BVConversationsInclude alloc] initWithApiResponse:includes]; + BVProduct *product = + [[BVProduct alloc] initWithApiResponse:productDict includes:include]; + [self queueProductReview:(BVProductReviewNotificationProperties *)noteProps + product:product]; } -- (void)queueProductReview:(BVProductReviewNotificationProperties*)noteProps product:(BVProduct*)product { - - if (![noteProps notificationsEnabled]) { - return; - } - [self addNotificationCategories:noteProps]; - - NSTimeInterval delay = noteProps.notificationDelay; - - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.title = [NSString localizedUserNotificationStringForKey:noteProps.reviewPromtDispayText arguments:nil]; - content.body = [NSString localizedUserNotificationStringForKey:noteProps.reviewPromptSubtitleText - arguments:nil]; - content.categoryIdentifier = PIN_CUSTOM_CATEGORY; - content.userInfo = @{USER_INFO_PROD_ID: product.identifier, - USER_INFO_PROD_NAME: product.name, - USER_INFO_PROD_IMAGE_URL: product.imageUrl, - USER_INFO_URL_SCHEME: noteProps.customUrlScheme, - USER_INFO_API_KEY_CONVERSATIONS: [BVSDKManager sharedManager].configuration.apiKeyConversations, - USER_INFO_CLIENTID: [BVSDKManager sharedManager].configuration.clientId, - USER_INFO_CONFIG_PROPS: noteProps.configDict}; - - UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay repeats:NO]; - - UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"com.bazaarvoice.store.review" - content:content trigger:trigger]; - - [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { - if (!error) { - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewNotificationInView:@"PINPushNotification" withId:product.identifier andProductType:ProductTypeProduct]; - } - }]; +- (void)queueProductReview:(BVProductReviewNotificationProperties *)noteProps + product:(BVProduct *)product { + if (![noteProps notificationsEnabled]) { + return; + } + [self addNotificationCategories:noteProps]; + + NSTimeInterval delay = noteProps.notificationDelay; + + UNUserNotificationCenter *center = + [UNUserNotificationCenter currentNotificationCenter]; + + UNMutableNotificationContent *content = + [[UNMutableNotificationContent alloc] init]; + content.title = [NSString + localizedUserNotificationStringForKey:noteProps.reviewPromtDispayText + arguments:nil]; + content.body = [NSString + localizedUserNotificationStringForKey:noteProps.reviewPromptSubtitleText + arguments:nil]; + content.categoryIdentifier = PIN_CUSTOM_CATEGORY; + content.userInfo = @{ + USER_INFO_PROD_ID : product.identifier, + USER_INFO_PROD_NAME : product.name, + USER_INFO_PROD_IMAGE_URL : product.imageUrl, + USER_INFO_URL_SCHEME : noteProps.customUrlScheme, + USER_INFO_API_KEY_CONVERSATIONS : [BVSDKManager sharedManager] + .configuration.apiKeyConversations, + USER_INFO_CLIENTID : [BVSDKManager sharedManager].configuration.clientId, + USER_INFO_CONFIG_PROPS : noteProps.configDict + }; + UNTimeIntervalNotificationTrigger *trigger = + [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay + repeats:NO]; + + UNNotificationRequest *request = [UNNotificationRequest + requestWithIdentifier:@"com.bazaarvoice.store.review" + content:content + trigger:trigger]; + + [center + addNotificationRequest:request + withCompletionHandler:^(NSError *__nullable error) { + if (!error) { + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewNotificationInView: + @"PINPushNotification" + withId:product.identifier + andProductType: + ProductTypeProduct]; + } + }]; } -- (void)loadProductInfo:(NSString*)productId completion:(void (^_Nonnull)(BVProduct * _Nullable product))completion{ - - BVProductDisplayPageRequest *request = [[BVProductDisplayPageRequest alloc] initWithProductId:productId]; - [request load:^(BVProductsResponse * _Nonnull response) { - - BVProduct* store = response.result; - completion(store); - - } failure:^(NSArray * _Nonnull errors) { +- (void)loadProductInfo:(NSString *)productId + completion: + (nonnull void (^)(BVProduct *__nullable product))completion { + BVProductDisplayPageRequest *request = + [[BVProductDisplayPageRequest alloc] initWithProductId:productId]; + [request load:^(BVProductsResponse *__nonnull response) { + + BVProduct *store = response.result; + completion(store); + + } + failure:^(NSArray *__nonnull errors) { [[BVLogger sharedLogger] printErrors:errors]; - }]; + }]; } -- (void)userNotificationCenter:(UNUserNotificationCenter* _Nonnull)center didReceiveNotificationResponse:(UNNotificationResponse * _Nonnull)response { +- (void)userNotificationCenter:(nonnull UNUserNotificationCenter *)center + didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response { } - @end diff --git a/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.h b/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.h index 94867fe6..b6c15152 100644 --- a/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.h +++ b/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.h @@ -6,9 +6,10 @@ // // -#import #import "BVNotificationCenterObject.h" +#import -@interface BVProductReviewSimpleNotificationCenter : NSObject +@interface BVProductReviewSimpleNotificationCenter + : NSObject @end diff --git a/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.m b/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.m index da6e1b3a..bbeca9e3 100644 --- a/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.m +++ b/Pod/BVNotifications/BVProductReviewSimpleNotificationCenter.m @@ -7,96 +7,123 @@ // #import "BVProductReviewSimpleNotificationCenter.h" -#import "BVProductReviewNotificationProperties.h" +#import "BVConversationsInclude.h" +#import "BVLogger.h" #import "BVNotificationConstants.h" #import "BVProductDisplayPageRequest.h" -#import "BVLogger.h" -#import "BVConversationsInclude.h" -#import "BVSDKManager.h" #import "BVProductReviewNotificationConfigurationLoader.h" +#import "BVProductReviewNotificationProperties.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" @implementation BVProductReviewSimpleNotificationCenter - (void)queueReviewWithProduct:(nonnull BVProduct *)product { - [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] product:product]; + [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + product:product]; } - (void)queuePIN:(nonnull BVPIN *)pin { - [self queueReviewWithProductId:pin.identifier]; + [self queueReviewWithProductId:pin.identifier]; } - (void)queueReviewWithProductId:(nonnull NSString *)productId { - [self loadProductInfo:productId completion:^(BVProduct * _Nullable product) { - if (product) { - [self queueProductReview:[[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties] product:product]; - } - }]; + [self loadProductInfo:productId + completion:^(BVProduct *__nullable product) { + if (product) { + [self queueProductReview: + [[BVProductReviewNotificationConfigurationLoader + sharedManager] + bvProductReviewNotificationProperties] + product:product]; + } + }]; } -- (void)rescheduleNotification:(NSDictionary *)productDict includes:(NSDictionary *)includes noteProps:(BVNotificationProperties *)noteProps { - BVConversationsInclude *include = [[BVConversationsInclude alloc] initWithApiResponse:includes]; - BVProduct *product = [[BVProduct alloc] initWithApiResponse:productDict includes:include]; - [self queueProductReview:(BVProductReviewNotificationProperties*)noteProps product:product]; +- (void)rescheduleNotification:(NSDictionary *)productDict + includes:(NSDictionary *)includes + noteProps:(BVNotificationProperties *)noteProps { + BVConversationsInclude *include = + [[BVConversationsInclude alloc] initWithApiResponse:includes]; + BVProduct *product = + [[BVProduct alloc] initWithApiResponse:productDict includes:include]; + [self queueProductReview:(BVProductReviewNotificationProperties *)noteProps + product:product]; } --(void)queueProductReview:(BVProductReviewNotificationProperties*)noteProps product:(BVProduct*)product { - [self addNotificationCategories:noteProps]; - - NSTimeInterval delay = noteProps.notificationDelay; - - UILocalNotification *notification = [[UILocalNotification alloc]init]; - notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:delay]; - notification.alertBody = noteProps.reviewPromtDispayText; - notification.category = PIN_CUSTOM_CATEGORY; - notification.userInfo = @{USER_INFO_PROD_ID: product.identifier, - USER_INFO_PROD_NAME: product.name, - USER_INFO_PROD_IMAGE_URL: product.imageUrl, - USER_INFO_URL_SCHEME: noteProps.customUrlScheme}; - [[UIApplication sharedApplication] scheduleLocalNotification:notification]; +- (void)queueProductReview:(BVProductReviewNotificationProperties *)noteProps + product:(BVProduct *)product { + [self addNotificationCategories:noteProps]; + + NSTimeInterval delay = noteProps.notificationDelay; + + UILocalNotification *notification = [[UILocalNotification alloc] init]; + notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:delay]; + notification.alertBody = noteProps.reviewPromtDispayText; + notification.category = PIN_CUSTOM_CATEGORY; + notification.userInfo = @{ + USER_INFO_PROD_ID : product.identifier, + USER_INFO_PROD_NAME : product.name, + USER_INFO_PROD_IMAGE_URL : product.imageUrl, + USER_INFO_URL_SCHEME : noteProps.customUrlScheme + }; + [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } -- (void)addNotificationCategories:(BVProductReviewNotificationProperties * _Nonnull)notificationProperties { - UIUserNotificationType type = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound; - UIMutableUserNotificationAction *replyAction = [UIMutableUserNotificationAction new]; - replyAction.identifier = ID_REPLY; - replyAction.title = notificationProperties.reviewPromptYesReview; - replyAction.activationMode = UIUserNotificationActivationModeForeground; - replyAction.destructive = NO; - replyAction.authenticationRequired = NO; - - UIMutableUserNotificationAction *noAction = [UIMutableUserNotificationAction new]; - noAction.identifier = ID_DISMISS; - noAction.title = notificationProperties.reviewPromtNoReview; - noAction.activationMode = UIUserNotificationActivationModeBackground; - noAction.destructive = NO; - noAction.authenticationRequired = NO; - - UIMutableUserNotificationCategory *category = [UIMutableUserNotificationCategory new]; - category.identifier = PIN_CUSTOM_CATEGORY; - [category setActions:@[replyAction, noAction] forContext:UIUserNotificationActionContextDefault]; - - NSSet *cats = [NSSet setWithObjects:category, nil]; - - UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:cats]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; +- (void)addNotificationCategories: + (nonnull BVProductReviewNotificationProperties *)notificationProperties { + UIUserNotificationType type = UIUserNotificationTypeAlert | + UIUserNotificationTypeBadge | + UIUserNotificationTypeSound; + UIMutableUserNotificationAction *replyAction = + [UIMutableUserNotificationAction new]; + replyAction.identifier = ID_REPLY; + replyAction.title = notificationProperties.reviewPromptYesReview; + replyAction.activationMode = UIUserNotificationActivationModeForeground; + replyAction.destructive = NO; + replyAction.authenticationRequired = NO; + + UIMutableUserNotificationAction *noAction = + [UIMutableUserNotificationAction new]; + noAction.identifier = ID_DISMISS; + noAction.title = notificationProperties.reviewPromtNoReview; + noAction.activationMode = UIUserNotificationActivationModeBackground; + noAction.destructive = NO; + noAction.authenticationRequired = NO; + + UIMutableUserNotificationCategory *category = + [UIMutableUserNotificationCategory new]; + category.identifier = PIN_CUSTOM_CATEGORY; + [category setActions:@[ replyAction, noAction ] + forContext:UIUserNotificationActionContextDefault]; + + NSSet *cats = [NSSet setWithObjects:category, nil]; + + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:type categories:cats]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } -- (void)loadProductInfo:(NSString*)productId completion:(void (^_Nonnull)(BVProduct * _Nullable product))completion{ - - BVProductDisplayPageRequest *request = [[BVProductDisplayPageRequest alloc] initWithProductId:productId]; - [request load:^(BVProductsResponse * _Nonnull response) { - - BVProduct* store = response.result; - completion(store); - - } failure:^(NSArray * _Nonnull errors) { +- (void)loadProductInfo:(NSString *)productId + completion: + (nonnull void (^)(BVProduct *__nullable product))completion { + BVProductDisplayPageRequest *request = + [[BVProductDisplayPageRequest alloc] initWithProductId:productId]; + [request load:^(BVProductsResponse *__nonnull response) { + + BVProduct *store = response.result; + completion(store); + + } + failure:^(NSArray *__nonnull errors) { [[BVLogger sharedLogger] printErrors:errors]; - }]; + }]; } --(void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification { - +- (void)handleActionWithIdentifier:(NSString *)identifier + forLocalNotification:(UILocalNotification *)notification { } @end diff --git a/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.h b/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.h index b9113731..6b76ccb1 100644 --- a/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.h +++ b/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.h @@ -12,10 +12,12 @@ @interface BVStoreNotificationConfigurationLoader : NSObject /** - Configuration properties that determine display text and delay vaues for a store review notification. + Configuration properties that determine display text and delay vaues for a + store review notification. */ -@property (strong, readonly) BVStoreReviewNotificationProperties * _Nonnull bvStoreReviewNotificationProperties; +@property(nonnull, strong, readonly) + BVStoreReviewNotificationProperties *bvStoreReviewNotificationProperties; -+(id _Nonnull)sharedManager; ++ (nonnull id)sharedManager; @end diff --git a/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.m b/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.m index d389764f..14b98a35 100644 --- a/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.m +++ b/Pod/BVNotifications/BVStoreNotificationConfigurationLoader.m @@ -5,127 +5,146 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // - #import "BVStoreNotificationConfigurationLoader.h" -#import "BVNotificationConfiguration.h" #import "BVLocationManager.h" +#import "BVNotificationConfiguration.h" #import "BVPlaceAttributes.h" -#import "BVVisit.h" +#import "BVSDKConfiguration.h" #import "BVSDKManager.h" #import "BVStoreReviewNotificationCenter.h" -#import "BVSDKConfiguration.h" +#import "BVVisit.h" -@interface BVStoreNotificationConfigurationLoader() +@interface BVStoreNotificationConfigurationLoader () @end @implementation BVStoreNotificationConfigurationLoader -+(void)load{ - [self sharedManager]; ++ (void)load { + [self sharedManager]; } -+(id)sharedManager { - static BVStoreNotificationConfigurationLoader *shared; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - shared = [[self alloc]init]; - }); - - return shared; ++ (id)sharedManager { + static BVStoreNotificationConfigurationLoader *shared; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + shared = [[self alloc] init]; + }); + + return shared; } - (id)init { - if (self = [super init]) { - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(receiveConversationsStoreAPIKey:) - name:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION - object:nil]; - - [BVLocationManager registerForLocationUpdates:self]; - } - - return self; -} + if (self = [super init]) { + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveConversationsStoreAPIKey:) + name:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION + object:nil]; + [BVLocationManager registerForLocationUpdates:self]; + } -- (void) receiveConversationsStoreAPIKey:(NSNotification *) notification -{ - // [notification name] should always be CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION - // unless you use this method for observation of other notifications - // as well. - - if ([[notification name] isEqualToString:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION]){ - - [[BVLogger sharedLogger] verbose:@"Recieved notifcation for conversations stores configuration"]; - - [self loadStoreNotificationConfiguration:^(BVStoreReviewNotificationProperties * _Nonnull response) { - // success - } failure:^(NSError * _Nonnull error) { - // failed - }]; - - } + return self; } --(BOOL)isClientConfiguredForPush:(BVSDKManager *)sdkMgr { - - BVStoreReviewNotificationProperties *storeNotificationProps = self.bvStoreReviewNotificationProperties; - - return [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] && - sdkMgr.configuration.apiKeyConversationsStores && - sdkMgr.configuration.storeReviewContentExtensionCategory && - storeNotificationProps && - storeNotificationProps.notificationsEnabled; +- (void)receiveConversationsStoreAPIKey:(NSNotification *)notification { + // [notification name] should always be + // CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION + // unless you use this method for observation of other notifications + // as well. + + if ([[notification name] + isEqualToString:CONVERSATIONS_STORES_API_KEY_SET_NOTIFICATION]) { + [[BVLogger sharedLogger] + verbose:@"Recieved notifcation for conversations stores configuration"]; + + [self loadStoreNotificationConfiguration:^( + BVStoreReviewNotificationProperties *__nonnull response) { + // success + } + failure:^(NSError *__nonnull error){ + // failed + }]; + } } +- (BOOL)isClientConfiguredForPush:(BVSDKManager *)sdkMgr { + BVStoreReviewNotificationProperties *storeNotificationProps = + self.bvStoreReviewNotificationProperties; + return + [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] && + sdkMgr.configuration.apiKeyConversationsStores && + sdkMgr.configuration.storeReviewContentExtensionCategory && + storeNotificationProps && storeNotificationProps.notificationsEnabled; +} --(void)loadStoreNotificationConfiguration:(void (^ _Nonnull)(BVStoreReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull error))failure { - - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/%@/conversations-stores/geofenceConfig.json", NOTIFICATION_CONFIG_ROOT, S3_API_VERSION, [BVSDKManager sharedManager].configuration.clientId]]; - [BVNotificationConfiguration loadGeofenceConfiguration:url completion:^(BVStoreReviewNotificationProperties * _Nonnull response) { - [[BVLogger sharedLogger] verbose:@"Successfully loaded BVStoreReviewNotificationProperties"]; +- (void) +loadStoreNotificationConfiguration: + (nonnull void (^)(BVStoreReviewNotificationProperties *__nonnull response)) + completion + failure:(nonnull void (^)(NSError *__nonnull error)) + failure { + NSURL *url = [NSURL + URLWithString:[NSString stringWithFormat:@"%@/incubator-mobile-apps/" + @"sdk/%@/ios/%@/" + @"conversations-stores/" + @"geofenceConfig.json", + NOTIFICATION_CONFIG_ROOT, + S3_API_VERSION, + [BVSDKManager sharedManager] + .configuration.clientId]]; + [BVNotificationConfiguration loadGeofenceConfiguration:url + completion:^(BVStoreReviewNotificationProperties *__nonnull response) { + [[BVLogger sharedLogger] + verbose:@"Successfully loaded BVStoreReviewNotificationProperties"]; _bvStoreReviewNotificationProperties = response; completion(response); - - } failure:^(NSError * _Nonnull error) { - [[BVLogger sharedLogger] error:@"ERROR: Failed to load BVStoreReviewNotificationProperties"]; + + } + failure:^(NSError *__nonnull error) { + [[BVLogger sharedLogger] + error:@"ERROR: Failed to load BVStoreReviewNotificationProperties"]; failure(error); - }]; - + }]; } #pragma mark BVLocationManagerDelegate --(void)didBeginVisit:(BVVisit* _Nonnull)visit { - [self receiveStoreVisitNotification:visit]; +- (void)didBeginVisit:(nonnull BVVisit *)visit { + [self receiveStoreVisitNotification:visit]; } - --(void)didEndVisit:(BVVisit* _Nonnull)visit { - [self receiveStoreVisitNotification:visit]; +- (void)didEndVisit:(nonnull BVVisit *)visit { + [self receiveStoreVisitNotification:visit]; } -- (void) receiveStoreVisitNotification:(BVVisit * )visit { - BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; - if ([self isClientConfiguredForPush:sdkMgr]) - { - // Check and make sure the visit time has been met before trying to queue a notification - BVStoreReviewNotificationProperties *noteProps = [[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties]; - - NSTimeInterval visitDuration = [visit.departureDate timeIntervalSinceDate:visit.arrivalDate]; - - if (visitDuration >= noteProps.visitDuration){ - - // queue up the notification.... - [[BVStoreReviewNotificationCenter sharedCenter] queueReviewWithStoreId:visit.storeId]; - - } else { - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"Vist time of %f, not long enough to post notification. Need %f seconds.", visitDuration, noteProps.visitDuration]]; - } +- (void)receiveStoreVisitNotification:(BVVisit *)visit { + BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; + if ([self isClientConfiguredForPush:sdkMgr]) { + // Check and make sure the visit time has been met before trying to + // queue a notification + BVStoreReviewNotificationProperties *noteProps = + [[BVStoreNotificationConfigurationLoader sharedManager] + bvStoreReviewNotificationProperties]; + + NSTimeInterval visitDuration = + [visit.departureDate timeIntervalSinceDate:visit.arrivalDate]; + + if (visitDuration >= noteProps.visitDuration) { + // queue up the notification.... + [[BVStoreReviewNotificationCenter sharedCenter] + queueReviewWithStoreId:visit.storeId]; + + } else { + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"Vist time of %f, not long " + @"enough to post " + @"notification. Need %f " + @"seconds.", + visitDuration, + noteProps.visitDuration]]; } - + } } @end diff --git a/Pod/BVNotifications/BVStoreReviewNotificationCenter.h b/Pod/BVNotifications/BVStoreReviewNotificationCenter.h index e25a9ea5..65a7ca94 100644 --- a/Pod/BVNotifications/BVStoreReviewNotificationCenter.h +++ b/Pod/BVNotifications/BVStoreReviewNotificationCenter.h @@ -6,13 +6,14 @@ // // -#import "BVNotificationConstants.h" #import "BVNotificationCenterObject.h" +#import "BVNotificationConstants.h" -@interface BVStoreReviewNotificationCenter : NSObject +@interface BVStoreReviewNotificationCenter + : NSObject --(id _Null_unspecified)init __attribute__((unavailable("Use sharedCenter"))); +- (id _Null_unspecified)init __attribute__((unavailable("Use sharedCenter"))); -+(instancetype _Nonnull)sharedCenter; ++ (nonnull instancetype)sharedCenter; @end diff --git a/Pod/BVNotifications/BVStoreReviewNotificationCenter.m b/Pod/BVNotifications/BVStoreReviewNotificationCenter.m index 0150d66e..1286ae3e 100644 --- a/Pod/BVNotifications/BVStoreReviewNotificationCenter.m +++ b/Pod/BVNotifications/BVStoreReviewNotificationCenter.m @@ -12,20 +12,22 @@ @implementation BVStoreReviewNotificationCenter -+(instancetype _Nonnull)sharedCenter; ++ (nonnull instancetype)sharedCenter; { - static dispatch_once_t p = 0; - - __strong static id _sharedObject = nil; - dispatch_once(&p, ^{ - if ([[[UIDevice currentDevice] systemVersion] compare:@"10.0" options:NSNumericSearch] != NSOrderedAscending) { - _sharedObject = [[BVStoreReviewRichNotificationCenter alloc] init]; - }else { - _sharedObject = [[BVStoreReviewSimpleNotificationCenter alloc] init]; - } - }); - - return _sharedObject; + static dispatch_once_t p = 0; + + __strong static id _sharedObject = nil; + dispatch_once(&p, ^{ + if ([[[UIDevice currentDevice] systemVersion] + compare:@"10.0" + options:NSNumericSearch] != NSOrderedAscending) { + _sharedObject = [[BVStoreReviewRichNotificationCenter alloc] init]; + } else { + _sharedObject = [[BVStoreReviewSimpleNotificationCenter alloc] init]; + } + }); + + return _sharedObject; } @end diff --git a/Pod/BVNotifications/BVStoreReviewNotificationProperties.h b/Pod/BVNotifications/BVStoreReviewNotificationProperties.h index 322de039..631251dd 100644 --- a/Pod/BVNotifications/BVStoreReviewNotificationProperties.h +++ b/Pod/BVNotifications/BVStoreReviewNotificationProperties.h @@ -6,11 +6,11 @@ // // -#import #import "BVNotificationProperties.h" +#import @interface BVStoreReviewNotificationProperties : BVNotificationProperties -@property (nonatomic, strong, readonly) NSString *defaultStoreImageUrl; +@property(nonatomic, strong, readonly) NSString *defaultStoreImageUrl; @end diff --git a/Pod/BVNotifications/BVStoreReviewNotificationProperties.m b/Pod/BVNotifications/BVStoreReviewNotificationProperties.m index c1778e5d..a1ee55ce 100644 --- a/Pod/BVNotifications/BVStoreReviewNotificationProperties.m +++ b/Pod/BVNotifications/BVStoreReviewNotificationProperties.m @@ -11,23 +11,23 @@ @implementation BVStoreReviewNotificationProperties -- (id)init{ - - self = [super init]; - - // TODO: Suitable defaults if not remote config? - - return self; +- (id)init { + + self = [super init]; + + // TODO: Suitable defaults if not remote config? + + return self; } +- (id)initWithDictionary:(NSDictionary *)configDict { + + self = [super initWithDictionary:configDict]; + + SET_IF_NOT_NULL(_defaultStoreImageUrl, + [configDict objectForKey:@"defaultStoreImageUrl"]); -- (id)initWithDictionary:(NSDictionary *)configDict{ - - self = [super initWithDictionary:configDict]; - - SET_IF_NOT_NULL(_defaultStoreImageUrl, [configDict objectForKey:@"defaultStoreImageUrl"]); - - return self; + return self; } @end diff --git a/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.h b/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.h index 16a885b3..2fee80e3 100644 --- a/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.h +++ b/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.h @@ -6,9 +6,11 @@ // // -#import #import "BVNotificationCenterObject.h" +#import -@interface BVStoreReviewRichNotificationCenter : NSObject +@interface BVStoreReviewRichNotificationCenter + : NSObject @end diff --git a/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.m b/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.m index cab61430..e6ee13fc 100644 --- a/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.m +++ b/Pod/BVNotifications/BVStoreReviewRichNotificationCenter.m @@ -7,171 +7,248 @@ // #import "BVStoreReviewRichNotificationCenter.h" -#import "BVNotificationConstants.h" -#import "BVLogger.h" -#import "BVSDKManager.h" -#import "BVProductDisplayPageRequest.h" #import "BVBulkStoreItemsRequest.h" -#import +#import "BVLogger.h" #import "BVNotificationConstants.h" #import "BVNotificationsAnalyticsHelper.h" -#import "BVNotificationConstants.h" -#import "BVStoreNotificationConfigurationLoader.h" +#import "BVProductDisplayPageRequest.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVStoreNotificationConfigurationLoader.h" +#import @implementation BVStoreReviewRichNotificationCenter --(id)init { - self = [super init]; - - if (self) { - NSFileManager *mngr = [NSFileManager defaultManager]; - BOOL isDir; - if (![mngr fileExistsAtPath:ATTACHMENT_FILE_DIRECTORY isDirectory:&isDir]) { - [mngr createDirectoryAtPath:ATTACHMENT_FILE_DIRECTORY withIntermediateDirectories:NO attributes:nil error:nil]; - } +- (id)init { + self = [super init]; + + if (self) { + NSFileManager *mngr = [NSFileManager defaultManager]; + BOOL isDir; + if (![mngr fileExistsAtPath:ATTACHMENT_FILE_DIRECTORY isDirectory:&isDir]) { + [mngr createDirectoryAtPath:ATTACHMENT_FILE_DIRECTORY + withIntermediateDirectories:NO + attributes:nil + error:nil]; } - - return self; + } + + return self; } -- (void)addNotificationCategories:(BVStoreReviewNotificationProperties *)notificationProperties { - - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - UNNotificationAction *replyAction = [UNNotificationAction actionWithIdentifier:ID_REPLY title:notificationProperties.reviewPromptYesReview options:0]; - UNNotificationAction *remindAction = [UNNotificationAction actionWithIdentifier:ID_REMIND title:notificationProperties.reviewPromptRemindText options:0]; - UNNotificationAction *dismissAction = [UNNotificationAction actionWithIdentifier:ID_DISMISS title:notificationProperties.reviewPromtNoReview options:0]; - - NSArray *notificationActions = @[ replyAction, remindAction, dismissAction ]; - - UNNotificationCategory *reviewStoreCategory = [UNNotificationCategory categoryWithIdentifier: STORE_NOTIFICATION_CUSTOM_CATEGORY actions:notificationActions intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; - - NSSet *categories = [NSSet setWithObject:reviewStoreCategory]; - - [center setNotificationCategories:categories]; +- (void)addNotificationCategories: + (BVStoreReviewNotificationProperties *)notificationProperties { + UNUserNotificationCenter *center = + [UNUserNotificationCenter currentNotificationCenter]; + UNNotificationAction *replyAction = [UNNotificationAction + actionWithIdentifier:ID_REPLY + title:notificationProperties.reviewPromptYesReview + options:0]; + UNNotificationAction *remindAction = [UNNotificationAction + actionWithIdentifier:ID_REMIND + title:notificationProperties.reviewPromptRemindText + options:0]; + UNNotificationAction *dismissAction = [UNNotificationAction + actionWithIdentifier:ID_DISMISS + title:notificationProperties.reviewPromtNoReview + options:0]; + + NSArray *notificationActions = @[ replyAction, remindAction, dismissAction ]; + + UNNotificationCategory *reviewStoreCategory = [UNNotificationCategory + categoryWithIdentifier:STORE_NOTIFICATION_CUSTOM_CATEGORY + actions:notificationActions + intentIdentifiers:@[] + options:UNNotificationCategoryOptionCustomDismissAction]; + + NSSet *categories = [NSSet setWithObject:reviewStoreCategory]; + + [center setNotificationCategories:categories]; } -- (void)loadStoreInfo:(NSString*)storeId completion:(void (^_Nonnull)(BVStore * _Nullable store))completion{ - - BVBulkStoreItemsRequest *request = [[BVBulkStoreItemsRequest alloc] initWithStoreIds:@[storeId]]; - [request load:^(BVBulkStoresResponse * _Nonnull response) { - - BVStore* store = response.results.firstObject; - completion(store); - - } failure:^(NSArray * _Nonnull errors) { +- (void)loadStoreInfo:(NSString *)storeId + completion:(nonnull void (^)(BVStore *__nullable store))completion { + BVBulkStoreItemsRequest *request = + [[BVBulkStoreItemsRequest alloc] initWithStoreIds:@[ storeId ]]; + [request load:^(BVBulkStoresResponse *__nonnull response) { + + BVStore *store = response.results.firstObject; + completion(store); + + } + failure:^(NSArray *__nonnull errors) { [[BVLogger sharedLogger] printErrors:errors]; - }]; + }]; } --(UNNotificationAttachment *)createMapSnapshot:(BVStore *)store{ - BVStoreReviewNotificationProperties *noteProps = [[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties]; - - if (noteProps == nil) { - [[BVLogger sharedLogger] error:@"Unable to create notification with nil BVStoreReviewNotificationProperties. Bad network connection or missing properties config?"]; - return nil; - } +- (UNNotificationAttachment *)createMapSnapshot:(BVStore *)store { + BVStoreReviewNotificationProperties *noteProps = + [[BVStoreNotificationConfigurationLoader sharedManager] + bvStoreReviewNotificationProperties]; - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - UIImage *image = [UIImage imageNamed:@"mapThumbnail" inBundle:bundle compatibleWithTraitCollection:nil]; - NSError *error; - NSData *data = UIImagePNGRepresentation(image); - NSURL *fileUrl = [NSURL fileURLWithPath:ATTACHMENT_FILE_PATH]; - [data writeToURL:fileUrl options:0 error:nil]; - UNNotificationAttachment *imgAttachment = [UNNotificationAttachment attachmentWithIdentifier:ATTACHMENT_THUMB_ID URL:fileUrl options:nil error: &error]; - - return imgAttachment; + if (noteProps == nil) { + [[BVLogger sharedLogger] + error:@"Unable to create notification with nil " + @"BVStoreReviewNotificationProperties. Bad network " + @"connection or missing properties config?"]; + return nil; + } + + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + UIImage *image = [UIImage imageNamed:@"mapThumbnail" + inBundle:bundle + compatibleWithTraitCollection:nil]; + NSError *error; + NSData *data = UIImagePNGRepresentation(image); + NSURL *fileUrl = [NSURL fileURLWithPath:ATTACHMENT_FILE_PATH]; + [data writeToURL:fileUrl options:0 error:nil]; + UNNotificationAttachment *imgAttachment = + [UNNotificationAttachment attachmentWithIdentifier:ATTACHMENT_THUMB_ID + URL:fileUrl + options:nil + error:&error]; + + return imgAttachment; } --(void)rescheduleNotification:(NSDictionary *)productDict includes:(NSDictionary *)includes noteProps:(BVStoreReviewNotificationProperties *)noteProps { - BVStore *store = [[BVStore alloc] initWithApiResponse:productDict]; - MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(store.storeLocation.latitude.floatValue, - store.storeLocation.longitude.floatValue), - MKCoordinateSpanMake(0.003, 0.003)); - UNNotificationAttachment *attachment = [self createMapSnapshot:store]; - [self scheduleRichNotification:noteProps attachments:@[attachment] store:store region:region]; +- (void)rescheduleNotification:(NSDictionary *)productDict + includes:(NSDictionary *)includes + noteProps: + (BVStoreReviewNotificationProperties *)noteProps { + BVStore *store = [[BVStore alloc] initWithApiResponse:productDict]; + MKCoordinateRegion region = MKCoordinateRegionMake( + CLLocationCoordinate2DMake(store.storeLocation.latitude.floatValue, + store.storeLocation.longitude.floatValue), + MKCoordinateSpanMake(0.003, 0.003)); + UNNotificationAttachment *attachment = [self createMapSnapshot:store]; + [self scheduleRichNotification:noteProps + attachments:@[ attachment ] + store:store + region:region]; } -- (void)scheduleRichNotification:(BVStoreReviewNotificationProperties *)noteProps attachments:(NSArray *)attachments store:(BVStore*)store region:(MKCoordinateRegion)region { - [self addNotificationCategories:noteProps]; - NSTimeInterval delay = noteProps.notificationDelay; - - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.title = [NSString localizedUserNotificationStringForKey:noteProps.reviewPromtDispayText arguments:nil]; - content.body = [NSString localizedUserNotificationStringForKey:noteProps.reviewPromptSubtitleText - arguments:nil]; - content.categoryIdentifier = STORE_NOTIFICATION_CUSTOM_CATEGORY; - content.attachments = attachments; - content.userInfo = @{USER_INFO_LAT: @(region.center.latitude), USER_INFO_LONG: @(region.center.longitude), - USER_INFO_LAT_DELTA: @(region.span.latitudeDelta), USER_INFO_LONG_DELTA: @(region.span.longitudeDelta), - USER_INFO_PROD_ID: store.identifier, - USER_INFO_PROD_NAME: store.name, - USER_INFO_PROD_IMAGE_URL: store.imageUrl, - USER_INFO_URL_SCHEME: noteProps.customUrlScheme, - USER_INFO_API_KEY_CONVERSATIONS: [BVSDKManager sharedManager].configuration.apiKeyConversationsStores, - USER_INFO_CLIENTID: [BVSDKManager sharedManager].configuration.clientId, - USER_INFO_CONFIG_PROPS: [[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties].configDict}; - - UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay repeats:NO]; - - UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"com.bazaarvoice.store.review" - content:content trigger:trigger]; - - [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { - if (!error) { - NSLog(@"add NotificationRequest succeeded!"); - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewNotificationInView:@"StoreReviewPushNotification" withId:store.identifier andProductType:ProductTypeStore]; - } - }]; +- (void) +scheduleRichNotification:(BVStoreReviewNotificationProperties *)noteProps + attachments:(NSArray *)attachments + store:(BVStore *)store + region:(MKCoordinateRegion)region { + [self addNotificationCategories:noteProps]; + NSTimeInterval delay = noteProps.notificationDelay; + + UNUserNotificationCenter *center = + [UNUserNotificationCenter currentNotificationCenter]; + + UNMutableNotificationContent *content = + [[UNMutableNotificationContent alloc] init]; + content.title = [NSString + localizedUserNotificationStringForKey:noteProps.reviewPromtDispayText + arguments:nil]; + content.body = [NSString + localizedUserNotificationStringForKey:noteProps.reviewPromptSubtitleText + arguments:nil]; + content.categoryIdentifier = STORE_NOTIFICATION_CUSTOM_CATEGORY; + content.attachments = attachments; + content.userInfo = @{ + USER_INFO_LAT : @(region.center.latitude), + USER_INFO_LONG : @(region.center.longitude), + USER_INFO_LAT_DELTA : @(region.span.latitudeDelta), + USER_INFO_LONG_DELTA : @(region.span.longitudeDelta), + USER_INFO_PROD_ID : store.identifier, + USER_INFO_PROD_NAME : store.name, + USER_INFO_PROD_IMAGE_URL : store.imageUrl, + USER_INFO_URL_SCHEME : noteProps.customUrlScheme, + USER_INFO_API_KEY_CONVERSATIONS : [BVSDKManager sharedManager] + .configuration.apiKeyConversationsStores, + USER_INFO_CLIENTID : [BVSDKManager sharedManager].configuration.clientId, + USER_INFO_CONFIG_PROPS : + [[BVStoreNotificationConfigurationLoader sharedManager] + bvStoreReviewNotificationProperties] + .configDict + }; + + UNTimeIntervalNotificationTrigger *trigger = + [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:delay + repeats:NO]; + + UNNotificationRequest *request = [UNNotificationRequest + requestWithIdentifier:@"com.bazaarvoice.store.review" + content:content + trigger:trigger]; + + [center + addNotificationRequest:request + withCompletionHandler:^(NSError *__nullable error) { + if (!error) { + NSLog(@"add NotificationRequest succeeded!"); + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewNotificationInView: + @"StoreReviewPushNotification" + withId:store.identifier + andProductType:ProductTypeStore]; + } + }]; } - (void)queueReviewWithStoreId:(nonnull NSString *)storeId { - - [self loadStoreInfo:storeId completion:^(BVStore * _Nullable store) { - if (store) { - [self queueReviewWithStore:store]; - } - }]; + [self loadStoreInfo:storeId + completion:^(BVStore *__nullable store) { + if (store) { + [self queueReviewWithStore:store]; + } + }]; } - (void)queueReviewWithStore:(nonnull BVStore *)store { - MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(store.storeLocation.latitude.floatValue, - store.storeLocation.longitude.floatValue), - MKCoordinateSpanMake(0.003, 0.003)); - UNNotificationAttachment *attachment = [self createMapSnapshot:store]; - if (attachment) { - [self scheduleRichNotification:[[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties] attachments:@[attachment] store:store region:region]; - } + MKCoordinateRegion region = MKCoordinateRegionMake( + CLLocationCoordinate2DMake(store.storeLocation.latitude.floatValue, + store.storeLocation.longitude.floatValue), + MKCoordinateSpanMake(0.003, 0.003)); + UNNotificationAttachment *attachment = [self createMapSnapshot:store]; + if (attachment) { + [self scheduleRichNotification:[[BVStoreNotificationConfigurationLoader + sharedManager] + bvStoreReviewNotificationProperties] + attachments:@[ attachment ] + store:store + region:region]; + } } -- (void)userNotificationCenter:(UNUserNotificationCenter* _Nonnull)center didReceiveNotificationResponse:(UNNotificationResponse * _Nonnull)response { - - NSString *storeId = response.notification.request.content.userInfo[USER_INFO_PROD_ID]; - - if ([[response actionIdentifier] isEqualToString:ID_REPLY] || - [response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]){ - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REPLY withId:storeId andProductType:ProductTypeStore]; - - } else if ([[response actionIdentifier] isEqualToString:ID_REMIND]){ - - //not yet handled; should maybe reschedule - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REMIND withId:storeId andProductType:ProductTypeStore]; - [self queueReviewWithStoreId:storeId]; - - } else if ([[response actionIdentifier] isEqualToString:ID_DISMISS] || - [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]){ - - // User cancelled. Client can determine whether or not to cache this store to not show again. - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_DISMISS withId:storeId andProductType:ProductTypeStore]; - } - +- (void)userNotificationCenter:(nonnull UNUserNotificationCenter *)center + didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response { + NSString *storeId = + response.notification.request.content.userInfo[USER_INFO_PROD_ID]; + + if ([[response actionIdentifier] isEqualToString:ID_REPLY] || + [response.actionIdentifier + isEqualToString:UNNotificationDefaultActionIdentifier]) { + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REPLY + withId:storeId + andProductType:ProductTypeStore]; + + } else if ([[response actionIdentifier] isEqualToString:ID_REMIND]) { + // not yet handled; should maybe reschedule + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REMIND + withId:storeId + andProductType:ProductTypeStore]; + [self queueReviewWithStoreId:storeId]; + + } else if ([[response actionIdentifier] isEqualToString:ID_DISMISS] || + [response.actionIdentifier + isEqualToString:UNNotificationDismissActionIdentifier]) { + // User cancelled. Client can determine whether or not to cache this + // store to not show again. + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_DISMISS + withId:storeId + andProductType:ProductTypeStore]; + } } -- (void)handleActionWithIdentifier:(NSString * _Nullable)identifier forLocalNotification:(UILocalNotification * _Nonnull)notification { - +- (void)handleActionWithIdentifier:(nullable NSString *)identifier + forLocalNotification:(nonnull UILocalNotification *)notification { } @end diff --git a/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.h b/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.h index ebdc1c1f..0fdc5ade 100644 --- a/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.h +++ b/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.h @@ -6,9 +6,10 @@ // // -#import #import "BVNotificationCenterObject.h" +#import -@interface BVStoreReviewSimpleNotificationCenter : NSObject +@interface BVStoreReviewSimpleNotificationCenter + : NSObject @end diff --git a/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.m b/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.m index 51c6690c..959bc238 100644 --- a/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.m +++ b/Pod/BVNotifications/BVStoreReviewSimpleNotificationCenter.m @@ -7,104 +7,137 @@ // #import "BVStoreReviewSimpleNotificationCenter.h" -#import "BVStoreReviewNotificationProperties.h" #import "BVBulkStoreItemsRequest.h" -#import "BVSDKManager.h" -#import "BVNotificationsAnalyticsHelper.h" -#import -#import "BVNotificationConstants.h" #import "BVConversationsInclude.h" -#import "BVStoreNotificationConfigurationLoader.h" +#import "BVNotificationConstants.h" +#import "BVNotificationsAnalyticsHelper.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" +#import "BVStoreNotificationConfigurationLoader.h" +#import "BVStoreReviewNotificationProperties.h" +#import @implementation BVStoreReviewSimpleNotificationCenter -- (void)userNotificationCenter:(UNUserNotificationCenter* _Nonnull)center didReceiveNotificationResponse:(UNNotificationResponse * _Nonnull)response { - +- (void)userNotificationCenter:(nonnull UNUserNotificationCenter *)center + didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response { } -- (void)handleActionWithIdentifier:(NSString * _Nullable)identifier forLocalNotification:(UILocalNotification * _Nonnull)notification { - NSString *storeId = notification.userInfo[USER_INFO_PROD_ID]; - - if ([identifier isEqualToString:ID_REPLY]){ - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REPLY withId:storeId andProductType:ProductTypeStore]; - } else if ([identifier isEqualToString:ID_REMIND]){ - //not yet handled; should maybe reschedule - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_REMIND withId:storeId andProductType:ProductTypeStore]; - } else if ([identifier isEqualToString:ID_DISMISS]){ - //not yet handled; should persist decision to not review and not prompt again - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:ID_DISMISS withId:storeId andProductType:ProductTypeStore]; - } - +- (void)handleActionWithIdentifier:(nullable NSString *)identifier + forLocalNotification:(nonnull UILocalNotification *)notification { + NSString *storeId = notification.userInfo[USER_INFO_PROD_ID]; + + if ([identifier isEqualToString:ID_REPLY]) { + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REPLY + withId:storeId + andProductType:ProductTypeStore]; + } else if ([identifier isEqualToString:ID_REMIND]) { + // not yet handled; should maybe reschedule + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_REMIND + withId:storeId + andProductType:ProductTypeStore]; + } else if ([identifier isEqualToString:ID_DISMISS]) { + // not yet handled; should persist decision to not review and not prompt + // again + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:ID_DISMISS + withId:storeId + andProductType:ProductTypeStore]; + } } - (void)queueReviewWithStoreId:(nonnull NSString *)storeId { - [self loadStoreInfo:storeId completion:^(BVStore *store) { - if (store) { - [self queueReviewWithStore:store]; - } - }]; + [self loadStoreInfo:storeId + completion:^(BVStore *store) { + if (store) { + [self queueReviewWithStore:store]; + } + }]; } -- (void)loadStoreInfo:(NSString*)storeId completion:(void (^_Nonnull)(BVStore * _Nullable store))completion{ - - BVBulkStoreItemsRequest *request = [[BVBulkStoreItemsRequest alloc] initWithStoreIds:@[storeId]]; - [request load:^(BVBulkStoresResponse * _Nonnull response) { - - BVStore* store = response.results.firstObject; - completion(store); - - } failure:^(NSArray * _Nonnull errors) { +- (void)loadStoreInfo:(NSString *)storeId + completion:(nonnull void (^)(BVStore *__nullable store))completion { + BVBulkStoreItemsRequest *request = + [[BVBulkStoreItemsRequest alloc] initWithStoreIds:@[ storeId ]]; + [request load:^(BVBulkStoresResponse *__nonnull response) { + + BVStore *store = response.results.firstObject; + completion(store); + + } + failure:^(NSArray *__nonnull errors) { [[BVLogger sharedLogger] printErrors:errors]; - }]; + }]; } - (void)queueReviewWithStore:(nonnull BVStore *)store { - [self scheduleNotification:[[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties] store:store]; + [self scheduleNotification:[[BVStoreNotificationConfigurationLoader + sharedManager] + bvStoreReviewNotificationProperties] + store:store]; } --(void)rescheduleNotification:(NSDictionary *)productDict includes:(NSDictionary *)includes noteProps:(BVStoreReviewNotificationProperties *)noteProps { - BVStore *store = [[BVStore alloc] initWithApiResponse:productDict]; - [self scheduleNotification:noteProps store:store]; +- (void)rescheduleNotification:(NSDictionary *)productDict + includes:(NSDictionary *)includes + noteProps: + (BVStoreReviewNotificationProperties *)noteProps { + BVStore *store = [[BVStore alloc] initWithApiResponse:productDict]; + [self scheduleNotification:noteProps store:store]; } --(void)scheduleNotification:(BVStoreReviewNotificationProperties *)noteProps store:(BVStore*)store { - [self addNotificationCategories:noteProps]; - - NSTimeInterval delay = noteProps.notificationDelay; - - UILocalNotification *notification = [[UILocalNotification alloc]init]; - notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:delay]; - notification.alertBody = noteProps.reviewPromtDispayText; - notification.category = STORE_NOTIFICATION_CUSTOM_CATEGORY; - notification.userInfo = @{USER_INFO_PROD_ID: store.identifier, USER_INFO_PROD_NAME: store.name, USER_INFO_PROD_IMAGE_URL: store.imageUrl, USER_INFO_URL_SCHEME: noteProps.customUrlScheme}; - [[UIApplication sharedApplication] scheduleLocalNotification:notification]; +- (void)scheduleNotification:(BVStoreReviewNotificationProperties *)noteProps + store:(BVStore *)store { + [self addNotificationCategories:noteProps]; + + NSTimeInterval delay = noteProps.notificationDelay; + + UILocalNotification *notification = [[UILocalNotification alloc] init]; + notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:delay]; + notification.alertBody = noteProps.reviewPromtDispayText; + notification.category = STORE_NOTIFICATION_CUSTOM_CATEGORY; + notification.userInfo = @{ + USER_INFO_PROD_ID : store.identifier, + USER_INFO_PROD_NAME : store.name, + USER_INFO_PROD_IMAGE_URL : store.imageUrl, + USER_INFO_URL_SCHEME : noteProps.customUrlScheme + }; + [[UIApplication sharedApplication] scheduleLocalNotification:notification]; } -- (void)addNotificationCategories:(BVStoreReviewNotificationProperties * _Nonnull)notificationProperties { - UIUserNotificationType type = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound; - UIMutableUserNotificationAction *replyAction = [UIMutableUserNotificationAction new]; - replyAction.identifier = ID_REPLY; - replyAction.title = notificationProperties.reviewPromptYesReview; - replyAction.activationMode = UIUserNotificationActivationModeForeground; - replyAction.destructive = NO; - replyAction.authenticationRequired = NO; - - UIMutableUserNotificationAction *noAction = [UIMutableUserNotificationAction new]; - noAction.identifier = ID_DISMISS; - noAction.title = notificationProperties.reviewPromtNoReview; - noAction.activationMode = UIUserNotificationActivationModeBackground; - noAction.destructive = NO; - noAction.authenticationRequired = NO; - - UIMutableUserNotificationCategory *category = [UIMutableUserNotificationCategory new]; - category.identifier = STORE_NOTIFICATION_CUSTOM_CATEGORY; - [category setActions:@[replyAction, noAction] forContext:UIUserNotificationActionContextDefault]; - - NSSet *cats = [NSSet setWithObjects:category, nil]; - - UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:cats]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; +- (void)addNotificationCategories: + (nonnull BVStoreReviewNotificationProperties *)notificationProperties { + UIUserNotificationType type = UIUserNotificationTypeAlert | + UIUserNotificationTypeBadge | + UIUserNotificationTypeSound; + UIMutableUserNotificationAction *replyAction = + [UIMutableUserNotificationAction new]; + replyAction.identifier = ID_REPLY; + replyAction.title = notificationProperties.reviewPromptYesReview; + replyAction.activationMode = UIUserNotificationActivationModeForeground; + replyAction.destructive = NO; + replyAction.authenticationRequired = NO; + + UIMutableUserNotificationAction *noAction = + [UIMutableUserNotificationAction new]; + noAction.identifier = ID_DISMISS; + noAction.title = notificationProperties.reviewPromtNoReview; + noAction.activationMode = UIUserNotificationActivationModeBackground; + noAction.destructive = NO; + noAction.authenticationRequired = NO; + + UIMutableUserNotificationCategory *category = + [UIMutableUserNotificationCategory new]; + category.identifier = STORE_NOTIFICATION_CUSTOM_CATEGORY; + [category setActions:@[ replyAction, noAction ] + forContext:UIUserNotificationActionContextDefault]; + + NSSet *cats = [NSSet setWithObjects:category, nil]; + + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:type categories:cats]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } @end diff --git a/Pod/BVPIN/BVPIN.h b/Pod/BVPIN/BVPIN.h index c173a9a2..0a957605 100644 --- a/Pod/BVPIN/BVPIN.h +++ b/Pod/BVPIN/BVPIN.h @@ -6,14 +6,15 @@ // // -#import #import "BVDisplayableProductContent.h" +#import -@interface BVPIN : NSObject +@interface BVPIN : NSObject - (instancetype)init NS_UNAVAILABLE; --(instancetype)initWithDictionary:(NSDictionary*)dictionary NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithDictionary:(NSDictionary *)dictionary + NS_DESIGNATED_INITIALIZER; @property(nonatomic, strong, readonly) NSNumber *averageRating; diff --git a/Pod/BVPIN/BVPIN.m b/Pod/BVPIN/BVPIN.m index 7fb3b9bc..bf5b016e 100644 --- a/Pod/BVPIN/BVPIN.m +++ b/Pod/BVPIN/BVPIN.m @@ -13,24 +13,24 @@ @implementation BVPIN @synthesize displayImageUrl; @synthesize identifier = _identifier; --(instancetype)initWithDictionary:(NSDictionary *)dictionary { - if (self = [super init]) { - _averageRating = dictionary[@"avg_rating"]; - _imageUrl = dictionary[@"image_url"]; - _productPageURL = dictionary[@"product_page_url"]; - _name = dictionary[@"name"]; - _identifier = dictionary[@"id"]; - } - - return self; +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + if (self = [super init]) { + _averageRating = dictionary[@"avg_rating"]; + _imageUrl = dictionary[@"image_url"]; + _productPageURL = dictionary[@"product_page_url"]; + _name = dictionary[@"name"]; + _identifier = dictionary[@"id"]; + } + + return self; } --(NSString*)displayName { - return _name; +- (NSString *)displayName { + return _name; } --(NSString*)displayImageUrl { - return _imageUrl; +- (NSString *)displayImageUrl { + return _imageUrl; } @end diff --git a/Pod/BVPIN/BVPINRequest.h b/Pod/BVPIN/BVPINRequest.h index fdb1baaa..8bb8796b 100644 --- a/Pod/BVPIN/BVPINRequest.h +++ b/Pod/BVPIN/BVPINRequest.h @@ -6,11 +6,13 @@ // // -#import #import "BVPIN.h" +#import @interface BVPINRequest : NSObject -+(void)getPendingPINs:(void(^ _Nonnull)(NSArray * _Nonnull pins))completion failure:(void(^ _Nonnull)(NSError * _Nonnull))failure; ++ (void)getPendingPINs: + (nonnull void (^)(NSArray *__nonnull pins))completion + failure:(nonnull void (^)(NSError *__nonnull))failure; @end diff --git a/Pod/BVPIN/BVPINRequest.m b/Pod/BVPIN/BVPINRequest.m index 2f1c3b9d..1988ea45 100644 --- a/Pod/BVPIN/BVPINRequest.m +++ b/Pod/BVPIN/BVPINRequest.m @@ -8,59 +8,84 @@ #import "BVPINRequest.h" #import "BVLogger.h" +#import "BVSDKConfiguration.h" #import "BVSDKManager.h" #import -#import "BVSDKConfiguration.h" @implementation BVPINRequest -+(void)getPendingPINs:(void(^ _Nonnull)(NSArray * _Nonnull pins))completion failure:(void(^ _Nonnull)(NSError * _Nonnull))failure{ ++ (void)getPendingPINs: + (nonnull void (^)(NSArray *__nonnull pins))completion + failure:(nonnull void (^)(NSError *__nonnull))failure { + NSString *idfa = + [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; + + NSURL *url = [NSURL + URLWithString:[NSString + stringWithFormat:@"https://api.bazaarvoice.com/pin/" + @"toreview?passkey=%@&bvid=magpie_" + @"idfa_%@&client=%@", + [BVSDKManager sharedManager] + .configuration.apiKeyPIN, + idfa, + [BVSDKManager sharedManager] + .configuration.clientId]]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + + [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; - NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; + NSURLSession *session = [NSURLSession sharedSession]; - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.bazaarvoice.com/pin/toreview?passkey=%@&bvid=magpie_idfa_%@&client=%@", [BVSDKManager sharedManager].configuration.apiKeyPIN, idfa,[BVSDKManager sharedManager].configuration.clientId]]; - NSURLRequest* urlRequest = [NSURLRequest requestWithURL:url]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", url]]; - - NSURLSession* session = [NSURLSession sharedSession]; + NSURLSessionDataTask *task = [session + dataTaskWithRequest:urlRequest + completionHandler:^(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable error) { - NSURLSessionDataTask* task = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; - - if (!error && httpResponse.statusCode <300 ){ - + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + if (!error && httpResponse.statusCode < 300) { NSError *jsonError; - NSArray *jsonArr = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; - if (jsonArr){ - NSMutableArray *pins = [NSMutableArray new]; - for (NSDictionary *obj in jsonArr) { - [pins addObject:[[BVPIN alloc] initWithDictionary: obj]]; - } - [[BVLogger sharedLogger] verbose: [NSString stringWithFormat:@"%@", jsonArr]]; - dispatch_async(dispatch_get_main_queue(), ^ { - completion([NSArray arrayWithArray:pins]); - }); + NSArray *jsonArr = [NSJSONSerialization + JSONObjectWithData:data + options:NSJSONReadingMutableContainers + error:&jsonError]; + if (jsonArr) { + NSMutableArray *pins = [NSMutableArray new]; + for (NSDictionary *obj in jsonArr) { + [pins addObject:[[BVPIN alloc] initWithDictionary:obj]]; + } + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"%@", jsonArr]]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion([NSArray arrayWithArray:pins]); + }); } else { - error = [NSError errorWithDomain:BVErrDomain code:BV_ERROR_PARSING_FAILED userInfo:@{NSLocalizedDescriptionKey:@"An unknown parsing error occurred."}]; - dispatch_async(dispatch_get_main_queue(), ^ { - failure(error); - }); + error = + [NSError errorWithDomain:BVErrDomain + code:BV_ERROR_PARSING_FAILED + userInfo:@{ + NSLocalizedDescriptionKey : + @"An unknown parsing error occurred." + }]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); } - } else { - if (error == nil){ - error = [NSError errorWithDomain:BVErrDomain code:httpResponse.statusCode userInfo:httpResponse.allHeaderFields]; + } else { + if (error == nil) { + error = [NSError errorWithDomain:BVErrDomain + code:httpResponse.statusCode + userInfo:httpResponse.allHeaderFields]; } - [[BVLogger sharedLogger] printError: error]; - dispatch_async(dispatch_get_main_queue(), ^ { - failure(error); + [[BVLogger sharedLogger] printError:error]; + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); }); + } + }]; - } - }]; - - [task resume]; + [task resume]; } @end diff --git a/Pod/BVRecommendations/BVProductRecommendationView.h b/Pod/BVRecommendations/BVProductRecommendationView.h index 85ba8688..4ed7f670 100644 --- a/Pod/BVRecommendations/BVProductRecommendationView.h +++ b/Pod/BVRecommendations/BVProductRecommendationView.h @@ -6,35 +6,34 @@ // // -#import #import "BVRecommendedProduct.h" - +#import /// A collection view cell to show a single product recommendation in. -/// This cell should be used in combination with `BVRecommendationCollectionView`. +/// This cell should be used in combination with +/// `BVRecommendationCollectionView`. @interface BVRecommendationCollectionViewCell : UICollectionViewCell /// The BVRecommendedProduct shown in this view -@property (strong, nonatomic) BVRecommendedProduct* bvRecommendedProduct; +@property(strong, nonatomic) BVRecommendedProduct *bvRecommendedProduct; @end - /// A table view cell to show a single product recommendation in. /// This cell should be used in combination with `BVRecommendationTableView`. @interface BVRecommendationTableViewCell : UITableViewCell /// The BVRecommendedProduct shown in this view -@property (strong, nonatomic) BVRecommendedProduct* bvRecommendedProduct; +@property(strong, nonatomic) BVRecommendedProduct *bvRecommendedProduct; @end - /// A view to show a single product recommendation in. -/// This view may be used in combination with `BVProductRecommendationsContainer`, which holds one or more of these views. +/// This view may be used in combination with +/// `BVProductRecommendationsContainer`, which holds one or more of these views. @interface BVProductRecommendationView : UIView /// The BVRecommendedProduct shown in this view -@property (strong, nonatomic) BVRecommendedProduct* bvRecommendedProduct; +@property(strong, nonatomic) BVRecommendedProduct *bvRecommendedProduct; @end \ No newline at end of file diff --git a/Pod/BVRecommendations/BVProductRecommendationView.m b/Pod/BVRecommendations/BVProductRecommendationView.m index 59aef079..3b2dd0c5 100644 --- a/Pod/BVRecommendations/BVProductRecommendationView.m +++ b/Pod/BVRecommendations/BVProductRecommendationView.m @@ -11,169 +11,162 @@ @implementation BVRecommendationCollectionViewCell --(void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { - - _bvRecommendedProduct = bvRecommendedProduct; - - [_bvRecommendedProduct recordImpression]; - +- (void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { + + _bvRecommendedProduct = bvRecommendedProduct; + + [_bvRecommendedProduct recordImpression]; } --(void)recommendationViewClicked { - - [self.bvRecommendedProduct recordTap]; - +- (void)recommendationViewClicked { + + [self.bvRecommendedProduct recordTap]; } --(void)didMoveToSuperview { - [super didMoveToSuperview]; - - [self checkButtonsInSubviews:self.subviews]; - [self checkGestureRecognizers]; +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + [self checkButtonsInSubviews:self.subviews]; + [self checkGestureRecognizers]; } --(void)checkButtonsInSubviews:(NSArray*)subviews { - - for(UIView* subview in subviews) { - if([subview isKindOfClass:[UIButton class]]) { - - UIButton* button = (UIButton*)subview; - [button addTarget:self action:@selector(recommendationViewClicked) forControlEvents:UIControlEventTouchUpInside]; - - } - [self checkButtonsInSubviews:subview.subviews]; +- (void)checkButtonsInSubviews:(NSArray *)subviews { + + for (UIView *subview in subviews) { + if ([subview isKindOfClass:[UIButton class]]) { + + UIButton *button = (UIButton *)subview; + [button addTarget:self + action:@selector(recommendationViewClicked) + forControlEvents:UIControlEventTouchUpInside]; } - + [self checkButtonsInSubviews:subview.subviews]; + } } --(void)checkGestureRecognizers { - - for(UIGestureRecognizer* recognizer in self.gestureRecognizers) { - if([recognizer isKindOfClass:[UIGestureRecognizer class]]){ - UIGestureRecognizer* tapRecognizer = (UIGestureRecognizer*)recognizer; - - if([tapRecognizer cancelsTouchesInView]){ - - [NSException raise:@"InvalidViewConfiguration" - format:@"UIGestureRecognizer must have `cancelsTouchesInView` set to false for the BVSDK to properly function."]; - - } - } +- (void)checkGestureRecognizers { + + for (UIGestureRecognizer *recognizer in self.gestureRecognizers) { + if ([recognizer isKindOfClass:[UIGestureRecognizer class]]) { + UIGestureRecognizer *tapRecognizer = (UIGestureRecognizer *)recognizer; + + if ([tapRecognizer cancelsTouchesInView]) { + + [NSException + raise:@"InvalidViewConfiguration" + format:@"UIGestureRecognizer must have `cancelsTouchesInView` set " + @"to false for the BVSDK to properly function."]; + } } - + } } @end @implementation BVRecommendationTableViewCell --(void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { - - _bvRecommendedProduct = bvRecommendedProduct; - - [_bvRecommendedProduct recordImpression]; - +- (void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { + + _bvRecommendedProduct = bvRecommendedProduct; + + [_bvRecommendedProduct recordImpression]; } --(void)recommendationViewClicked { - - [self.bvRecommendedProduct recordTap]; - +- (void)recommendationViewClicked { + + [self.bvRecommendedProduct recordTap]; } --(void)didMoveToSuperview { - [super didMoveToSuperview]; - - [self checkButtonsInSubviews:self.subviews]; - [self checkGestureRecognizers]; +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + [self checkButtonsInSubviews:self.subviews]; + [self checkGestureRecognizers]; } --(void)checkButtonsInSubviews:(NSArray*)subviews { - - for(UIView* subview in subviews) { - if([subview isKindOfClass:[UIButton class]]) { - - UIButton* button = (UIButton*)subview; - [button addTarget:self action:@selector(recommendationViewClicked) forControlEvents:UIControlEventTouchUpInside]; - - } - [self checkButtonsInSubviews:subview.subviews]; +- (void)checkButtonsInSubviews:(NSArray *)subviews { + + for (UIView *subview in subviews) { + if ([subview isKindOfClass:[UIButton class]]) { + + UIButton *button = (UIButton *)subview; + [button addTarget:self + action:@selector(recommendationViewClicked) + forControlEvents:UIControlEventTouchUpInside]; } - + [self checkButtonsInSubviews:subview.subviews]; + } } --(void)checkGestureRecognizers { - - for(UIGestureRecognizer* recognizer in self.gestureRecognizers) { - if([recognizer isKindOfClass:[UIGestureRecognizer class]]){ - UIGestureRecognizer* tapRecognizer = (UIGestureRecognizer*)recognizer; - - if([tapRecognizer cancelsTouchesInView]){ - - [NSException raise:@"InvalidViewConfiguration" - format:@"UIGestureRecognizer must have `cancelsTouchesInView` set to false for the BVSDK to properly function."]; - - } - } +- (void)checkGestureRecognizers { + + for (UIGestureRecognizer *recognizer in self.gestureRecognizers) { + if ([recognizer isKindOfClass:[UIGestureRecognizer class]]) { + UIGestureRecognizer *tapRecognizer = (UIGestureRecognizer *)recognizer; + + if ([tapRecognizer cancelsTouchesInView]) { + + [NSException + raise:@"InvalidViewConfiguration" + format:@"UIGestureRecognizer must have `cancelsTouchesInView` set " + @"to false for the BVSDK to properly function."]; + } } - + } } @end - @implementation BVProductRecommendationView --(void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { - - _bvRecommendedProduct = bvRecommendedProduct; - - [_bvRecommendedProduct recordImpression]; - +- (void)setBvRecommendedProduct:(BVRecommendedProduct *)bvRecommendedProduct { + + _bvRecommendedProduct = bvRecommendedProduct; + + [_bvRecommendedProduct recordImpression]; } --(void)recommendationViewClicked { - - [_bvRecommendedProduct recordTap]; - +- (void)recommendationViewClicked { + + [_bvRecommendedProduct recordTap]; } --(void)didMoveToSuperview { - [super didMoveToSuperview]; - - [self checkButtonsInSubviews:self.subviews]; - [self checkGestureRecognizers]; +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + [self checkButtonsInSubviews:self.subviews]; + [self checkGestureRecognizers]; } --(void)checkButtonsInSubviews:(NSArray*)subviews { - - for(UIView* subview in subviews) { - if([subview isKindOfClass:[UIButton class]]) { - - UIButton* button = (UIButton*)subview; - [button addTarget:self action:@selector(recommendationViewClicked) forControlEvents:UIControlEventTouchUpInside]; - - } - [self checkButtonsInSubviews:subview.subviews]; +- (void)checkButtonsInSubviews:(NSArray *)subviews { + + for (UIView *subview in subviews) { + if ([subview isKindOfClass:[UIButton class]]) { + + UIButton *button = (UIButton *)subview; + [button addTarget:self + action:@selector(recommendationViewClicked) + forControlEvents:UIControlEventTouchUpInside]; } - + [self checkButtonsInSubviews:subview.subviews]; + } } --(void)checkGestureRecognizers { - - for(UIGestureRecognizer* recognizer in self.gestureRecognizers) { - if([recognizer isKindOfClass:[UIGestureRecognizer class]]){ - UIGestureRecognizer* tapRecognizer = (UIGestureRecognizer*)recognizer; - - if([tapRecognizer cancelsTouchesInView]){ - - [NSException raise:@"InvalidViewConfiguration" - format:@"UIGestureRecognizer must have `cancelsTouchesInView` set to false for the BVSDK to properly function."]; - - } - } +- (void)checkGestureRecognizers { + + for (UIGestureRecognizer *recognizer in self.gestureRecognizers) { + if ([recognizer isKindOfClass:[UIGestureRecognizer class]]) { + UIGestureRecognizer *tapRecognizer = (UIGestureRecognizer *)recognizer; + + if ([tapRecognizer cancelsTouchesInView]) { + + [NSException + raise:@"InvalidViewConfiguration" + format:@"UIGestureRecognizer must have `cancelsTouchesInView` set " + @"to false for the BVSDK to properly function."]; + } } - + } } @end \ No newline at end of file diff --git a/Pod/BVRecommendations/BVProductRecommendationsContainer.h b/Pod/BVRecommendations/BVProductRecommendationsContainer.h index 860ed8f9..38964b4e 100644 --- a/Pod/BVRecommendations/BVProductRecommendationsContainer.h +++ b/Pod/BVRecommendations/BVProductRecommendationsContainer.h @@ -6,62 +6,69 @@ // // -#import #import "BVRecommendationsLoader.h" +#import /** - Recommendations container views conform to this protocol to be able to load recommendations - via the `loadRequest:completionHandler:errorHandler:` required method. + Recommendations container views conform to this protocol to be able to load + recommendations via the `loadRequest:completionHandler:errorHandler:` + required method. */ @protocol BVRecommendationsContainerProtocol @required -/** +/** Load product recommendations based on data fed in from `request`. @param request The request parameters to load - @param completionHandler Completion handler which returns an array of `BVRecommendedProduct` product recommendations - @param errorHandler Error handler which returns an `NSError` if the request has failed. + @param completionHandler Completion handler which returns an array of + `BVRecommendedProduct` product recommendations + @param errorHandler Error handler which returns an `NSError` if the + request has failed. @availability 3.3.0 and later */ -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler; +- (void)loadRequest:(BVRecommendationsRequest *)request + completionHandler:(recommendationsCompletionHandler)completionHandler + errorHandler:(recommendationsErrorHandler)errorHandler; @end - /** A collection view to show product recommendations in. - - This collection view should be used in combination with `BVRecommendationCollectionViewCell` cells. + + This collection view should be used in combination with + `BVRecommendationCollectionViewCell` cells. */ -@interface BVProductRecommendationsCollectionView : UICollectionView +@interface BVProductRecommendationsCollectionView + : UICollectionView @end - /** A table view to show product recommendations in. - - This table view should be used in combination with `BVRecommendationTableViewCell` cells. + + This table view should be used in combination with + `BVRecommendationTableViewCell` cells. */ -@interface BVProductRecommendationsTableView : UITableView +@interface BVProductRecommendationsTableView + : UITableView @end - /** A container view to show one or more product recommendations in. - - Usually, product recommendations are shown in a scrollable collection view or table view. - For this case, use `BVProductRecommendationsCollectionView` or `BVProductRecommendationsTableView`. - - This container is meant to hold static, non-scrolling product recommendations. - You should create one or more `BVProductRecommendationView` views and add them to this container view. + + Usually, product recommendations are shown in a scrollable collection view + or table view. For this case, use `BVProductRecommendationsCollectionView` or + `BVProductRecommendationsTableView`. + + This container is meant to hold static, non-scrolling product + recommendations. You should create one or more `BVProductRecommendationView` + views and add them to this container view. */ -@interface BVProductRecommendationsContainer : UIView +@interface BVProductRecommendationsContainer + : UIView @end \ No newline at end of file diff --git a/Pod/BVRecommendations/BVProductRecommendationsContainer.m b/Pod/BVRecommendations/BVProductRecommendationsContainer.m index 39b6d2d0..84d316ff 100644 --- a/Pod/BVRecommendations/BVProductRecommendationsContainer.m +++ b/Pod/BVRecommendations/BVProductRecommendationsContainer.m @@ -10,417 +10,444 @@ #import "BVMessageInterceptor.h" #import "BVRecommendations.h" - -@interface BVProductRecommendationsContainer() { - bool hasSentRenderedEvent; - bool hasSentSeenEvent; +@interface BVProductRecommendationsContainer () { + bool hasSentRenderedEvent; + bool hasSentSeenEvent; } @end @implementation BVProductRecommendationsContainer -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler { - - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:completionHandler errorHandler:errorHandler]; - - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - -} - --(void)willMoveToSuperview:(UIView *)newSuperview { - - if (newSuperview == nil || hasSentRenderedEvent){ - return; - } - - hasSentRenderedEvent = true; - - [BVRecsAnalyticsHelper queueAnalyticsEventForRecommendationsOnPage:RecommendationsCustom]; - -} +- (void)loadRequest:(BVRecommendationsRequest *)request + completionHandler:(recommendationsCompletionHandler)completionHandler + errorHandler:(recommendationsErrorHandler)errorHandler { --(void)willMoveToWindow:(UIWindow *)newWindow { - - if (newWindow == nil || hasSentSeenEvent){ - return; - } - - hasSentSeenEvent = true; - + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:completionHandler + errorHandler:errorHandler]; + + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; } -// helper +- (void)willMoveToSuperview:(UIView *)newSuperview { + + if (newSuperview == nil || hasSentRenderedEvent) { + return; + } -+(NSString*)formatIndex:(NSIndexPath*)indexPath { - return [NSString stringWithFormat:@"%li:%li", (long)indexPath.section, (long)indexPath.row]; + hasSentRenderedEvent = true; + + [BVRecsAnalyticsHelper + queueAnalyticsEventForRecommendationsOnPage:RecommendationsCustom]; } -@end +- (void)willMoveToWindow:(UIWindow *)newWindow { + if (newWindow == nil || hasSentSeenEvent) { + return; + } + hasSentSeenEvent = true; +} +// helper -@interface BVProductRecommendationsTableView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentRenderedEvent; - bool hasSentSeenEvent; - NSMutableDictionary* cellToProductMap; ++ (NSString *)formatIndex:(NSIndexPath *)indexPath { + return [NSString stringWithFormat:@"%li:%li", (long)indexPath.section, + (long)indexPath.row]; +} + +@end + +@interface BVProductRecommendationsTableView () { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentRenderedEvent; + bool hasSentSeenEvent; + NSMutableDictionary *cellToProductMap; } @end @implementation BVProductRecommendationsTableView -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler { - - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:completionHandler errorHandler:errorHandler]; - - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - -} - --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (void)loadRequest:(BVRecommendationsRequest *)request + completionHandler:(recommendationsCompletionHandler)completionHandler + errorHandler:(recommendationsErrorHandler)errorHandler { + + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:completionHandler + errorHandler:errorHandler]; + + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; } --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { - self = [super initWithFrame:frame style:style]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { + self = [super initWithFrame:frame style:style]; + if (self) { + [self setup]; + } + return self; } --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } --(void)willMoveToSuperview:(UIView *)newSuperview { - - if (newSuperview == nil || hasSentRenderedEvent){ - return; - } - - hasSentRenderedEvent = true; - - [BVRecsAnalyticsHelper queueAnalyticsEventForRecommendationsOnPage:RecommendationsTableView]; - +- (void)willMoveToSuperview:(UIView *)newSuperview { + + if (newSuperview == nil || hasSentRenderedEvent) { + return; + } + + hasSentRenderedEvent = true; + + [BVRecsAnalyticsHelper + queueAnalyticsEventForRecommendationsOnPage:RecommendationsTableView]; } --(void)willMoveToWindow:(UIWindow *)newWindow { - - if (newWindow == nil || hasSentSeenEvent){ - return; - } - - hasSentSeenEvent = true; - +- (void)willMoveToWindow:(UIWindow *)newWindow { + + if (newWindow == nil || hasSentSeenEvent) { + return; + } + + hasSentSeenEvent = true; } #pragma mark UIScrollViewDelegate +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - [BVRecsAnalyticsHelper queueAnalyticsEventForWidgetScroll:RecommendationsTableView]; - } - -} + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - [BVRecsAnalyticsHelper queueAnalyticsEventForWidgetScroll:RecommendationsTableView]; - + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + [BVRecsAnalyticsHelper + queueAnalyticsEventForWidgetScroll:RecommendationsTableView]; + } } +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + [BVRecsAnalyticsHelper + queueAnalyticsEventForWidgetScroll:RecommendationsTableView]; +} #pragma mark UITableViewDelegate --(void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition { - [super selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition]; - +- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath + animated:(BOOL)animated + scrollPosition:(UITableViewScrollPosition)scrollPosition { + [super selectRowAtIndexPath:indexPath + animated:animated + scrollPosition:scrollPosition]; } --(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - - if([delegate_interceptor.receiver respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { - [delegate_interceptor.receiver tableView:tableView didSelectRowAtIndexPath:indexPath]; - } - - BVRecommendedProduct* product = [cellToProductMap objectForKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; - if(product != nil) { - [product recordTap]; - } - +- (void)tableView:(UITableView *)tableView + didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + + if ([delegate_interceptor.receiver + respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) { + [delegate_interceptor.receiver tableView:tableView + didSelectRowAtIndexPath:indexPath]; + } + + BVRecommendedProduct *product = [cellToProductMap + objectForKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; + if (product != nil) { + [product recordTap]; + } } #pragma mark - UITableViewDataSource --(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - - UITableViewCell* cell = [datasource_interceptor.receiver tableView:tableView cellForRowAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVRecommendationTableViewCell class]]) { - - BVRecommendationTableViewCell* bvCell = (BVRecommendationTableViewCell*)cell; - BVRecommendedProduct* product = bvCell.bvRecommendedProduct; - if(product != nil){ - [cellToProductMap setObject:product forKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; - } - else { - // error, cell must have product set - [NSException raise:BVErrDomain format:@"BVRecommendationTableViewCell has nil `bvProduct` property. This must be set in `cellForItemAtIndexPath`."]; - } - +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + + UITableViewCell *cell = [datasource_interceptor.receiver tableView:tableView + cellForRowAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVRecommendationTableViewCell class]]) { + + BVRecommendationTableViewCell *bvCell = + (BVRecommendationTableViewCell *)cell; + BVRecommendedProduct *product = bvCell.bvRecommendedProduct; + if (product != nil) { + [cellToProductMap + setObject:product + forKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; + } else { + // error, cell must have product set + [NSException + raise:BVErrDomain + format:@"BVRecommendationTableViewCell has nil `bvProduct` property. " + @"This must be set in `cellForItemAtIndexPath`."]; } - - - return cell; - + } + + return cell; } --(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [datasource_interceptor.receiver tableView:tableView numberOfRowsInSection:section]; +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + return [datasource_interceptor.receiver tableView:tableView + numberOfRowsInSection:section]; } @end - - -@interface BVProductRecommendationsCollectionView(){ - BVMessageInterceptor* delegate_interceptor; - BVMessageInterceptor* datasource_interceptor; - bool hasSentScrollEvent; - bool hasSentRenderedEvent; - bool hasSentSeenEvent; - NSMutableDictionary* cellToProductMap; +@interface BVProductRecommendationsCollectionView () < + UICollectionViewDelegate, UICollectionViewDataSource> { + BVMessageInterceptor *delegate_interceptor; + BVMessageInterceptor *datasource_interceptor; + bool hasSentScrollEvent; + bool hasSentRenderedEvent; + bool hasSentSeenEvent; + NSMutableDictionary *cellToProductMap; } @end @implementation BVProductRecommendationsCollectionView -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler { - - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:completionHandler errorHandler:errorHandler]; - - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - -} - --(id)init { - self = [super init]; - if(self){ - [self setup]; - } - return self; +- (void)loadRequest:(BVRecommendationsRequest *)request + completionHandler:(recommendationsCompletionHandler)completionHandler + errorHandler:(recommendationsErrorHandler)errorHandler { + + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:completionHandler + errorHandler:errorHandler]; + + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; } --(id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if(self){ - [self setup]; - } - return self; +- (id)init { + self = [super init]; + if (self) { + [self setup]; + } + return self; } --(id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { - self = [super initWithFrame:frame collectionViewLayout:layout]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setup]; + } + return self; } --(id)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if(self){ - [self setup]; - } - return self; +- (id)initWithFrame:(CGRect)frame + collectionViewLayout:(UICollectionViewLayout *)layout { + self = [super initWithFrame:frame collectionViewLayout:layout]; + if (self) { + [self setup]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self) { + [self setup]; + } + return self; } --(void)setup { - cellToProductMap = [NSMutableDictionary dictionary]; - delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDelegate:(id)delegate_interceptor]; - datasource_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setup { + cellToProductMap = [NSMutableDictionary dictionary]; + delegate_interceptor = [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDelegate:(id)delegate_interceptor]; + datasource_interceptor = + [[BVMessageInterceptor alloc] initWithMiddleman:self]; + [super setDataSource:(id)datasource_interceptor]; } --(void)setDataSource:(id)newDataSource { - [super setDataSource:nil]; - [datasource_interceptor setReceiver:newDataSource]; - [super setDataSource:(id)datasource_interceptor]; +- (void)setDataSource:(id)newDataSource { + [super setDataSource:nil]; + [datasource_interceptor setReceiver:newDataSource]; + [super setDataSource:(id)datasource_interceptor]; } - (void)setDelegate:(id)newDelegate { - [super setDelegate:nil]; - [delegate_interceptor setReceiver:newDelegate]; - [super setDelegate:(id)delegate_interceptor]; + [super setDelegate:nil]; + [delegate_interceptor setReceiver:newDelegate]; + [super setDelegate:(id)delegate_interceptor]; } - (void)dealloc { - delegate_interceptor = nil; - datasource_interceptor = nil; + delegate_interceptor = nil; + datasource_interceptor = nil; } --(void)willMoveToSuperview:(UIView *)newSuperview { - - if (newSuperview == nil || hasSentRenderedEvent){ - return; - } - - hasSentRenderedEvent = true; - +- (void)willMoveToSuperview:(UIView *)newSuperview { + + if (newSuperview == nil || hasSentRenderedEvent) { + return; + } + + hasSentRenderedEvent = true; } --(void)willMoveToWindow:(UIWindow *)newWindow { - - if (newWindow == nil || hasSentSeenEvent){ - return; - } - - hasSentSeenEvent = true; - - [BVRecsAnalyticsHelper queueAnalyticsEventForRecommendationsOnPage:RecommendationsCarousel]; - +- (void)willMoveToWindow:(UIWindow *)newWindow { + + if (newWindow == nil || hasSentSeenEvent) { + return; + } + + hasSentSeenEvent = true; + + [BVRecsAnalyticsHelper + queueAnalyticsEventForRecommendationsOnPage:RecommendationsCarousel]; } #pragma mark UIScrollViewDelegate +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { --(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]){ - [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; - } - - if(!hasSentScrollEvent) { - hasSentScrollEvent = true; - [BVRecsAnalyticsHelper queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; - } - -} + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { + [delegate_interceptor.receiver scrollViewWillBeginDragging:scrollView]; + } --(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - - if([delegate_interceptor.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { - [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; - } - - [BVRecsAnalyticsHelper queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; - + if (!hasSentScrollEvent) { + hasSentScrollEvent = true; + [BVRecsAnalyticsHelper + queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; + } } +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { -#pragma mark UICollectionViewDelegate + if ([delegate_interceptor.receiver + respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { + [delegate_interceptor.receiver scrollViewDidEndDecelerating:scrollView]; + } + + [BVRecsAnalyticsHelper + queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; +} +#pragma mark UICollectionViewDelegate +- (void)collectionView:(UICollectionView *)collectionView + didSelectItemAtIndexPath:(NSIndexPath *)indexPath { --(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { + if ([delegate_interceptor.receiver + respondsToSelector:@selector + (collectionView:didSelectItemAtIndexPath:)]) { + [delegate_interceptor.receiver collectionView:collectionView + didSelectItemAtIndexPath:indexPath]; + } - if([delegate_interceptor.receiver respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]) { - [delegate_interceptor.receiver collectionView:collectionView didSelectItemAtIndexPath:indexPath]; - } - - BVRecommendedProduct* product = [cellToProductMap objectForKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; - if(product != nil) { - [product recordTap]; - } - + BVRecommendedProduct *product = [cellToProductMap + objectForKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; + if (product != nil) { + [product recordTap]; + } } #pragma mark - UICollectionViewDataSource --(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - UICollectionViewCell* cell = [datasource_interceptor.receiver collectionView:collectionView cellForItemAtIndexPath:indexPath]; - - if([cell isKindOfClass:[BVRecommendationCollectionViewCell class]]) { - - BVRecommendationCollectionViewCell* bvCell = (BVRecommendationCollectionViewCell*)cell; - BVRecommendedProduct* product = bvCell.bvRecommendedProduct; - if(product != nil){ - [cellToProductMap setObject:product forKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; - } - else { - // error, cell must have product set - [NSException raise:BVErrDomain format:@"BVRecommendationCollectionViewCell has nil `bvRecommendedProduct` property. This must be set in `cellForItemAtIndexPath`."]; - } - +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView + cellForItemAtIndexPath:(NSIndexPath *)indexPath { + + UICollectionViewCell *cell = + [datasource_interceptor.receiver collectionView:collectionView + cellForItemAtIndexPath:indexPath]; + + if ([cell isKindOfClass:[BVRecommendationCollectionViewCell class]]) { + + BVRecommendationCollectionViewCell *bvCell = + (BVRecommendationCollectionViewCell *)cell; + BVRecommendedProduct *product = bvCell.bvRecommendedProduct; + if (product != nil) { + [cellToProductMap + setObject:product + forKey:[BVProductRecommendationsContainer formatIndex:indexPath]]; + } else { + // error, cell must have product set + [NSException raise:BVErrDomain + format:@"BVRecommendationCollectionViewCell has nil " + @"`bvRecommendedProduct` property. This must be set " + @"in `cellForItemAtIndexPath`."]; } - - return cell; - + } + + return cell; } --(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [datasource_interceptor.receiver collectionView:collectionView numberOfItemsInSection:section]; +- (NSInteger)collectionView:(UICollectionView *)collectionView + numberOfItemsInSection:(NSInteger)section { + return [datasource_interceptor.receiver collectionView:collectionView + numberOfItemsInSection:section]; } @end diff --git a/Pod/BVRecommendations/BVProductReview.h b/Pod/BVRecommendations/BVProductReview.h index 30d63851..20defd29 100644 --- a/Pod/BVRecommendations/BVProductReview.h +++ b/Pod/BVRecommendations/BVProductReview.h @@ -5,27 +5,22 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #import /// A product review written about a BVRecommendedProduct @interface BVProductReview : NSObject - /// Internal use --(id)initWithDict:(NSDictionary*)dict; - - -/// Title of the review. Example: "Great product!", "Drains batteries too quickly." -@property (strong, nonatomic) NSString *reviewTitle; +- (id)initWithDict:(NSDictionary *)dict; +/// Title of the review. Example: "Great product!", "Drains batteries too +/// quickly." +@property(strong, nonatomic) NSString *reviewTitle; /// Body text of the review. -@property (strong, nonatomic) NSString *reviewText; - +@property(strong, nonatomic) NSString *reviewText; /// The review author's name -@property (strong, nonatomic) NSString *reviewAuthorName; - +@property(strong, nonatomic) NSString *reviewAuthorName; @end diff --git a/Pod/BVRecommendations/BVProductReview.m b/Pod/BVRecommendations/BVProductReview.m index c73cf9c7..8dce9a64 100644 --- a/Pod/BVRecommendations/BVProductReview.m +++ b/Pod/BVRecommendations/BVProductReview.m @@ -5,24 +5,23 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import "BVCore.h" #import "BVProductReview.h" +#import "BVCore.h" @implementation BVProductReview --(id)initWithDict:(NSDictionary*)dict { - self = [super init]; - if(self) { - - if(dict != nil && ![dict isKindOfClass:[NSNull class]]){ - - SET_IF_NOT_NULL(self.reviewText, [dict objectForKey:@"text"]); - SET_IF_NOT_NULL(self.reviewTitle, [dict objectForKey:@"title"]); - SET_IF_NOT_NULL(self.reviewAuthorName, [dict objectForKey:@"authorName"]); - } - +- (id)initWithDict:(NSDictionary *)dict { + self = [super init]; + if (self) { + + if (dict != nil && ![dict isKindOfClass:[NSNull class]]) { + + SET_IF_NOT_NULL(self.reviewText, [dict objectForKey:@"text"]); + SET_IF_NOT_NULL(self.reviewTitle, [dict objectForKey:@"title"]); + SET_IF_NOT_NULL(self.reviewAuthorName, [dict objectForKey:@"authorName"]); } - return self; + } + return self; } @end diff --git a/Pod/BVRecommendations/BVRecommendations.h b/Pod/BVRecommendations/BVRecommendations.h index bfd27559..33bae9fb 100644 --- a/Pod/BVRecommendations/BVRecommendations.h +++ b/Pod/BVRecommendations/BVRecommendations.h @@ -9,14 +9,13 @@ #define BVRecommendations_h #import "BVCore.h" -#import "BVRecommendedProduct.h" -#import "BVRecommendationsLoader.h" -#import "BVRecsAnalyticsHelper.h" #import "BVProductRecommendationView.h" #import "BVProductRecommendationsContainer.h" +#import "BVProductReview.h" +#import "BVRecommendationsLoader.h" #import "BVRecommendationsRequest.h" +#import "BVRecommendedProduct.h" +#import "BVRecsAnalyticsHelper.h" #import "BVShopperProfile.h" -#import "BVProductRecommendationView.h" -#import "BVProductReview.h" #endif diff --git a/Pod/BVRecommendations/BVRecommendationsLoader.h b/Pod/BVRecommendations/BVRecommendationsLoader.h index 61b05e28..f2d5b96b 100644 --- a/Pod/BVRecommendations/BVRecommendationsLoader.h +++ b/Pod/BVRecommendations/BVRecommendationsLoader.h @@ -5,38 +5,33 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // - #import #import "BVCore.h" -#import "BVShopperProfile.h" #import "BVRecommendationsRequest.h" - -NS_ASSUME_NONNULL_BEGIN - +#import "BVShopperProfile.h" /// Loads product recommendations. @interface BVRecommendationsLoader : NSObject - -typedef void (^recommendationsCompletionHandler)(NSArray*); -typedef void (^recommendationsErrorHandler)(NSError*); - +typedef void (^recommendationsCompletionHandler)( + NSArray *__nonnull); +typedef void (^recommendationsErrorHandler)(NSError *__nonnull); /** Load product recommendations based on data fed in from `request`. - + @param request The request parameters to load - @param completionHandler Completion handler which returns an array of `BVRecommendedProduct` product recommendations - @param errorHandler Error handler which returns an `NSError` if the request has failed. - + @param completionHandler Completion handler which returns an array of + `BVRecommendedProduct` product recommendations + @param errorHandler Error handler which returns an `NSError` if the + request has failed. + @availability 3.3.0 and later */ -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler; +- (void)loadRequest:(nullable BVRecommendationsRequest *)request + completionHandler: + (nullable recommendationsCompletionHandler)completionHandler + errorHandler:(nullable recommendationsErrorHandler)errorHandler; @end - - -NS_ASSUME_NONNULL_END diff --git a/Pod/BVRecommendations/BVRecommendationsLoader.m b/Pod/BVRecommendations/BVRecommendationsLoader.m index d8f08537..2b391bb8 100644 --- a/Pod/BVRecommendations/BVRecommendationsLoader.m +++ b/Pod/BVRecommendations/BVRecommendationsLoader.m @@ -7,195 +7,222 @@ #import +#import "BVAnalyticsManager.h" #import "BVCore.h" #import "BVRecommendationsLoader.h" -#import "BVAnalyticsManager.h" #import "BVRecsAnalyticsHelper.h" -#import "BVShopperProfileRequestCache.h" #import "BVSDKConfiguration.h" +#import "BVShopperProfileRequestCache.h" @implementation BVRecommendationsLoader -- (void)loadRequest:(BVRecommendationsRequest*)request - completionHandler:(recommendationsCompletionHandler)completionHandler - errorHandler:(recommendationsErrorHandler)errorHandler { - - // check if SDK is properly configured - // if not, hit the error handler - if([self isSDKValid] == false) { - - [self errorOnMainThread:[self invalidSDKError] handler:errorHandler]; - return; - - } - - BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; - NSString *client = sdkMgr.configuration.clientId; - NSString *apiRoot = sdkMgr.urlRootShopperAdvertising; - NSString *apiKey = sdkMgr.configuration.apiKeyShopperAdvertising; - - // check that `apiKeyShopperAdvertising` is valid. Will fail only in debug mode. - NSAssert(apiKey.length, @"You must supply apiKeyShopperAdvertising in the BVSDKManager before using the Bazaarvoice SDK."); - - // Cool, clientId and passKey are valid. - - BVShopperProfileRequestCache *cache = [BVShopperProfileRequestCache sharedCache]; - - NSUInteger limit = request.limit; - NSString* productId = request.productId; - NSString* categoryId = request.categoryId; - - NSString *idfaString = [self getIdfaString]; - - if (limit <= 0 || limit > 50){ - limit = 20; // default limit - } - - NSString *idParam = [NSString stringWithFormat:@"magpie_idfa_%@", idfaString]; - - NSString *filterTypes = @"interests,brands,recommendations,reviews"; - - NSString *endPoint = [NSString stringWithFormat:@"%@/recommendations/%@?passKey=%@&include=%@&limit=%lu&client=%@", apiRoot, idParam, apiKey, filterTypes, (unsigned long)limit, client]; - - if (productId != nil){ - endPoint = [endPoint stringByAppendingString:[NSString stringWithFormat:@"&product=%@/%@", client, productId]]; - } - - if (categoryId != nil){ - endPoint = [endPoint stringByAppendingString:[NSString stringWithFormat:@"&category=%@", categoryId]]; - } - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"GET: %@", endPoint]]; - - NSURLRequest *networkRequest = [NSURLRequest requestWithURL: - [NSURL URLWithString:endPoint]]; - - NSCachedURLResponse *cachedResp = [cache cachedResponseForRequest:networkRequest]; - - if (cachedResp){ - - NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:cachedResp.data options:kNilOptions error:nil]; - - BVShopperProfile *profile = [[BVShopperProfile alloc] initWithDictionary:responseDict]; - - [cache printCacheSize]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"CACHED RESPONSE: %@", responseDict]]; - - [self completionOnMainThread:profile.recommendations handler:completionHandler]; - - return; - } - - NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:networkRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { - - NSHTTPURLResponse *urlResp = (NSHTTPURLResponse *)response; - - if ((!error && urlResp.statusCode < 300) && data != nil){ - +- (void)loadRequest:(BVRecommendationsRequest *)request + completionHandler:(recommendationsCompletionHandler)completionHandler + errorHandler:(recommendationsErrorHandler)errorHandler { + // check if SDK is properly configured + // if not, hit the error handler + if ([self isSDKValid] == false) { + [self errorOnMainThread:[self invalidSDKError] handler:errorHandler]; + return; + } + + BVSDKManager *sdkMgr = [BVSDKManager sharedManager]; + NSString *client = sdkMgr.configuration.clientId; + NSString *apiRoot = sdkMgr.urlRootShopperAdvertising; + NSString *apiKey = sdkMgr.configuration.apiKeyShopperAdvertising; + + // check that `apiKeyShopperAdvertising` is valid. Will fail only in debug + // mode. + NSAssert(apiKey.length, @"You must supply apiKeyShopperAdvertising in the " + @"BVSDKManager before using the Bazaarvoice SDK."); + + // Cool, clientId and passKey are valid. + + BVShopperProfileRequestCache *cache = + [BVShopperProfileRequestCache sharedCache]; + + NSUInteger limit = request.limit; + NSString *productId = request.productId; + NSString *categoryId = request.categoryId; + + NSString *idfaString = [self getIdfaString]; + + if (limit <= 0 || limit > 50) { + limit = 20; // default limit + } + + NSString *idParam = [NSString stringWithFormat:@"magpie_idfa_%@", idfaString]; + + NSString *filterTypes = @"interests,brands,recommendations,reviews"; + + NSString *endPoint = [NSString + stringWithFormat: + @"%@/recommendations/%@?passKey=%@&include=%@&limit=%lu&client=%@", + apiRoot, idParam, apiKey, filterTypes, (unsigned long)limit, client]; + + if (productId != nil) { + endPoint = [endPoint + stringByAppendingString:[NSString stringWithFormat:@"&product=%@/%@", + client, productId]]; + } + + if (categoryId != nil) { + endPoint = [endPoint + stringByAppendingString:[NSString stringWithFormat:@"&category=%@", + categoryId]]; + } + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"GET: %@", endPoint]]; + + NSURLRequest *networkRequest = + [NSURLRequest requestWithURL:[NSURL URLWithString:endPoint]]; + + NSCachedURLResponse *cachedResp = + [cache cachedResponseForRequest:networkRequest]; + + if (cachedResp) { + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:cachedResp.data + options:kNilOptions + error:nil]; + + BVShopperProfile *profile = + [[BVShopperProfile alloc] initWithDictionary:responseDict]; + + [cache printCacheSize]; + + [[BVLogger sharedLogger] + verbose:[NSString + stringWithFormat:@"CACHED RESPONSE: %@", responseDict]]; + + [self completionOnMainThread:profile.recommendations + handler:completionHandler]; + + return; + } + + NSURLSessionDataTask *task = [[NSURLSession sharedSession] + dataTaskWithRequest:networkRequest + completionHandler:^(NSData *data, NSURLResponse *response, + NSError *error) { + + NSHTTPURLResponse *urlResp = (NSHTTPURLResponse *)response; + + if ((!error && urlResp.statusCode < 300) && data != nil) { NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response; - + NSError *errorJSON; - NSDictionary* responseDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&errorJSON]; - - if (!errorJSON){ - - BVShopperProfile *profile = [[BVShopperProfile alloc] initWithDictionary:responseDict]; - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"RESPONSE: (%ld): %@", (long)httpResp.statusCode, responseDict]]; - - // Successful response, save in cache - NSCachedURLResponse *newCachedResp = [[NSCachedURLResponse alloc] initWithResponse:response data:data]; - - [cache storeCachedResponse:newCachedResp forRequest:networkRequest]; - - // Success! - [self completionOnMainThread:profile.recommendations handler:completionHandler]; - - return; - + NSDictionary *responseDict = + [NSJSONSerialization JSONObjectWithData:data + options:kNilOptions + error:&errorJSON]; + + if (!errorJSON) { + BVShopperProfile *profile = + [[BVShopperProfile alloc] initWithDictionary:responseDict]; + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat:@"RESPONSE: (%ld): %@", + (long)httpResp.statusCode, + responseDict]]; + + // Successful response, save in cache + NSCachedURLResponse *newCachedResp = + [[NSCachedURLResponse alloc] initWithResponse:response + data:data]; + + [cache storeCachedResponse:newCachedResp + forRequest:networkRequest]; + + // Success! + [self completionOnMainThread:profile.recommendations + handler:completionHandler]; + + return; + } else { - //serialization error - [self errorOnMainThread:errorJSON handler:errorHandler]; - return; + // serialization error + [self errorOnMainThread:errorJSON handler:errorHandler]; + return; } - - } else { + + } else { // request error - if (error){ - [self errorOnMainThread:error handler:errorHandler]; - return; + if (error) { + [self errorOnMainThread:error handler:errorHandler]; + return; } else { - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: urlResp.description }; - NSError *err = [NSError errorWithDomain:BVErrDomain code:urlResp.statusCode userInfo:userInfo]; - [self errorOnMainThread:err handler:errorHandler]; - return; + NSDictionary *userInfo = + @{NSLocalizedDescriptionKey : urlResp.description}; + NSError *err = [NSError errorWithDomain:BVErrDomain + code:urlResp.statusCode + userInfo:userInfo]; + [self errorOnMainThread:err handler:errorHandler]; + return; } - } - - - }]; - - [task resume]; - + } + + }]; + + [task resume]; } --(void)completionOnMainThread:(NSArray*)recommendations handler:(recommendationsCompletionHandler)completionHandler { - - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(recommendations); - }); - +- (void) +completionOnMainThread:(NSArray *)recommendations + handler:(recommendationsCompletionHandler)completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(recommendations); + }); } --(void)errorOnMainThread:(NSError*)error handler:(recommendationsErrorHandler)errorHandler { - - dispatch_async(dispatch_get_main_queue(), ^{ - errorHandler(error); - }); - +- (void)errorOnMainThread:(NSError *)error + handler:(recommendationsErrorHandler)errorHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + errorHandler(error); + }); } --(NSString*)getIdfaString { - - if([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]){ - return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; - } else { - return @"nontracking"; - } - +- (NSString *)getIdfaString { + if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) { + return [[[ASIdentifierManager sharedManager] advertisingIdentifier] + UUIDString]; + } else { + return @"nontracking"; + } } --(BOOL)isSDKValid { - - NSString* clientId = [BVSDKManager sharedManager].configuration.clientId; - NSString* passKey = [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; - - if (clientId == nil || passKey == nil || [clientId isEqualToString:@""] || [passKey isEqualToString:@""]) { - return false; - } - - return true; - +- (BOOL)isSDKValid { + NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; + NSString *passKey = + [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; + + if (clientId == nil || passKey == nil || [clientId isEqualToString:@""] || + [passKey isEqualToString:@""]) { + return false; + } + + return true; } --(NSError*)invalidSDKError { - - NSMutableDictionary* userInfo = [NSMutableDictionary dictionary]; - - NSString* clientId = [BVSDKManager sharedManager].configuration.clientId; - NSString* passKey = [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; - - if([clientId isEqualToString:@""]) { - [userInfo setValue:@"Client Id is not set." forKey:NSLocalizedDescriptionKey]; - } - - if ([passKey isEqualToString:@""]) { - [userInfo setValue:@"apiKeyShopperAdvertising is not set." forKey:NSLocalizedDescriptionKey]; - } - - return [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; - +- (NSError *)invalidSDKError { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + + NSString *clientId = [BVSDKManager sharedManager].configuration.clientId; + NSString *passKey = + [BVSDKManager sharedManager].configuration.apiKeyShopperAdvertising; + + if ([clientId isEqualToString:@""]) { + [userInfo setValue:@"Client Id is not set." + forKey:NSLocalizedDescriptionKey]; + } + + if ([passKey isEqualToString:@""]) { + [userInfo setValue:@"apiKeyShopperAdvertising is not set." + forKey:NSLocalizedDescriptionKey]; + } + + return [NSError errorWithDomain:BVErrDomain code:-1 userInfo:userInfo]; } @end diff --git a/Pod/BVRecommendations/BVRecommendationsRequest.h b/Pod/BVRecommendations/BVRecommendationsRequest.h index 33818732..c61c9820 100644 --- a/Pod/BVRecommendations/BVRecommendationsRequest.h +++ b/Pod/BVRecommendations/BVRecommendationsRequest.h @@ -7,31 +7,38 @@ #import +/** + Recommendations can be requested with a limit and optional productId and + categoryId filtering. -/** - Recommendations can be requested with a limit and optional productId and categoryId filtering. - - When filtered to a `productId`, the recommendations will be largely related to that product. - - When filtered to a `categoryId`, the recommendations will be limited to products in that category. - - `limit` is the maximum number of recommendations to load. Suggested: 20. Max: 50. + When filtered to a `productId`, the recommendations will be largely related + to that product. + + When filtered to a `categoryId`, the recommendations will be limited to + products in that category. + + `limit` is the maximum number of recommendations to load. Suggested: 20. + Max: 50. */ @interface BVRecommendationsRequest : NSObject - (instancetype)initWithLimit:(NSUInteger)limit; -- (instancetype)initWithLimit:(NSUInteger)limit withProductId:(NSString*)productId; -- (instancetype)initWithLimit:(NSUInteger)limit withCategoryId:(NSString*)categoryId; +- (instancetype)initWithLimit:(NSUInteger)limit + withProductId:(NSString *)productId; +- (instancetype)initWithLimit:(NSUInteger)limit + withCategoryId:(NSString *)categoryId; /// Unavailable - use -initWithLimit: instead -- (instancetype)init __attribute__((unavailable("Use -initWithLimit: instead"))); +- (instancetype)init + __attribute__((unavailable("Use -initWithLimit: instead"))); /// Unavailable - use -initWithLimit: instead -- (instancetype)new __attribute__((unavailable("Use -initWithLimit: instead"))); +- (instancetype) new + __attribute__((unavailable("Use -initWithLimit: instead"))); /// internal use -@property (readonly) NSString* productId; -@property (readonly) NSString* categoryId; -@property (readonly) NSUInteger limit; +@property(readonly) NSString *productId; +@property(readonly) NSString *categoryId; +@property(readonly) NSUInteger limit; @end diff --git a/Pod/BVRecommendations/BVRecommendationsRequest.m b/Pod/BVRecommendations/BVRecommendationsRequest.m index 8a8624da..44e38f7b 100644 --- a/Pod/BVRecommendations/BVRecommendationsRequest.m +++ b/Pod/BVRecommendations/BVRecommendationsRequest.m @@ -7,44 +7,46 @@ #import "BVRecommendationsRequest.h" -@interface BVRecommendationsRequest() +@interface BVRecommendationsRequest () -@property (readwrite) NSString* _Nullable productId; -@property (readwrite) NSString* _Nullable categoryId; -@property (readwrite) NSUInteger limit; +@property(nullable, readwrite) NSString *productId; +@property(nullable, readwrite) NSString *categoryId; +@property(readwrite) NSUInteger limit; @end @implementation BVRecommendationsRequest - (instancetype)initWithLimit:(NSUInteger)limit { - self = [super init]; - if(self){ - self.productId = nil; - self.categoryId = nil; - self.limit = limit; - } - return self; + self = [super init]; + if (self) { + self.productId = nil; + self.categoryId = nil; + self.limit = limit; + } + return self; } -- (instancetype)initWithLimit:(NSUInteger)limit withProductId:(NSString*)productId { - self = [super init]; - if(self){ - self.productId = productId; - self.categoryId = nil; - self.limit = limit; - } - return self; +- (instancetype)initWithLimit:(NSUInteger)limit + withProductId:(NSString *)productId { + self = [super init]; + if (self) { + self.productId = productId; + self.categoryId = nil; + self.limit = limit; + } + return self; } -- (instancetype)initWithLimit:(NSUInteger)limit withCategoryId:(NSString*)categoryId { - self = [super init]; - if(self){ - self.productId = nil; - self.categoryId = categoryId; - self.limit = limit; - } - return self; +- (instancetype)initWithLimit:(NSUInteger)limit + withCategoryId:(NSString *)categoryId { + self = [super init]; + if (self) { + self.productId = nil; + self.categoryId = categoryId; + self.limit = limit; + } + return self; } @end diff --git a/Pod/BVRecommendations/BVRecommendedProduct.h b/Pod/BVRecommendations/BVRecommendedProduct.h index 805d9f48..b912522c 100644 --- a/Pod/BVRecommendations/BVRecommendedProduct.h +++ b/Pod/BVRecommendations/BVRecommendedProduct.h @@ -1,3 +1,5 @@ + + // // BVProduct.h // Bazaarvoice SDK @@ -8,77 +10,61 @@ #ifndef BVRecommendedProduct_h #define BVRecommendedProduct_h -#import -#import "BVProductReview.h" #import "BVDisplayableProductContent.h" -NS_ASSUME_NONNULL_BEGIN - +#import "BVProductReview.h" +#import /// Model contents for a single product recommendation display item -@interface BVRecommendedProduct : NSObject - +@interface BVRecommendedProduct : NSObject /** A single product recommendation model object - + @param dict The API response object for a product recommendation - @param recommendationStats The API response object for recommendation statistics - + @param recommendationStats The API response object for recommendation + statistics + @return A full initialized BVRecommendedProduct model object */ -- (id)initWithDictionary:(NSDictionary *)dict withRecommendationStats:(NSDictionary*)recommendationStats; - +- (nonnull id)initWithDictionary:(nonnull NSDictionary *)dict + withRecommendationStats:(nonnull NSDictionary *)recommendationStats; /// The unique idenfitier of the product -@property (strong, nonatomic) NSString *productId; - +@property(nonnull, strong, nonatomic) NSString *productId; /// The product title -@property (strong, nonatomic) NSString *productName; - +@property(nonnull, strong, nonatomic) NSString *productName; /// The fully qualified URL for this product. -@property (strong, nonatomic) NSString *productPageURL; - +@property(nonnull, strong, nonatomic) NSString *productPageURL; /// The product image thumbnail. Sizes may vary depending on the brand or client -@property (strong, nonatomic) NSString *imageURL; - +@property(nonnull, strong, nonatomic) NSString *imageURL; /// The average (float) rating for this product -@property (strong, nonatomic) NSNumber *averageRating; - +@property(nonnull, strong, nonatomic) NSNumber *averageRating; /// The total number of reviews on the product (integer) -@property (strong, nonatomic) NSNumber *numReviews; - +@property(nonnull, strong, nonatomic) NSNumber *numReviews; /// Price, in USD -@property (strong, nonatomic) NSString *price; - +@property(nonnull, strong, nonatomic) NSString *price; /// Highlighted review for this product. -@property (strong, nonatomic) BVProductReview* review; - +@property(nonnull, strong, nonatomic) BVProductReview *review; /// Whether this recommendation is a sponsored piece of content or not @property bool sponsored; - /// Record a tap event -- the user tapped a product --(void)recordTap; - +- (void)recordTap; /// Record an impression event -- the user was shown this product --(void)recordImpression; - +- (void)recordImpression; /// Internal use -@property (strong, nonatomic) NSDictionary* rawProductDict; - +@property(nonnull, strong, nonatomic) NSDictionary *rawProductDict; @end -NS_ASSUME_NONNULL_END - #endif diff --git a/Pod/BVRecommendations/BVRecommendedProduct.m b/Pod/BVRecommendations/BVRecommendedProduct.m index 8127d150..bb879b2e 100644 --- a/Pod/BVRecommendations/BVRecommendedProduct.m +++ b/Pod/BVRecommendations/BVRecommendedProduct.m @@ -8,7 +8,7 @@ #import "BVRecommendedProduct.h" #import "BVRecsAnalyticsHelper.h" -@interface BVRecommendedProduct() +@interface BVRecommendedProduct () @property bool hasSentImpressionEvent; @@ -19,66 +19,67 @@ @implementation BVRecommendedProduct @synthesize displayImageUrl; @synthesize displayName; -- (id)initWithDictionary:(NSDictionary *)dict withRecommendationStats:(NSDictionary*)recommendationStats{ - - self = [super init]; - - NSMutableDictionary* combinedDictionary = [NSMutableDictionary dictionaryWithDictionary:dict]; - [combinedDictionary addEntriesFromDictionary:recommendationStats]; - self.rawProductDict = combinedDictionary; - - SET_IF_NOT_NULL(self.productName, [dict objectForKey:@"name"]); - SET_IF_NOT_NULL(self.productId, [dict objectForKey:@"product"]); - SET_IF_NOT_NULL(self.productPageURL, [dict objectForKey:@"product_page_url"]); - SET_IF_NOT_NULL(self.imageURL, [dict objectForKey:@"image_url"]); - SET_IF_NOT_NULL(self.averageRating, [dict objectForKey:@"avg_rating"]); - SET_IF_NOT_NULL(self.numReviews, [dict objectForKey:@"num_reviews"]); - SET_IF_NOT_NULL(self.price, [dict objectForKey:@"price"]); - - self.review = [[BVProductReview alloc] initWithDict:[dict objectForKey:@"review"]]; - - self.sponsored = false; - if ([dict objectForKey:@"sponsored"] && [[dict objectForKey:@"sponsored"] integerValue] == 1){ - self.sponsored = true; - } - - return self; +- (id)initWithDictionary:(NSDictionary *)dict + withRecommendationStats:(NSDictionary *)recommendationStats { + + self = [super init]; + + NSMutableDictionary *combinedDictionary = + [NSMutableDictionary dictionaryWithDictionary:dict]; + [combinedDictionary addEntriesFromDictionary:recommendationStats]; + self.rawProductDict = combinedDictionary; + + SET_IF_NOT_NULL(self.productName, [dict objectForKey:@"name"]); + SET_IF_NOT_NULL(self.productId, [dict objectForKey:@"product"]); + SET_IF_NOT_NULL(self.productPageURL, [dict objectForKey:@"product_page_url"]); + SET_IF_NOT_NULL(self.imageURL, [dict objectForKey:@"image_url"]); + SET_IF_NOT_NULL(self.averageRating, [dict objectForKey:@"avg_rating"]); + SET_IF_NOT_NULL(self.numReviews, [dict objectForKey:@"num_reviews"]); + SET_IF_NOT_NULL(self.price, [dict objectForKey:@"price"]); + + self.review = + [[BVProductReview alloc] initWithDict:[dict objectForKey:@"review"]]; + + self.sponsored = false; + if ([dict objectForKey:@"sponsored"] && + [[dict objectForKey:@"sponsored"] integerValue] == 1) { + self.sponsored = true; + } + + return self; } +- (void)recordImpression { --(void)recordImpression { - - if(self.hasSentImpressionEvent) { - return; - } - self.hasSentImpressionEvent = true; - - [BVRecsAnalyticsHelper queueAnalyticsEventForProductView:self]; - + if (self.hasSentImpressionEvent) { + return; + } + self.hasSentImpressionEvent = true; + + [BVRecsAnalyticsHelper queueAnalyticsEventForProductView:self]; } --(void)recordTap { - - [BVRecsAnalyticsHelper queueAnalyticsEventForProductTapped:self]; - +- (void)recordTap { + + [BVRecsAnalyticsHelper queueAnalyticsEventForProductTapped:self]; } +- (NSString *)description { -- (NSString *)description{ - - return [NSString stringWithFormat:@"BVProduct: %@ - id: %@", self.productName, self.productId]; + return [NSString stringWithFormat:@"BVProduct: %@ - id: %@", self.productName, + self.productId]; } --(NSString*)identifier { - return _productId; +- (NSString *)identifier { + return _productId; } --(NSString*)displayName { - return _productName; +- (NSString *)displayName { + return _productName; } --(NSString*)displayImageUrl { - return _imageURL; +- (NSString *)displayImageUrl { + return _imageURL; } @end diff --git a/Pod/BVRecommendations/BVRecsAnalyticsHelper.h b/Pod/BVRecommendations/BVRecsAnalyticsHelper.h index a9069cf1..5894efb7 100644 --- a/Pod/BVRecommendations/BVRecsAnalyticsHelper.h +++ b/Pod/BVRecommendations/BVRecsAnalyticsHelper.h @@ -5,74 +5,78 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import -#import "BVShopperProfile.h" #import "BVRecommendationsLoader.h" +#import "BVShopperProfile.h" +#import @class BVRecommendationsLoader; -/// When interacting with a BVRecommendationsUI component, this enum provides a list of container types the user interacted with. +/// When interacting with a BVRecommendationsUI component, this enum provides a +/// list of container types the user interacted with. typedef NS_ENUM(NSInteger, BVProductRecommendationWidget) { - - /// Product recommendations from a horizontally scrolling collection view. - RecommendationsCarousel, - - /// Product recommendations from a UITableView - RecommendationsTableView, - - /// Custom widget created. - RecommendationsCustom - -}; + /// Product recommendations from a horizontally scrolling collection view. + RecommendationsCarousel, + + /// Product recommendations from a UITableView + RecommendationsTableView, + + /// Custom widget created. + RecommendationsCustom + +}; /// Used to send analytic events related to product recommendations @interface BVRecsAnalyticsHelper : NSObject - /** Queue an analytic event for a visible BVRecommendedProduct recommendation. - + @param product - A BVProduct object that was visible on screen. */ + (void)queueAnalyticsEventForProductView:(BVRecommendedProduct *)product; - /** Queue an analytic event for a user tapping on a product view - + @param product - The product that was tapped by the user */ + (void)queueAnalyticsEventForProductTapped:(BVRecommendedProduct *)product; - /** Queue an analytic event for a recommendations widget being loaded - + @param widgetType - Type of widget that was loaded */ -+ (void)queueAnalyticsEventForRecommendationsOnPage:(BVProductRecommendationWidget)widgetType; - ++ (void)queueAnalyticsEventForRecommendationsOnPage: + (BVProductRecommendationWidget)widgetType; /** - Queue an analytic event for a recommendation widigit becoming visible on screen. - - @param recommendationsRequest - The BVRecommendationsRequest use to construct the request parameters + Queue an analytic event for a recommendation widigit becoming visible on + screen. + + @param recommendationsRequest - The BVRecommendationsRequest use to + construct the request parameters @param widgetType - One of enum of BVProductRecommendationWidget - + @availability 3.0.1 and later */ -+ (void)queueEmbeddedRecommendationsPageViewEvent:(BVRecommendationsRequest *)recommendationsRequest - withWidgetType:(BVProductRecommendationWidget)widgetType; - ++ (void)queueEmbeddedRecommendationsPageViewEvent: + (BVRecommendationsRequest *)recommendationsRequest + withWidgetType: + (BVProductRecommendationWidget) + widgetType; /* - Queue a view did scroll interaction event on a scrollable recommendations widget. This should only be sent once the view stops scrolling, in order to send too many events for every swipe. - + Queue a view did scroll interaction event on a scrollable recommendations + widget. This should only be sent once the view stops scrolling, in order to + send too many events for every swipe. + @param widgetType - One of enum of BVProductRecommendationWidget - + @availability 3.0.1 and later */ -+ (void)queueAnalyticsEventForWidgetScroll:(BVProductRecommendationWidget)widgetType; ++ (void)queueAnalyticsEventForWidgetScroll: + (BVProductRecommendationWidget)widgetType; @end diff --git a/Pod/BVRecommendations/BVRecsAnalyticsHelper.m b/Pod/BVRecommendations/BVRecsAnalyticsHelper.m index a7851da4..85a55b88 100644 --- a/Pod/BVRecommendations/BVRecsAnalyticsHelper.m +++ b/Pod/BVRecommendations/BVRecsAnalyticsHelper.m @@ -7,9 +7,9 @@ #import "BVRecsAnalyticsHelper.h" #import "BVAnalyticsManager.h" +#import "BVSDKConfiguration.h" #include #include -#import "BVSDKConfiguration.h" @implementation BVRecsAnalyticsHelper @@ -17,172 +17,177 @@ @implementation BVRecsAnalyticsHelper static const NSString *bvProductName = @"Recommendations"; -+(NSDictionary *)getRelavantInfoForRecommendationType:(BVRecommendedProduct *)product isVisible:(BOOL)visible { - - NSDictionary* analyticValues = [self getPrivateAnalyticInfoForProduct:product]; - - NSMutableDictionary* info = [NSMutableDictionary dictionaryWithDictionary:analyticValues]; - - [info addEntriesFromDictionary:@{ - @"bvProduct": bvProductName, - @"productId": product.productId, - @"sponsored" : product.sponsored ? @"true" : @"false", - @"visible": visible ? @"true" : @"false", - }]; - - return info; ++ (NSDictionary *)getRelavantInfoForRecommendationType: + (BVRecommendedProduct *)product + isVisible:(BOOL)visible { + + NSDictionary *analyticValues = + [self getPrivateAnalyticInfoForProduct:product]; + NSMutableDictionary *info = + [NSMutableDictionary dictionaryWithDictionary:analyticValues]; + + [info addEntriesFromDictionary:@{ + @"bvProduct" : bvProductName, + @"productId" : product.productId, + @"sponsored" : product.sponsored ? @"true" : @"false", + @"visible" : visible ? @"true" : @"false", + }]; + + return info; } -+(NSDictionary*)getRecommendationParams { - return @{ - @"cl": @"Impression", - @"type": @"Recommendation", - @"source": @"recommendation-mob", - @"client": [[[BVSDKManager sharedManager] configuration] clientId], - @"bvProduct": bvProductName - }; ++ (NSDictionary *)getRecommendationParams { + return @{ + @"cl" : @"Impression", + @"type" : @"Recommendation", + @"source" : @"recommendation-mob", + @"client" : [[[BVSDKManager sharedManager] configuration] clientId], + @"bvProduct" : bvProductName + }; } -+(NSDictionary*)getUsedFeatureParams { - return @{ - @"cl": @"Feature", - @"type": @"Used", - @"source": @"recommendation-mob", - @"client": [[[BVSDKManager sharedManager]configuration] clientId], - @"bvProduct": bvProductName - }; ++ (NSDictionary *)getUsedFeatureParams { + return @{ + @"cl" : @"Feature", + @"type" : @"Used", + @"source" : @"recommendation-mob", + @"client" : [[[BVSDKManager sharedManager] configuration] clientId], + @"bvProduct" : bvProductName + }; } -+ (NSDictionary*)getPageViewEmbeddedEventParams { - return @{ - @"cl": @"PageView", - @"type": @"Embedded", - @"source": @"recommendation-mob", - @"client": [[[BVSDKManager sharedManager] configuration] clientId], - @"bvProduct": bvProductName - }; ++ (NSDictionary *)getPageViewEmbeddedEventParams { + return @{ + @"cl" : @"PageView", + @"type" : @"Embedded", + @"source" : @"recommendation-mob", + @"client" : [[[BVSDKManager sharedManager] configuration] clientId], + @"bvProduct" : bvProductName + }; } -+ (NSDictionary *)getFieldForWidgetType:(BVProductRecommendationWidget)widgetType{ - - switch (widgetType) { - case RecommendationsCarousel: - return @{@"component" : @"carousel"}; - case RecommendationsTableView: - return @{@"component" : @"tableview"}; - case RecommendationsCustom: - return @{@"component" : @"custom"}; - } - ++ (NSDictionary *)getFieldForWidgetType: + (BVProductRecommendationWidget)widgetType { + + switch (widgetType) { + case RecommendationsCarousel: + return @{@"component" : @"carousel"}; + case RecommendationsTableView: + return @{@"component" : @"tableview"}; + case RecommendationsCustom: + return @{@"component" : @"custom"}; + } } ++ (NSString *)getWidgetTypeString:(BVProductRecommendationWidget)widgetType { -+ (NSString *)getWidgetTypeString:(BVProductRecommendationWidget)widgetType{ - - switch (widgetType) { - case RecommendationsCarousel: - return @"carousel"; - case RecommendationsTableView: - return @"tableview"; - case RecommendationsCustom: - return @"custom"; - } - + switch (widgetType) { + case RecommendationsCarousel: + return @"carousel"; + case RecommendationsTableView: + return @"tableview"; + case RecommendationsCustom: + return @"custom"; + } } -+(NSDictionary*)getRecommendationContainerType:(NSDictionary *)productRec { - - NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; - [parameters addEntriesFromDictionary:[self getRecommendationParams]]; - [parameters addEntriesFromDictionary:productRec]; - return parameters; - ++ (NSDictionary *)getRecommendationContainerType:(NSDictionary *)productRec { + + NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; + [parameters addEntriesFromDictionary:[self getRecommendationParams]]; + [parameters addEntriesFromDictionary:productRec]; + return parameters; } ++ (void)queueAnalyticsEventForProductView:(BVRecommendedProduct *)product { -+(void)queueAnalyticsEventForProductView:(BVRecommendedProduct *)product{ - - if (!product){ - return; - } - - NSMutableDictionary* event = [NSMutableDictionary dictionaryWithDictionary:[self getRelavantInfoForRecommendationType:product isVisible:YES]]; - [event addEntriesFromDictionary:[self getRecommendationParams]]; - [[BVAnalyticsManager sharedManager] queueEvent:event]; - -} + if (!product) { + return; + } -+ (void)queueAnalyticsEventForRecommendationsOnPage:(BVProductRecommendationWidget)widgetType{ - - NSMutableDictionary* event = [NSMutableDictionary dictionaryWithDictionary:[self getUsedFeatureParams]]; - [event addEntriesFromDictionary:[self getFieldForWidgetType:widgetType]]; - [event setObject:@"InView" forKey:@"name"]; - [[BVAnalyticsManager sharedManager] queueEvent:event]; - + NSMutableDictionary *event = [NSMutableDictionary + dictionaryWithDictionary:[self + getRelavantInfoForRecommendationType:product + isVisible:YES]]; + [event addEntriesFromDictionary:[self getRecommendationParams]]; + [[BVAnalyticsManager sharedManager] queueEvent:event]; } ++ (void)queueAnalyticsEventForRecommendationsOnPage: + (BVProductRecommendationWidget)widgetType { + + NSMutableDictionary *event = [NSMutableDictionary + dictionaryWithDictionary:[self getUsedFeatureParams]]; + [event addEntriesFromDictionary:[self getFieldForWidgetType:widgetType]]; + [event setObject:@"InView" forKey:@"name"]; + [[BVAnalyticsManager sharedManager] queueEvent:event]; +} ++ (void)queueAnalyticsEventForWidgetScroll: + (BVProductRecommendationWidget)widgetType { -+ (void)queueAnalyticsEventForWidgetScroll:(BVProductRecommendationWidget)widgetType{ - - NSMutableDictionary *event = [NSMutableDictionary dictionaryWithDictionary:[self getUsedFeatureParams]]; - [event addEntriesFromDictionary:[self getFieldForWidgetType:widgetType]]; - [event setObject:@"Scrolled" forKey:@"name"]; - [[BVAnalyticsManager sharedManager] queueEvent:event]; - + NSMutableDictionary *event = [NSMutableDictionary + dictionaryWithDictionary:[self getUsedFeatureParams]]; + [event addEntriesFromDictionary:[self getFieldForWidgetType:widgetType]]; + [event setObject:@"Scrolled" forKey:@"name"]; + [[BVAnalyticsManager sharedManager] queueEvent:event]; } -+ (void)queueAnalyticsEventForProductTapped:(BVRecommendedProduct *)product{ - - if (!product){ - return; - } ++ (void)queueAnalyticsEventForProductTapped:(BVRecommendedProduct *)product { + + if (!product) { + return; + } - NSMutableDictionary *event = [NSMutableDictionary dictionaryWithDictionary:[self getUsedFeatureParams]]; - [event addEntriesFromDictionary:[self getRelavantInfoForRecommendationType:product isVisible:true]]; - [event setObject:@"ContentClick" forKey:@"name"]; - [[BVAnalyticsManager sharedManager] queueEvent:event]; - + NSMutableDictionary *event = [NSMutableDictionary + dictionaryWithDictionary:[self getUsedFeatureParams]]; + [event + addEntriesFromDictionary:[self + getRelavantInfoForRecommendationType:product + isVisible:true]]; + [event setObject:@"ContentClick" forKey:@"name"]; + [[BVAnalyticsManager sharedManager] queueEvent:event]; } ++ (void)queueEmbeddedRecommendationsPageViewEvent: + (BVRecommendationsRequest *)recommendationsRequest + withWidgetType: + (BVProductRecommendationWidget) + widgetType { -+ (void)queueEmbeddedRecommendationsPageViewEvent:(BVRecommendationsRequest *)recommendationsRequest - withWidgetType:(BVProductRecommendationWidget)widgetType { - - NSMutableDictionary* event = [NSMutableDictionary dictionary]; - - [event addEntriesFromDictionary:[self getPageViewEmbeddedEventParams]]; - [event setObject:[self getWidgetTypeString:widgetType] forKey:@"reportingGroup"]; - [event setValue:recommendationsRequest.productId forKey:@"productId"]; - [event setValue:recommendationsRequest.categoryId forKey:@"categoryId"]; - - [[BVAnalyticsManager sharedManager] queueEvent:event]; - + NSMutableDictionary *event = [NSMutableDictionary dictionary]; + + [event addEntriesFromDictionary:[self getPageViewEmbeddedEventParams]]; + [event setObject:[self getWidgetTypeString:widgetType] + forKey:@"reportingGroup"]; + [event setValue:recommendationsRequest.productId forKey:@"productId"]; + [event setValue:recommendationsRequest.categoryId forKey:@"categoryId"]; + + [[BVAnalyticsManager sharedManager] queueEvent:event]; } -+(NSDictionary*)getPrivateAnalyticInfoForProduct:(BVRecommendedProduct*)product { - - NSMutableDictionary* productAnalytics = [NSMutableDictionary dictionary]; - - if (product.rawProductDict){ - - for (NSString* key in @[@"RKB", @"RKI", @"RKP", @"RKT", @"RKC"]) { - - if ([product.rawProductDict objectForKey:key] != nil){ - NSNumber* value = @([[product.rawProductDict objectForKey:key] integerValue]); - [productAnalytics setObject:value forKey:key]; - } - - } - ++ (NSDictionary *)getPrivateAnalyticInfoForProduct: + (BVRecommendedProduct *)product { + + NSMutableDictionary *productAnalytics = [NSMutableDictionary dictionary]; + + if (product.rawProductDict) { + + for (NSString *key in @[ @"RKB", @"RKI", @"RKP", @"RKT", @"RKC" ]) { + + if ([product.rawProductDict objectForKey:key] != nil) { + NSNumber *value = + @([[product.rawProductDict objectForKey:key] integerValue]); + [productAnalytics setObject:value forKey:key]; + } } - - NSString* rs = [product.rawProductDict objectForKey:@"RS"]; - [productAnalytics setObject:rs ? rs : [NSNull null] forKey:@"RS"]; - - return productAnalytics; - + } + + NSString *rs = [product.rawProductDict objectForKey:@"RS"]; + [productAnalytics setObject:rs ? rs : [NSNull null] forKey:@"RS"]; + + return productAnalytics; } @end diff --git a/Pod/BVRecommendations/BVShopperProfile.h b/Pod/BVRecommendations/BVShopperProfile.h index cb225585..3d8d3dd2 100644 --- a/Pod/BVRecommendations/BVShopperProfile.h +++ b/Pod/BVRecommendations/BVShopperProfile.h @@ -8,44 +8,38 @@ #ifndef BVShopperProfile_h #define BVShopperProfile_h -#import #import "BVRecommendedProduct.h" +#import -NS_ASSUME_NONNULL_BEGIN - - -/// Data model for a user's shopper profile including: interests, brands, and product recommendations +/// Data model for a user's shopper profile including: interests, brands, and +/// product recommendations @interface BVShopperProfile : NSObject - /** - Builds the model based on the JSON API response from the BV recommendations API + Builds the model based on the JSON API response from the BV recommendations + API @param apiResponse JSON response from BV recommendations API @return The initialzed shopper profile model with product `recommendations`. */ -- (id)initWithDictionary:(NSDictionary *)apiResponse; - - -/// Brands the user likes. Dictionary where keys are brands and values are strenght, specified by strings: HIGH | MED | LOW -@property (strong, nonatomic) NSDictionary *brands; +- (nonnull id)initWithDictionary:(nonnull NSDictionary *)apiResponse; +/// Brands the user likes. Dictionary where keys are brands and values are +/// strenght, specified by strings: HIGH | MED | LOW +@property(nonnull, strong, nonatomic) NSDictionary *brands; /// Dictionary of user's interests -@property (strong, nonatomic) NSDictionary *interests; - +@property(nonnull, strong, nonatomic) NSDictionary *interests; /// Array of BVRecommendedProduct objects, recommended for the user. -@property (strong, nonatomic) NSArray *recommendations; - - -/// Product recommendation keys associated with the recommended BVRecommendedProduct(s). -@property (strong, nonatomic) NSSet *product_keys; // Array of keys by product +@property(nonnull, strong, nonatomic) NSArray *recommendations; +/// Product recommendation keys associated with the recommended +/// BVRecommendedProduct(s). +@property(nonnull, strong, nonatomic) + NSSet *product_keys; // Array of keys by product @end -NS_ASSUME_NONNULL_END - #endif diff --git a/Pod/BVRecommendations/BVShopperProfile.m b/Pod/BVRecommendations/BVShopperProfile.m index acbf7125..29628075 100644 --- a/Pod/BVRecommendations/BVShopperProfile.m +++ b/Pod/BVRecommendations/BVShopperProfile.m @@ -9,69 +9,68 @@ @implementation BVShopperProfile +- (id)init { -- (id)init{ - - self = [super init]; - - self.brands = [NSDictionary dictionary]; - self.interests = [NSDictionary dictionary]; - self.recommendations = [NSArray array]; - self.product_keys = [NSSet set]; - - return self; + self = [super init]; + + self.brands = [NSDictionary dictionary]; + self.interests = [NSDictionary dictionary]; + self.recommendations = [NSArray array]; + self.product_keys = [NSSet set]; + + return self; } -- (id)initWithDictionary:(NSDictionary *)apiResponse{ - - self = [self init]; - - NSDictionary *profile = [apiResponse objectForKey:@"profile"]; - - if (profile){ - - self.brands = [profile objectForKey:@"brands"]; - - self.interests = [profile objectForKey:@"interests"]; - - NSDictionary* recommendationStats = [profile objectForKey:@"recommendationStats"]; - - NSSet *recommendationProductIds = [profile objectForKey:@"recommendations"]; - - NSDictionary* products = [profile objectForKey:@"products"]; - - NSMutableArray *tmp = [NSMutableArray array]; - - if (recommendationProductIds && products) { - - self.product_keys = recommendationProductIds; - - for (NSString* productKey in recommendationProductIds) { - - NSDictionary* product = [products objectForKey:productKey]; - - BVRecommendedProduct *bvProduct = [[BVRecommendedProduct alloc] initWithDictionary:product withRecommendationStats:recommendationStats]; - - if (bvProduct){ - [tmp addObject:bvProduct]; - } - - } - +- (id)initWithDictionary:(NSDictionary *)apiResponse { + + self = [self init]; + + NSDictionary *profile = [apiResponse objectForKey:@"profile"]; + + if (profile) { + + self.brands = [profile objectForKey:@"brands"]; + + self.interests = [profile objectForKey:@"interests"]; + + NSDictionary *recommendationStats = + [profile objectForKey:@"recommendationStats"]; + + NSSet *recommendationProductIds = [profile objectForKey:@"recommendations"]; + + NSDictionary *products = [profile objectForKey:@"products"]; + + NSMutableArray *tmp = [NSMutableArray array]; + + if (recommendationProductIds && products) { + + self.product_keys = recommendationProductIds; + + for (NSString *productKey in recommendationProductIds) { + + NSDictionary *product = [products objectForKey:productKey]; + + BVRecommendedProduct *bvProduct = [[BVRecommendedProduct alloc] + initWithDictionary:product + withRecommendationStats:recommendationStats]; + + if (bvProduct) { + [tmp addObject:bvProduct]; } - - self.recommendations = [NSArray arrayWithArray:tmp]; + } } - - return self; - + + self.recommendations = [NSArray arrayWithArray:tmp]; + } + + return self; } +- (NSString *)description { -- (NSString *)description{ - - return [NSString stringWithFormat:@"BVShopperProfile: Interests:%@\nBrands:%@", self.interests, self.brands]; - + return + [NSString stringWithFormat:@"BVShopperProfile: Interests:%@\nBrands:%@", + self.interests, self.brands]; } @end diff --git a/Pod/BVRecommendations/BVShopperProfileRequestCache.m b/Pod/BVRecommendations/BVShopperProfileRequestCache.m index 3836cc91..a6792755 100644 --- a/Pod/BVRecommendations/BVShopperProfileRequestCache.m +++ b/Pod/BVRecommendations/BVShopperProfileRequestCache.m @@ -8,57 +8,67 @@ #import "BVShopperProfileRequestCache.h" #import "BVLogger.h" -NSString * const CACHE_DATE_KEY = @"cache date"; +NSString *const CACHE_DATE_KEY = @"cache date"; @implementation BVShopperProfileRequestCache -+(instancetype)sharedCache -{ - static dispatch_once_t p = 0; - __strong static id _sharedObject = nil; - dispatch_once(&p, ^{ - _sharedObject = [[self alloc] init]; - }); - - return _sharedObject; ++ (instancetype)sharedCache { + static dispatch_once_t p = 0; + __strong static id _sharedObject = nil; + dispatch_once(&p, ^{ + _sharedObject = [[self alloc] init]; + }); + + return _sharedObject; } +- (id)init { --(id)init { - - _cacheMaxAgeInSeconds = 60; - return [super initWithMemoryCapacity:(2*1024*1024) diskCapacity:0 diskPath:@"com.bv.recs.cache"]; - + _cacheMaxAgeInSeconds = 60; + return [super initWithMemoryCapacity:(2 * 1024 * 1024) + diskCapacity:0 + diskPath:@"com.bv.recs.cache"]; } +- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { --(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { - - NSCachedURLResponse * cachedResponse = [super cachedResponseForRequest:request]; - if (cachedResponse) { - NSDate* cacheDate = [[cachedResponse userInfo] objectForKey:CACHE_DATE_KEY]; - if ([cacheDate timeIntervalSinceNow] < -(_cacheMaxAgeInSeconds) || cacheDate == nil) { - [self removeCachedResponseForRequest:request]; - cachedResponse = nil; - } + NSCachedURLResponse *cachedResponse = + [super cachedResponseForRequest:request]; + if (cachedResponse) { + NSDate *cacheDate = [[cachedResponse userInfo] objectForKey:CACHE_DATE_KEY]; + if ([cacheDate timeIntervalSinceNow] < -(_cacheMaxAgeInSeconds) || + cacheDate == nil) { + [self removeCachedResponseForRequest:request]; + cachedResponse = nil; } - - return cachedResponse; + } + + return cachedResponse; } -- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request { - - NSMutableDictionary *userInfo = cachedResponse.userInfo ? [cachedResponse.userInfo mutableCopy] : [NSMutableDictionary dictionary]; - [userInfo setObject:[NSDate date] forKey:CACHE_DATE_KEY]; - NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy]; - - [super storeCachedResponse:newCachedResponse forRequest:request]; +- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse + forRequest:(NSURLRequest *)request { + + NSMutableDictionary *userInfo = cachedResponse.userInfo + ? [cachedResponse.userInfo mutableCopy] + : [NSMutableDictionary dictionary]; + [userInfo setObject:[NSDate date] forKey:CACHE_DATE_KEY]; + NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] + initWithResponse:cachedResponse.response + data:cachedResponse.data + userInfo:userInfo + storagePolicy:cachedResponse.storagePolicy]; + + [super storeCachedResponse:newCachedResponse forRequest:request]; } -- (void)printCacheSize{ - - [[BVLogger sharedLogger] verbose:[NSString stringWithFormat:@"NSURLCache Memory/Disk Size: %ld/%ld (bytes)", (unsigned long)[self currentMemoryUsage], (unsigned long)[self currentDiskUsage]]]; - +- (void)printCacheSize { + + [[BVLogger sharedLogger] + verbose:[NSString stringWithFormat: + @"NSURLCache Memory/Disk Size: %ld/%ld (bytes)", + (unsigned long)[self currentMemoryUsage], + (unsigned long)[self currentDiskUsage]]]; } @end \ No newline at end of file diff --git a/Pod/BVRecommendations/Private/BVShopperProfileRequestCache.h b/Pod/BVRecommendations/Private/BVShopperProfileRequestCache.h index 7f050e0d..4a20f751 100644 --- a/Pod/BVRecommendations/Private/BVShopperProfileRequestCache.h +++ b/Pod/BVRecommendations/Private/BVShopperProfileRequestCache.h @@ -10,11 +10,12 @@ // For internal use only. @interface BVShopperProfileRequestCache : NSURLCache -+(instancetype)sharedCache; ++ (instancetype)sharedCache; --(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; +- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; -- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request; +- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse + forRequest:(NSURLRequest *)request; - (void)printCacheSize; diff --git a/Tests/BVPixelTests.m b/Tests/BVPixelTests.m index 589f4d42..9b323ad6 100644 --- a/Tests/BVPixelTests.m +++ b/Tests/BVPixelTests.m @@ -7,668 +7,735 @@ #import -#import "BVBaseStubTestCase.h" -#import "BVSDKManager.h" #import "BVAnalyticsManager.h" +#import "BVBaseStubTestCase.h" #import "BVSDKConfiguration.h" +#import "BVSDKManager.h" -#define ANALYTICS_TEST_USING_MOCK_DATA 1 // Setting to 1 uses mock result. Set to 0 to make network request. +#define ANALYTICS_TEST_USING_MOCK_DATA \ + 1 // Setting to 1 uses mock result. Set to 0 to make network request. @interface BVAnalyticsManager (TestAccessors) - @property (strong) NSMutableArray* eventQueue; +@property(strong) NSMutableArray *eventQueue; @end @interface NSDictionary (DictionaryTest) --(BOOL)containsDictionary:(NSDictionary *)otherDictionary; +- (BOOL)containsDictionary:(NSDictionary *)otherDictionary; @end @implementation NSDictionary (DictionaryTest) --(BOOL)containsDictionary:(NSDictionary *)otherDictionary{ - - BOOL contains = YES; - - for (NSString *otherKey in otherDictionary){ - NSObject *otherValue = [otherDictionary objectForKey:otherKey]; - - // test - NSObject *testValue = [self objectForKey:otherKey]; - if (![testValue isEqual:otherValue]){ - contains = NO; - break; - - } +- (BOOL)containsDictionary:(NSDictionary *)otherDictionary { + + BOOL contains = YES; + + for (NSString *otherKey in otherDictionary) { + NSObject *otherValue = [otherDictionary objectForKey:otherKey]; + + // test + NSObject *testValue = [self objectForKey:otherKey]; + if (![testValue isEqual:otherValue]) { + contains = NO; + break; } - - return contains; + } + + return contains; } @end - - -@interface BVPixelTests : BVBaseStubTestCase -{ - XCTestExpectation *impressionExpectation; - XCTestExpectation *pageviewExpectation; - int numberOfExpectedImpressionAnalyticsEvents; - int numberOfExpectedPageviewAnalyticsEvents; +@interface BVPixelTests : BVBaseStubTestCase { + XCTestExpectation *impressionExpectation; + XCTestExpectation *pageviewExpectation; + int numberOfExpectedImpressionAnalyticsEvents; + int numberOfExpectedPageviewAnalyticsEvents; } @end @implementation BVPixelTests - (void)setUp { - [super setUp]; - - [[BVAnalyticsManager sharedManager] setFlushInterval:0.1]; - - NSDictionary *configDict = @{@"clientId": @"mobileBVPixelTestsiOS"}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeProd]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelAnalyticsOnly]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(analyticsPageviewEventCompleted:) - name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(analyticsImpressionEventCompleted:) - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; - - impressionExpectation = [self expectationWithDescription:@"Expecting impression analytics events"]; - pageviewExpectation = [self expectationWithDescription:@"Expecting pageview analytics events"]; - numberOfExpectedImpressionAnalyticsEvents = 0; - numberOfExpectedPageviewAnalyticsEvents = 0; + [super setUp]; + + [[BVAnalyticsManager sharedManager] setFlushInterval:0.1]; + + NSDictionary *configDict = @{@"clientId" : @"mobileBVPixelTestsiOS"}; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeProd]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelAnalyticsOnly]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(analyticsPageviewEventCompleted:) + name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(analyticsImpressionEventCompleted:) + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; + + impressionExpectation = [self + expectationWithDescription:@"Expecting impression analytics events"]; + pageviewExpectation = + [self expectationWithDescription:@"Expecting pageview analytics events"]; + numberOfExpectedImpressionAnalyticsEvents = 0; + numberOfExpectedPageviewAnalyticsEvents = 0; } --(void)tearDown { - - [super tearDown]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; - +- (void)tearDown { + + [super tearDown]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" + object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; } --(void)waitForAnalytics { - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - }]; +- (void)waitForAnalytics { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } + }]; } --(void)analyticsImpressionEventCompleted:(NSNotification *)notification { - - NSLog(@"analytics impression event fired in tests: %i", numberOfExpectedImpressionAnalyticsEvents); - - NSError *err = (NSError *)[notification object]; - if (err){ - XCTFail(@"ERROR: Analytic event failed %@", err); - } else { - NSLog(@"Analytic Impression HTTP success."); - } - - numberOfExpectedImpressionAnalyticsEvents -= 1; - [self checkComplete]; - +- (void)analyticsImpressionEventCompleted:(NSNotification *)notification { + + NSLog(@"analytics impression event fired in tests: %i", + numberOfExpectedImpressionAnalyticsEvents); + + NSError *err = (NSError *)[notification object]; + if (err) { + XCTFail(@"ERROR: Analytic event failed %@", err); + } else { + NSLog(@"Analytic Impression HTTP success."); + } + + numberOfExpectedImpressionAnalyticsEvents -= 1; + [self checkComplete]; } --(void)analyticsPageviewEventCompleted:(NSNotification *)notification { - - NSLog(@"analytics pageview event fired in tests: %i", numberOfExpectedPageviewAnalyticsEvents); - - NSError *err = (NSError *)[notification object]; - if (err){ - XCTFail(@"ERROR: Analytic event failed %@", err); - } else { - NSLog(@"Analytic Page View HTTP success."); - } - - numberOfExpectedPageviewAnalyticsEvents -= 1; - [self checkComplete]; - +- (void)analyticsPageviewEventCompleted:(NSNotification *)notification { + + NSLog(@"analytics pageview event fired in tests: %i", + numberOfExpectedPageviewAnalyticsEvents); + + NSError *err = (NSError *)[notification object]; + if (err) { + XCTFail(@"ERROR: Analytic event failed %@", err); + } else { + NSLog(@"Analytic Page View HTTP success."); + } + + numberOfExpectedPageviewAnalyticsEvents -= 1; + [self checkComplete]; } --(void)checkComplete { - if(numberOfExpectedImpressionAnalyticsEvents == 0){ - [impressionExpectation fulfill]; - numberOfExpectedImpressionAnalyticsEvents = -1; - } - if(numberOfExpectedPageviewAnalyticsEvents == 0){ - [pageviewExpectation fulfill]; - numberOfExpectedPageviewAnalyticsEvents = -1; - } +- (void)checkComplete { + if (numberOfExpectedImpressionAnalyticsEvents == 0) { + [impressionExpectation fulfill]; + numberOfExpectedImpressionAnalyticsEvents = -1; + } + if (numberOfExpectedPageviewAnalyticsEvents == 0) { + [pageviewExpectation fulfill]; + numberOfExpectedPageviewAnalyticsEvents = -1; + } } - (void)checkCommonEventParams:(NSDictionary *)eventDict { - - NSString *source = [eventDict objectForKey:@"source"]; - NSString *idfa = [eventDict objectForKey:@"advertisingId"]; - NSString *mobileSource = [eventDict objectForKey:@"mobileSource"]; - NSString *UA = [eventDict objectForKey:@"UA"]; - NSString *clientId = [eventDict objectForKey:@"client"]; - NSString *hashedIP = [eventDict objectForKey:@"HashedIP"]; - - XCTAssertTrue([source isEqualToString:@"native-mobile-sdk"]); - XCTAssertTrue([mobileSource isEqualToString:@"bv-ios-sdk"]); - XCTAssertTrue([clientId isEqualToString:[BVSDKManager sharedManager].configuration.clientId]); - XCTAssertTrue([hashedIP isEqualToString:@"default"]); - - XCTAssertNotNil(idfa); - XCTAssertNotNil(UA); + + NSString *source = [eventDict objectForKey:@"source"]; + NSString *idfa = [eventDict objectForKey:@"advertisingId"]; + NSString *mobileSource = [eventDict objectForKey:@"mobileSource"]; + NSString *UA = [eventDict objectForKey:@"UA"]; + NSString *clientId = [eventDict objectForKey:@"client"]; + NSString *hashedIP = [eventDict objectForKey:@"HashedIP"]; + + XCTAssertTrue([source isEqualToString:@"native-mobile-sdk"]); + XCTAssertTrue([mobileSource isEqualToString:@"bv-ios-sdk"]); + XCTAssertTrue([clientId + isEqualToString:[BVSDKManager sharedManager].configuration.clientId]); + XCTAssertTrue([hashedIP isEqualToString:@"default"]); + + XCTAssertNotNil(idfa); + XCTAssertNotNil(UA); } +- (void)testProductPageViewEventParameters { + numberOfExpectedImpressionAnalyticsEvents = 0; + numberOfExpectedPageviewAnalyticsEvents = 1; -- (void)testProductPageViewEventParameters { - - numberOfExpectedImpressionAnalyticsEvents = 0; - numberOfExpectedPageviewAnalyticsEvents = 1; - - NSDictionary *testValues = @{@"productId":@"12345", - @"categoryId":@"testCategoryId", - @"rootCategoryId":@"electronics"}; - - BVPageViewEvent *pvEvent = [[BVPageViewEvent alloc] - initWithProductId:[testValues objectForKey:@"productId"] - withBVPixelProductType:BVPixelProductTypeConversationsReviews - withBrand:nil - withCategoryId:[testValues objectForKey:@"categoryId"] - withRootCategoryId:[testValues objectForKey:@"rootCategoryId"] - withAdditionalParams:nil]; - - NSDictionary *eventDict = [pvEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:PRODUCT_PAGEVIEW_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - [self checkCommonEventParams:eventDict]; - - // Fire the PageView event - [BVPixel trackEvent:pvEvent]; - - [self waitForAnalytics]; - -} + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"categoryId" : @"testCategoryId", + @"rootCategoryId" : @"electronics" + }; + + BVPageViewEvent *pvEvent = [[BVPageViewEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withBVPixelProductType:BVPixelProductTypeConversationsReviews + withBrand:nil + withCategoryId:[testValues objectForKey:@"categoryId"] + withRootCategoryId:[testValues objectForKey:@"rootCategoryId"] + withAdditionalParams:nil]; + NSDictionary *eventDict = [pvEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:PRODUCT_PAGEVIEW_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + [self checkCommonEventParams:eventDict]; + + // Fire the PageView event + [BVPixel trackEvent:pvEvent]; + + [self waitForAnalytics]; +} - (void)testImpressionEventParametersReview { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{ - @"productId":@"12345", - @"contentId":@"9876", - @"categoryId":@"catId", - @"contentType":@"Review", - @"bvProduct":@"RatingsAndReviews" - }; - - BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] - withContentId:[testValues objectForKey:@"contentId"] - withCategoryId:[testValues objectForKey:@"categoryId"] - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeReview - withBrand:nil withAdditionalParams:nil]; - - - NSDictionary *eventDict = [reviewImpression toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:reviewImpression]; - - [self waitForAnalytics]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"contentId" : @"9876", + @"categoryId" : @"catId", + @"contentType" : @"Review", + @"bvProduct" : @"RatingsAndReviews" + }; + + BVImpressionEvent *reviewImpression = [[BVImpressionEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withContentId:[testValues objectForKey:@"contentId"] + withCategoryId:[testValues objectForKey:@"categoryId"] + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeReview + withBrand:nil + withAdditionalParams:nil]; + + NSDictionary *eventDict = [reviewImpression toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:reviewImpression]; + + [self waitForAnalytics]; } - (void)testImpressionEventParametersQuestion { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{ - @"productId":@"12345", - @"contentId":@"9876", - @"categoryId":@"catId", - @"contentType":@"Question", - @"bvProduct":@"RatingsAndReviews" - }; - - BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] - withContentId:[testValues objectForKey:@"contentId"] - withCategoryId:[testValues objectForKey:@"categoryId"] - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeQuestion - withBrand:nil withAdditionalParams:nil]; - - - NSDictionary *eventDict = [questionImpression toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:questionImpression]; - - [self waitForAnalytics]; -} + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; -- (void)testImpressionEventParametersAnswer { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{ - @"productId":@"12345", - @"contentId":@"9876", - @"categoryId":@"catId", - @"contentType":@"Answer", - @"bvProduct":@"RatingsAndReviews" - }; - - BVImpressionEvent *answerImpression = [[BVImpressionEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] - withContentId:[testValues objectForKey:@"contentId"] - withCategoryId:[testValues objectForKey:@"categoryId"] - withProductType:BVPixelProductTypeConversationsReviews - withContentType:BVPixelImpressionContentTypeAnswer - withBrand:nil withAdditionalParams:nil]; - - - NSDictionary *eventDict = [answerImpression toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:answerImpression]; - - [self waitForAnalytics]; + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"contentId" : @"9876", + @"categoryId" : @"catId", + @"contentType" : @"Question", + @"bvProduct" : @"RatingsAndReviews" + }; + + BVImpressionEvent *questionImpression = [[BVImpressionEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withContentId:[testValues objectForKey:@"contentId"] + withCategoryId:[testValues objectForKey:@"categoryId"] + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeQuestion + withBrand:nil + withAdditionalParams:nil]; + + NSDictionary *eventDict = [questionImpression toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:questionImpression]; + + [self waitForAnalytics]; } +- (void)testImpressionEventParametersAnswer { + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; -- (void)testViewedCGC { + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"contentId" : @"9876", + @"categoryId" : @"catId", + @"contentType" : @"Answer", + @"bvProduct" : @"RatingsAndReviews" + }; - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{ - @"productId":@"12345", - @"categoryId":@"catId", - @"bvProduct":@"RatingsAndReviews" - }; - - BVViewedCGCEvent *viewedCGCEvent = [[BVViewedCGCEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] - withRootCategoryID:nil - withCategoryId:[testValues objectForKey:@"categoryId"] - withProductType:BVPixelProductTypeConversationsReviews - withBrand:nil - withAdditionalParams:nil]; - - NSDictionary *eventDict = [viewedCGCEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:VIEWED_CGC_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:viewedCGCEvent]; - - [self waitForAnalytics]; + BVImpressionEvent *answerImpression = [[BVImpressionEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withContentId:[testValues objectForKey:@"contentId"] + withCategoryId:[testValues objectForKey:@"categoryId"] + withProductType:BVPixelProductTypeConversationsReviews + withContentType:BVPixelImpressionContentTypeAnswer + withBrand:nil + withAdditionalParams:nil]; + NSDictionary *eventDict = [answerImpression toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:UGC_IMPRESSION_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:answerImpression]; + + [self waitForAnalytics]; } +- (void)testViewedCGC { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"categoryId" : @"catId", + @"bvProduct" : @"RatingsAndReviews" + }; + + BVViewedCGCEvent *viewedCGCEvent = [[BVViewedCGCEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withRootCategoryID:nil + withCategoryId:[testValues objectForKey:@"categoryId"] + withProductType:BVPixelProductTypeConversationsReviews + withBrand:nil + withAdditionalParams:nil]; + + NSDictionary *eventDict = [viewedCGCEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:VIEWED_CGC_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:viewedCGCEvent]; + + [self waitForAnalytics]; +} - (void)testFeatureUsedProfile { - - [[BVAnalyticsManager sharedManager] setFlushInterval:0.1]; - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *extraParams = @{@"interaction":@"false", - @"page":@"authorId"}; - - NSDictionary *testValues = @{ - @"productId":@"none", - @"name":@"Profile", - @"bvProduct":@"Profiles" - }; - - BVFeatureUsedEvent *profileEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"none" - withBrand:nil - withProductType:BVPixelProductTypeConversationsProfile - withEventName:BVPixelFeatureUsedNameProfile - withAdditionalParams:extraParams]; - - NSDictionary *eventDict = [profileEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - // Test extra params - contains = [eventDict containsDictionary:extraParams]; - XCTAssertTrue(contains, "Were the extra params for the profile added correctly?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:profileEvent]; - - [self waitForAnalytics]; + + [[BVAnalyticsManager sharedManager] setFlushInterval:0.1]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *extraParams = + @{@"interaction" : @"false", @"page" : @"authorId"}; + + NSDictionary *testValues = @{ + @"productId" : @"none", + @"name" : @"Profile", + @"bvProduct" : @"Profiles" + }; + + BVFeatureUsedEvent *profileEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"none" + withBrand:nil + withProductType:BVPixelProductTypeConversationsProfile + withEventName:BVPixelFeatureUsedNameProfile + withAdditionalParams:extraParams]; + + NSDictionary *eventDict = [profileEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + // Test extra params + contains = [eventDict containsDictionary:extraParams]; + XCTAssertTrue(contains, + "Were the extra params for the profile added correctly?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:profileEvent]; + + [self waitForAnalytics]; } --(void)testUsedFeatureWriteReview { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{@"productId":@"12345", - @"bvProduct":@"RatingsAndReviews", - @"name":@"Write", - @"detail1":@"Review" - }; - - NSDictionary *extraParams = @{@"detail1":@"Review"}; - - BVFeatureUsedEvent *writeReviewEvent = [[BVFeatureUsedEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameWriteReview - withAdditionalParams:extraParams]; - - NSDictionary *eventDict = [writeReviewEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test parameter values are in the dictionary - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are the input params in the event?"); - - // Test extra params - contains = [eventDict containsDictionary:extraParams]; - XCTAssertTrue(contains, "Were the extra params for the profile added correctly?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:writeReviewEvent]; - - [self waitForAnalytics]; - +- (void)testUsedFeatureWriteReview { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"bvProduct" : @"RatingsAndReviews", + @"name" : @"Write", + @"detail1" : @"Review" + }; + + NSDictionary *extraParams = @{@"detail1" : @"Review"}; + + BVFeatureUsedEvent *writeReviewEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameWriteReview + withAdditionalParams:extraParams]; + + NSDictionary *eventDict = [writeReviewEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test parameter values are in the dictionary + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, "Are the input params in the event?"); + + // Test extra params + contains = [eventDict containsDictionary:extraParams]; + XCTAssertTrue(contains, + "Were the extra params for the profile added correctly?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:writeReviewEvent]; + + [self waitForAnalytics]; } +- (void)testUsedFeatureFeedbackHelpfulness { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *additionalParams = @{ + @"contentType" : [BVPixelImpressionContentTypeUtil + toString:BVPixelImpressionContentTypeReview], + @"contentId" : @"abcdef", + @"detail1" : @"Positive" + }; + + BVFeatureUsedEvent *feedbackEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"12345" + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withEventName:BVPixelFeatureUsedEventNameFeedback + withAdditionalParams:additionalParams]; + + NSDictionary *eventDict = [feedbackEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); --(void)testUsedFeatureFeedbackHelpfulness { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *additionalParams = @{@"contentType":[BVPixelImpressionContentTypeUtil toString:BVPixelImpressionContentTypeReview], - @"contentId":@"abcdef", - @"detail1":@"Positive"}; - - BVFeatureUsedEvent *feedbackEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"12345" - withBrand:nil - withProductType:BVPixelProductTypeConversationsReviews - withEventName:BVPixelFeatureUsedEventNameFeedback - withAdditionalParams:additionalParams]; - - NSDictionary *eventDict = [feedbackEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test extra params - contains = [eventDict containsDictionary:additionalParams]; - XCTAssertTrue(contains, "Were the extra params for the profile added correctly?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:feedbackEvent]; - - [self waitForAnalytics]; + // Test extra params + contains = [eventDict containsDictionary:additionalParams]; + XCTAssertTrue(contains, + "Were the extra params for the profile added correctly?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:feedbackEvent]; + + [self waitForAnalytics]; } --(void)testUsedFeatureFeedbackInappropriate { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *additionalParams = @{@"contentType":[BVPixelImpressionContentTypeUtil toString:BVPixelImpressionContentTypeQuestion], - @"contentId":@"abcdef", - @"detail1":@"Inappropriate"}; - - BVFeatureUsedEvent *feedbackEvent = [[BVFeatureUsedEvent alloc] initWithProductId:@"12345" - withBrand:nil - withProductType:BVPixelProductTypeConversationsQuestionAnswer - withEventName:BVPixelFeatureUsedEventNameInappropriate - withAdditionalParams:additionalParams]; - - NSDictionary *eventDict = [feedbackEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test extra params - contains = [eventDict containsDictionary:additionalParams]; - XCTAssertTrue(contains, "Were the extra params for the profile added correctly?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:feedbackEvent]; - - [self waitForAnalytics]; - +- (void)testUsedFeatureFeedbackInappropriate { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *additionalParams = @{ + @"contentType" : [BVPixelImpressionContentTypeUtil + toString:BVPixelImpressionContentTypeQuestion], + @"contentId" : @"abcdef", + @"detail1" : @"Inappropriate" + }; + + BVFeatureUsedEvent *feedbackEvent = [[BVFeatureUsedEvent alloc] + initWithProductId:@"12345" + withBrand:nil + withProductType:BVPixelProductTypeConversationsQuestionAnswer + withEventName:BVPixelFeatureUsedEventNameInappropriate + withAdditionalParams:additionalParams]; + + NSDictionary *eventDict = [feedbackEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:USED_FEATURE_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test extra params + contains = [eventDict containsDictionary:additionalParams]; + XCTAssertTrue(contains, + "Were the extra params for the profile added correctly?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:feedbackEvent]; + + [self waitForAnalytics]; } --(void)testInViewEvent { - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSDictionary *testValues = @{@"productId":@"12345", - @"bvProduct":@"RatingsAndReviews", - @"name":@"InView", - @"component":@"ReviewsTableView" - }; - - - BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] initWithProductId:[testValues objectForKey:@"productId"] withBrand:nil withProductType:BVPixelProductTypeConversationsReviews withContainerId:[testValues objectForKey:@"component"] withAdditionalParams:nil]; - - NSDictionary *eventDict = [inViewEvent toRaw]; - - // Test the required schema is defined correctly - BOOL contains = [eventDict containsDictionary:FEATURE_USED_INVIEW_SCHEMA]; - XCTAssertTrue(contains, "Does the default schema exist for this product?"); - - // Test the required schema is defined correctly - contains = [eventDict containsDictionary:testValues]; - XCTAssertTrue(contains, "Are all the test values added to the event properly?"); - - [self checkCommonEventParams:eventDict]; - - [BVPixel trackEvent:inViewEvent]; - - [self waitForAnalytics]; - +- (void)testInViewEvent { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSDictionary *testValues = @{ + @"productId" : @"12345", + @"bvProduct" : @"RatingsAndReviews", + @"name" : @"InView", + @"component" : @"ReviewsTableView" + }; + + BVInViewEvent *inViewEvent = [[BVInViewEvent alloc] + initWithProductId:[testValues objectForKey:@"productId"] + withBrand:nil + withProductType:BVPixelProductTypeConversationsReviews + withContainerId:[testValues objectForKey:@"component"] + withAdditionalParams:nil]; + + NSDictionary *eventDict = [inViewEvent toRaw]; + + // Test the required schema is defined correctly + BOOL contains = [eventDict containsDictionary:FEATURE_USED_INVIEW_SCHEMA]; + XCTAssertTrue(contains, "Does the default schema exist for this product?"); + + // Test the required schema is defined correctly + contains = [eventDict containsDictionary:testValues]; + XCTAssertTrue(contains, + "Are all the test values added to the event properly?"); + + [self checkCommonEventParams:eventDict]; + + [BVPixel trackEvent:inViewEvent]; + + [self waitForAnalytics]; } +- (void)testTransactionConversionNoPII { -- (void)testTransactionConversionNoPII{ - #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSArray* orderItems = @[[[BVTransactionItem alloc]initWithSku:@"123" name:@"test product" category:@"home and garden" price:99.99 quantity:1 imageUrl:nil]]; - BVTransactionEvent *transaction = [[BVTransactionEvent alloc]initWithOrderId:@"testorderid" orderTotal:99.99 orderItems:orderItems andOtherParams:@{@"state":@"TX", @"city": @"Austin"}]; - - - [self checkCommonEventParams:[transaction toRaw]]; - [self checkCommonEventParams:[transaction toRawNonPII]]; - - [BVPixel trackEvent:transaction]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 1, @"Conversions without PII should only produce one event"); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSArray *orderItems = + @[ [[BVTransactionItem alloc] initWithSku:@"123" + name:@"test product" + category:@"home and garden" + price:99.99 + quantity:1 + imageUrl:nil] ]; + BVTransactionEvent *transaction = [[BVTransactionEvent alloc] + initWithOrderId:@"testorderid" + orderTotal:99.99 + orderItems:orderItems + andOtherParams:@{@"state" : @"TX", @"city" : @"Austin"}]; + + [self checkCommonEventParams:[transaction toRaw]]; + [self checkCommonEventParams:[transaction toRawNonPII]]; + + [BVPixel trackEvent:transaction]; + + XCTAssert([[BVAnalyticsManager sharedManager] eventQueue].count == 1, + @"Conversions without PII should only produce one event"); + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testTransactionConversionPII{ - +- (void)testTransactionConversionPII { + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - NSArray* orderItems = @[[[BVTransactionItem alloc]initWithSku:@"123" name:@"test product" category:@"home and garden" price:99.99 quantity:1 imageUrl:nil]]; - BVTransactionEvent *transaction = [[BVTransactionEvent alloc]initWithOrderId:@"testorderid" orderTotal:99.99 orderItems:orderItems andOtherParams:@{@"state":@"TX", @"city": @"Austin", - /*PII*/ @"email":@"some.one@domain.com"}]; - - - NSDictionary *transactionPIIDict = [transaction toRaw]; - NSDictionary *transactionNoPII = [transaction toRawNonPII]; - - [self checkCommonEventParams:transactionPIIDict]; //PII is in event, but IDFA is removed - [self checkCommonEventParams:transactionNoPII]; // PII is not in the event and IDFA is present - - // Default event that has PII (e.g. email) will have set idfa to nontracking - XCTAssertTrue([[transactionPIIDict objectForKey:@"advertisingId"] isEqualToString:@"nontracking"]); - XCTAssertTrue([[transactionPIIDict objectForKey:@"hadPII"] isEqualToString:@"true"]); - XCTAssertTrue([[transactionPIIDict objectForKey:@"email"] isEqualToString:@"some.one@domain.com"]); - - // For the nonPII-created dict, the email would be stripped but idfa presetn - XCTAssertNil([transactionNoPII objectForKey:@"email"]); - XCTAssertTrue([[transactionNoPII objectForKey:@"hadPII"] isEqualToString:@"true"]); - XCTAssertFalse([[transactionNoPII objectForKey:@"advertisingId"] isEqualToString:@"nontracking"]); - - [BVPixel trackEvent:transaction]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 2, @"Conversions with PII should produce two events"); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; - + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + NSArray *orderItems = + @[ [[BVTransactionItem alloc] initWithSku:@"123" + name:@"test product" + category:@"home and garden" + price:99.99 + quantity:1 + imageUrl:nil] ]; + BVTransactionEvent *transaction = + [[BVTransactionEvent alloc] initWithOrderId:@"testorderid" + orderTotal:99.99 + orderItems:orderItems + andOtherParams:@{ + @"state" : @"TX", + @"city" : @"Austin", + /*PII*/ @"email" : @"some.one@domain.com" + }]; + + NSDictionary *transactionPIIDict = [transaction toRaw]; + NSDictionary *transactionNoPII = [transaction toRawNonPII]; + + [self checkCommonEventParams:transactionPIIDict]; // PII is in event, but IDFA + // is removed + [self checkCommonEventParams:transactionNoPII]; // PII is not in the event and + // IDFA is present + + // Default event that has PII (e.g. email) will have set idfa to nontracking + XCTAssertTrue([[transactionPIIDict objectForKey:@"advertisingId"] + isEqualToString:@"nontracking"]); + XCTAssertTrue( + [[transactionPIIDict objectForKey:@"hadPII"] isEqualToString:@"true"]); + XCTAssertTrue([[transactionPIIDict objectForKey:@"email"] + isEqualToString:@"some.one@domain.com"]); + + // For the nonPII-created dict, the email would be stripped but idfa presetn + XCTAssertNil([transactionNoPII objectForKey:@"email"]); + XCTAssertTrue( + [[transactionNoPII objectForKey:@"hadPII"] isEqualToString:@"true"]); + XCTAssertFalse([[transactionNoPII objectForKey:@"advertisingId"] + isEqualToString:@"nontracking"]); + + [BVPixel trackEvent:transaction]; + + XCTAssert([[BVAnalyticsManager sharedManager] eventQueue].count == 2, + @"Conversions with PII should produce two events"); + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testNonTransactionConversionNoPII{ - +- (void)testNonTransactionConversionNoPII { + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - BVConversionEvent *conversion = [[BVConversionEvent alloc]initWithType:@"Broucher Download" value:@"HotSpringsSpas" label:@"TestLabel" otherParams:@{@"state":@"TX", @"city": @"Austin"}]; - - [self checkCommonEventParams:[conversion toRaw]]; - [self checkCommonEventParams:[conversion toRawNonPII]]; - - [BVPixel trackEvent:conversion]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 1, @"Non-transactional Conversions without PII should only produce one event"); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + BVConversionEvent *conversion = [[BVConversionEvent alloc] + initWithType:@"Broucher Download" + value:@"HotSpringsSpas" + label:@"TestLabel" + otherParams:@{@"state" : @"TX", @"city" : @"Austin"}]; + + [self checkCommonEventParams:[conversion toRaw]]; + [self checkCommonEventParams:[conversion toRawNonPII]]; + + [BVPixel trackEvent:conversion]; + + XCTAssert([[BVAnalyticsManager sharedManager] eventQueue].count == 1, + @"Non-transactional Conversions without PII should only produce " + @"one event"); + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testNonTransactionConversionPII{ - +- (void)testNonTransactionConversionPII { + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - BVConversionEvent *conversion = [[BVConversionEvent alloc]initWithType:@"Broucher Download" value:@"HotSpringsSpas" label:@"TestLabel" otherParams:@{@"state":@"TX", @"city": @"Austin", - /*PII*/ @"email":@"some.one@domain.com"}]; - - NSDictionary *conversionPIIDict = [conversion toRaw]; - NSDictionary *conversionNoPII = [conversion toRawNonPII]; - - [self checkCommonEventParams:conversionPIIDict]; //PII is in event, but IDFA is removed - [self checkCommonEventParams:conversionNoPII]; // PII is not in the event and IDFA is present - - // Default event that has PII (e.g. email) will have set idfa to nontracking - XCTAssertTrue([[conversionPIIDict objectForKey:@"advertisingId"] isEqualToString:@"nontracking"]); - XCTAssertTrue([[conversionPIIDict objectForKey:@"hadPII"] isEqualToString:@"true"]); - XCTAssertTrue([[conversionPIIDict objectForKey:@"email"] isEqualToString:@"some.one@domain.com"]); - - // For the nonPII-created dict, the email would be stripped but idfa presetn - XCTAssertNil([conversionNoPII objectForKey:@"email"]); - XCTAssertTrue([[conversionNoPII objectForKey:@"hadPII"] isEqualToString:@"true"]); - XCTAssertFalse([[conversionNoPII objectForKey:@"advertisingId"] isEqualToString:@"nontracking"]); - - [BVPixel trackEvent:conversion]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 2, @"Non-transactional Conversions with PII should produce two events"); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; -} + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + BVConversionEvent *conversion = + [[BVConversionEvent alloc] initWithType:@"Broucher Download" + value:@"HotSpringsSpas" + label:@"TestLabel" + otherParams:@{ + @"state" : @"TX", + @"city" : @"Austin", + /*PII*/ @"email" : @"some.one@domain.com" + }]; + + NSDictionary *conversionPIIDict = [conversion toRaw]; + NSDictionary *conversionNoPII = [conversion toRawNonPII]; + + [self checkCommonEventParams:conversionPIIDict]; // PII is in event, but IDFA + // is removed + [self checkCommonEventParams:conversionNoPII]; // PII is not in the event and + // IDFA is present + + // Default event that has PII (e.g. email) will have set idfa to nontracking + XCTAssertTrue([[conversionPIIDict objectForKey:@"advertisingId"] + isEqualToString:@"nontracking"]); + XCTAssertTrue( + [[conversionPIIDict objectForKey:@"hadPII"] isEqualToString:@"true"]); + XCTAssertTrue([[conversionPIIDict objectForKey:@"email"] + isEqualToString:@"some.one@domain.com"]); + + // For the nonPII-created dict, the email would be stripped but idfa presetn + XCTAssertNil([conversionNoPII objectForKey:@"email"]); + XCTAssertTrue( + [[conversionNoPII objectForKey:@"hadPII"] isEqualToString:@"true"]); + XCTAssertFalse([[conversionNoPII objectForKey:@"advertisingId"] + isEqualToString:@"nontracking"]); + + [BVPixel trackEvent:conversion]; + + XCTAssert( + [[BVAnalyticsManager sharedManager] eventQueue].count == 2, + @"Non-transactional Conversions with PII should produce two events"); + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; +} @end - diff --git a/Tests/Tests/BVBaseStubTestCase.h b/Tests/Tests/BVBaseStubTestCase.h index 5e0303be..939366fd 100644 --- a/Tests/Tests/BVBaseStubTestCase.h +++ b/Tests/Tests/BVBaseStubTestCase.h @@ -14,16 +14,18 @@ #import #import - @interface BVBaseStubTestCase : XCTestCase -// Will stub out calls to bazaarvoice.com, return 200, and a resultFile with Content-Type = application/json +// Will stub out calls to bazaarvoice.com, return 200, and a resultFile with +// Content-Type = application/json - (void)addStubWith200ResponseForJSONFileNamed:(NSString *)resultFile; -// Use this method to stub calls to bazaarvoice and add any resultFile, headers, and HTTP status you want -- (void)addStubWithResultFile:(NSString *)resultFile statusCode:(int)httpStatus withHeaders:(NSDictionary * )httpHeaders; +// Use this method to stub calls to bazaarvoice and add any resultFile, headers, +// and HTTP status you want +- (void)addStubWithResultFile:(NSString *)resultFile + statusCode:(int)httpStatus + withHeaders:(NSDictionary *)httpHeaders; @end - #endif /* BVBaseStubTestCase_h */ diff --git a/Tests/Tests/BVBaseStubTestCase.m b/Tests/Tests/BVBaseStubTestCase.m index e8528256..b62aca61 100644 --- a/Tests/Tests/BVBaseStubTestCase.m +++ b/Tests/Tests/BVBaseStubTestCase.m @@ -9,48 +9,53 @@ #import "BVBaseStubTestCase.h" - @implementation BVBaseStubTestCase - (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. + [super setUp]; + // Put setup code here. This method is called before the invocation of each + // test method in the class. } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; - [OHHTTPStubs removeAllStubs]; + // Put teardown code here. This method is called after the invocation of each + // test method in the class. + [super tearDown]; + [OHHTTPStubs removeAllStubs]; } -- (void)addStubWith200ResponseForJSONFileNamed:(NSString *)resultFile{ - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { +- (void)addStubWith200ResponseForJSONFileNamed:(NSString *)resultFile { + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { // return normal user profile from /users API - return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(resultFile, self.class) - statusCode:200 - headers:@{@"Content-Type":@"application/json;charset=utf-8"}] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; - + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(resultFile, self.class) + statusCode:200 + headers:@{ + @"Content-Type" : + @"application/json;charset=utf-8" + }] responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; } -- (void)addStubWithResultFile:(NSString *)resultFile statusCode:(int)httpStatus withHeaders:(NSDictionary * )httpHeaders{ - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { +- (void)addStubWithResultFile:(NSString *)resultFile + statusCode:(int)httpStatus + withHeaders:(NSDictionary *)httpHeaders { + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { // return normal user profile from /users API - return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(resultFile, self.class) - statusCode:httpStatus - headers:httpHeaders] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; - - + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(resultFile, self.class) + statusCode:httpStatus + headers:httpHeaders] + responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; } - @end diff --git a/Tests/Tests/BVConversationsTests.m b/Tests/Tests/BVConversationsTests.m index a5740271..78013c86 100644 --- a/Tests/Tests/BVConversationsTests.m +++ b/Tests/Tests/BVConversationsTests.m @@ -5,602 +5,827 @@ // Copyright 2015 Bazaarvoice Inc. All rights reserved. // - +#import #import #import -#import #import "BVBaseStubTestCase.h" -#define CONVERSATIONS_TEST_USING_MOCK_DATA 1 // Setting to 1 uses mock result. Set to 0 to make network request. +#define CONVERSATIONS_TEST_USING_MOCK_DATA \ + 1 // Setting to 1 uses mock result. Set to 0 to make network request. -@interface BVConversationsTests : BVBaseStubTestCase { - BOOL requestComplete; - BOOL receivedProgressCallback; - id sentRequest; - NSDictionary * receivedResponse; +@interface BVConversationsTests : BVBaseStubTestCase { + BOOL requestComplete; + BOOL receivedProgressCallback; + id sentRequest; + NSDictionary *receivedResponse; } @end @implementation BVConversationsTests -- (void)setUp -{ - [super setUp]; - - requestComplete = NO; - - [BVSDKManager sharedManager].staging = YES; - [BVSDKManager sharedManager].clientId = @"apitestcustomer"; - [BVSDKManager sharedManager].apiKeyConversations = TEST_KEY_CONVERSATIONS; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - -} - -- (void)tearDown -{ - // Tear-down code here. - - [super tearDown]; -} - -- (void)checkParams:(NSMutableDictionary *)params withoutAPIBase:(BOOL)withoutAPIBase { - NSString *url = [sentRequest performSelector:@selector(requestURL)]; - if (!withoutAPIBase) { - NSDictionary *baseDictionary = [NSDictionary - dictionaryWithObjectsAndKeys:BV_API_VERSION, - @"ApiVersion", - [BVSDKManager sharedManager].apiKeyConversations, - @"PassKey", - nil]; - [params addEntriesFromDictionary:baseDictionary]; - } - NSMutableDictionary *foundParams = [[NSMutableDictionary alloc] init]; - NSArray *comp1 = [url componentsSeparatedByString:@"?"]; - NSString *query = [comp1 lastObject]; - NSArray *queryElements = [query componentsSeparatedByString:@"&"]; - for (NSString *element in queryElements) { - NSArray *keyVal = [element componentsSeparatedByString:@"="]; - NSAssert(keyVal.count == 2, @"Malformed URL"); - [foundParams setObject:[keyVal objectAtIndex:1] forKey:[keyVal objectAtIndex:0]]; - } - - NSAssert(params.count == foundParams.count, @"Wrong number of URL params... %lu expected vs %lu found\n request:%@", (unsigned long) params.count, (unsigned long) foundParams.count, url); - - NSArray *keyArray = [params allKeys]; - NSUInteger count = [keyArray count]; - for (int i=0; i < count; i++) { - NSString * key = [keyArray objectAtIndex:i]; - NSAssert([foundParams objectForKey:key], @"Request missing parameter %@", key); - NSString *requestVal = (NSString *)[foundParams objectForKey:key]; - NSString *expectedVal = (NSString *)[params objectForKey:key]; - NSAssert([requestVal isEqualToString:expectedVal], @"Request value of %@ does not match expected value of %@", requestVal, expectedVal); - } +- (void)setUp { + [super setUp]; + + requestComplete = NO; + + [BVSDKManager sharedManager].staging = YES; + [BVSDKManager sharedManager].clientId = @"apitestcustomer"; + [BVSDKManager sharedManager].apiKeyConversations = TEST_KEY_CONVERSATIONS; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; +} + +- (void)tearDown { + // Tear-down code here. + + [super tearDown]; +} + +- (void)checkParams:(NSMutableDictionary *)params + withoutAPIBase:(BOOL)withoutAPIBase { + NSString *url = [sentRequest performSelector:@selector(requestURL)]; + if (!withoutAPIBase) { + NSDictionary *baseDictionary = [NSDictionary + dictionaryWithObjectsAndKeys:BV_API_VERSION, @"ApiVersion", + [BVSDKManager sharedManager] + .apiKeyConversations, + @"PassKey", nil]; + [params addEntriesFromDictionary:baseDictionary]; + } + NSMutableDictionary *foundParams = [[NSMutableDictionary alloc] init]; + NSArray *comp1 = [url componentsSeparatedByString:@"?"]; + NSString *query = [comp1 lastObject]; + NSArray *queryElements = [query componentsSeparatedByString:@"&"]; + for (NSString *element in queryElements) { + NSArray *keyVal = [element componentsSeparatedByString:@"="]; + NSAssert(keyVal.count == 2, @"Malformed URL"); + [foundParams setObject:[keyVal objectAtIndex:1] + forKey:[keyVal objectAtIndex:0]]; + } + + NSAssert( + params.count == foundParams.count, + @"Wrong number of URL params... %lu expected vs %lu found\n request:%@", + (unsigned long)params.count, (unsigned long)foundParams.count, url); + + NSArray *keyArray = [params allKeys]; + NSUInteger count = [keyArray count]; + for (int i = 0; i < count; i++) { + NSString *key = [keyArray objectAtIndex:i]; + NSAssert([foundParams objectForKey:key], @"Request missing parameter %@", + key); + NSString *requestVal = (NSString *)[foundParams objectForKey:key]; + NSString *expectedVal = (NSString *)[params objectForKey:key]; + NSAssert([requestVal isEqualToString:expectedVal], + @"Request value of %@ does not match expected value of %@", + requestVal, expectedVal); + } } - (void)checkParams:(NSMutableDictionary *)params { - [self checkParams:params withoutAPIBase:NO]; -} - -- (void)didReceiveResponse:(NSDictionary *)response forRequest:(id)request{ - -// NSLog(@"%@", response); - requestComplete = YES; - receivedResponse = response; - sentRequest = request; - - BOOL hasErrors = [[response objectForKey:@"HasErrors"] boolValue] || ([response objectForKey:@"HasErrors"] == nil); - BOOL missingErrorArray = [response objectForKey:@"Errors"] == nil; // We always expect an errors array, but will be empty on success - if (hasErrors || missingErrorArray) { - NSLog(@"\n\n==========================\n\n"); - XCTFail(@"Error in Class: %@ \n Failure: %@", [request class], [response objectForKey:@"Errors"]); - NSLog(@"\n\n==========================\n\n"); - NSLog(@"%@", response); - } - else if(!receivedProgressCallback && [request isKindOfClass:[BVPost class]]) - { - // We only need to check if we received a progress callback for POST / submission requests - NSLog(@"\n\n==========================\n\n"); - XCTFail(@"Failed to receive a progress callback for request: %@", [request class]); - NSLog(@"\n\n==========================\n\n"); - } - else - { - XCTAssertNotNil(response, @"Invalid response for Class: %@", [request class]); - } - NSLog(@"\n\n"); -} - --(void)didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend forRequest:(id)request { - receivedProgressCallback = YES; + [self checkParams:params withoutAPIBase:NO]; } +- (void)didReceiveResponse:(NSDictionary *)response forRequest:(id)request { + + // NSLog(@"%@", response); + requestComplete = YES; + receivedResponse = response; + sentRequest = request; + + BOOL hasErrors = [[response objectForKey:@"HasErrors"] boolValue] || + ([response objectForKey:@"HasErrors"] == nil); + BOOL missingErrorArray = + [response objectForKey:@"Errors"] == + nil; // We always expect an errors array, but will be empty on success + if (hasErrors || missingErrorArray) { + NSLog(@"\n\n==========================\n\n"); + XCTFail(@"Error in Class: %@ \n Failure: %@", [request class], + [response objectForKey:@"Errors"]); + NSLog(@"\n\n==========================\n\n"); + NSLog(@"%@", response); + } else if (!receivedProgressCallback && + [request isKindOfClass:[BVPost class]]) { + // We only need to check if we received a progress callback for POST / + // submission requests + NSLog(@"\n\n==========================\n\n"); + XCTFail(@"Failed to receive a progress callback for request: %@", + [request class]); + NSLog(@"\n\n==========================\n\n"); + } else { + XCTAssertNotNil(response, @"Invalid response for Class: %@", + [request class]); + } + NSLog(@"\n\n"); +} + +- (void)didSendBodyData:(int64_t)bytesSent + totalBytesSent:(int64_t)totalBytesSent + totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend + forRequest:(id)request { + receivedProgressCallback = YES; +} - (void)didFailToReceiveResponse:(NSError *)err forRequest:(id)request { - requestComplete = YES; + requestComplete = YES; } #pragma mark BVGet tests - (void)testShowReviewSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowReviewSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowReviewSparse.json"]; #endif - - BVGet *request = [[BVGet alloc] init]; - [request sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; -} + BVGet *request = [[BVGet alloc] init]; + [request sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; +} - (void)testShowReview { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; #endif - - BVGet *showDisplayRequest = [[ BVGet alloc ] initWithType:BVGetTypeReviews]; - [ showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"6601211"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"009"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 50; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - showDisplayRequest.search = @"Great sound"; - [showDisplayRequest sendRequestWithDelegate:self]; - - // Begin a run loop terminated when the requestComplete it set to true - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:009", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"Id:eq:6601211", @"Filter", @"50", @"Limit", @"Great%20sound", @"Search", nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeReviews]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"6601211"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"009"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 50; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + showDisplayRequest.search = @"Great sound"; + [showDisplayRequest sendRequestWithDelegate:self]; + + // Begin a run loop terminated when the requestComplete it set to true + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:009", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"Id:eq:6601211", @"Filter", + @"50", @"Limit", + @"Great%20sound", @"Search", + nil]]; } - (void)testShowReviewIncludesSearch { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowReviewIncludesSearch.json"]; + [self addStubWith200ResponseForJSONFileNamed: + @"testShowReviewIncludesSearch.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeReviews]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest setSearchOnIncludedType:BVIncludeTypeProducts search:@"Increase your potential to shine"]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Id:asc", @"Sort_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"Increase%20your%20potential%20to%20shine", @"Search_Products", nil]]; -} + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeReviews]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest + setSearchOnIncludedType:BVIncludeTypeProducts + search:@"Increase your potential to shine"]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys: + @"Id:asc", @"Sort_Products", @"Id:asc", @"Sort", + @"Products", @"Include", + @"Increase%20your%20potential%20to%20shine", + @"Search_Products", nil]]; +} - (void)testShowQuestionSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestionSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestionSparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowQuestion { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestion.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestion.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"87757"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"test1"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 50; - showDisplayRequest.excludeFamily = true; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:test1", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"Id:eq:87757", @"Filter", @"50", @"Limit", @"true", @"ExcludeFamily", nil]]; - - + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"87757"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"test1"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 50; + showDisplayRequest.excludeFamily = true; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:test1", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"Id:eq:87757", @"Filter", + @"50", @"Limit", @"true", + @"ExcludeFamily", nil]]; } - (void)testShowQuestionsSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestionsSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestionsSparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self checkParams:[NSMutableDictionary dictionary]]; } +- (void)testShowQuestions { -- (void)testShowQuestions{ - #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestions.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestions.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"6055"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"test0"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 50; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeAnswers]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Answers", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:test0", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"Id:eq:6055", @"Filter", @"50", @"Limit", nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeQuestions]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"6055"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"test0"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 50; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeAnswers]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Answers", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:test0", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"Id:eq:6055", @"Filter", + @"50", @"Limit", nil]]; } - (void)testShowStorySparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowStorySparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowStorySparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStories]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"14181"]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys:@"Id:eq:14181", @"Filter",nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStories]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"14181"]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Id:eq:14181", @"Filter", + nil]]; } - (void)testShowStory { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowStory.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowStory.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStories]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"14181"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeComments forAttribute:@"Id" equality:BVEqualityEqualTo value:@"1010"]; - [showDisplayRequest addInclude:BVIncludeTypeComments]; - showDisplayRequest.limit = 50; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeComments value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeComments attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeStories]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Stories", @"Stats", @"Id:asc", @"Sort_Comments", @"Id:eq:1010", @"Filter_Comments", @"10", @"Limit_Comments", @"Id:asc", @"Sort", @"Comments", @"Include", @"0", @"Offset", @"Id:eq:14181", @"Filter", @"50", @"Limit", nil]]; -} + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStories]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"14181"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeComments + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"1010"]; + [showDisplayRequest addInclude:BVIncludeTypeComments]; + showDisplayRequest.limit = 50; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeComments value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeComments + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeStories]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Stories", @"Stats", + @"Id:asc", @"Sort_Comments", + @"Id:eq:1010", + @"Filter_Comments", @"10", + @"Limit_Comments", @"Id:asc", + @"Sort", @"Comments", + @"Include", @"0", @"Offset", + @"Id:eq:14181", @"Filter", + @"50", @"Limit", nil]]; +} - (void)testShowCommentsSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowCommentsSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowCommentsSparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeReviewCommments]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = + [[BVGet alloc] initWithType:BVGetTypeReviewCommments]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowComments { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowComments.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowComments.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeReviewCommments]; - [showDisplayRequest setFilterForAttribute:@"ReviewId" equality:BVEqualityEqualTo value:@"6597809"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"2323001"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 50; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:2323001", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"ReviewId:eq:6597809", @"Filter", @"50", @"Limit", nil]]; + + BVGet *showDisplayRequest = + [[BVGet alloc] initWithType:BVGetTypeReviewCommments]; + [showDisplayRequest setFilterForAttribute:@"ReviewId" + equality:BVEqualityEqualTo + value:@"6597809"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"2323001"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 50; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:2323001", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"ReviewId:eq:6597809", + @"Filter", @"50", @"Limit", + nil]]; } - (void)testShowCommentStorySparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowCommentStorySparse.json"]; + [self addStubWith200ResponseForJSONFileNamed: + @"testShowCommentStorySparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStoryCommments]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; - + + BVGet *showDisplayRequest = + [[BVGet alloc] initWithType:BVGetTypeStoryCommments]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowCommentStory { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowCommentStory.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowCommentStory.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStoryCommments]; - [showDisplayRequest setFilterForAttribute:@"StoryId" equality:BVEqualityEqualTo value:@"967"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"test1"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 10; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:test1", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"StoryId:eq:967", @"Filter", @"10", @"Limit", nil]]; + + BVGet *showDisplayRequest = + [[BVGet alloc] initWithType:BVGetTypeStoryCommments]; + [showDisplayRequest setFilterForAttribute:@"StoryId" + equality:BVEqualityEqualTo + value:@"967"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"test1"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 10; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:test1", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"StoryId:eq:967", @"Filter", + @"10", @"Limit", nil]]; } - (void)testShowProfileSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowProfileSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowProfileSparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeAuthors]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeAuthors]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowProfile { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowProfile.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowProfile.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeAuthors]; - [showDisplayRequest setFilterForAttribute:@"TotalCommentCount" equality:BVEqualityGreaterThanOrEqual value:@"0"]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"smartPP"]; - showDisplayRequest.limit = 10; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort", @"0", @"Offset", @"Id:eq:smartPP", @"Filter", @"10", @"Limit", nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeAuthors]; + [showDisplayRequest setFilterForAttribute:@"TotalCommentCount" + equality:BVEqualityGreaterThanOrEqual + value:@"0"]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"smartPP"]; + showDisplayRequest.limit = 10; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort", @"0", + @"Offset", @"Id:eq:smartPP", + @"Filter", @"10", @"Limit", + nil]]; } - (void)testShowProductsSparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowProductsSparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowProductsSparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeProducts]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeProducts]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowProducts { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowProducts.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowProducts.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeProducts]; - [showDisplayRequest setFilterForAttribute:@"CategoryId" equality:BVEqualityEqualTo value:@"testcategory1011"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeReviews forAttribute:@"Id" equality:BVEqualityEqualTo value:@"83501"]; - [showDisplayRequest addInclude:BVIncludeTypeReviews]; - showDisplayRequest.limit = 10; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeReviews value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeReviews attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Reviews", @"Id:eq:83501", @"Filter_Reviews", @"10", @"Limit_Reviews", @"Id:asc", @"Sort", @"Reviews", @"Include", @"0", @"Offset", @"CategoryId:eq:testcategory1011", @"Filter", @"10", @"Limit", nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeProducts]; + [showDisplayRequest setFilterForAttribute:@"CategoryId" + equality:BVEqualityEqualTo + value:@"testcategory1011"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeReviews + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"83501"]; + [showDisplayRequest addInclude:BVIncludeTypeReviews]; + showDisplayRequest.limit = 10; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeReviews value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeReviews + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys: + @"Reviews", @"Stats", @"Id:asc", @"Sort_Reviews", + @"Id:eq:83501", @"Filter_Reviews", @"10", + @"Limit_Reviews", @"Id:asc", @"Sort", @"Reviews", + @"Include", @"0", @"Offset", + @"CategoryId:eq:testcategory1011", @"Filter", @"10", + @"Limit", nil]]; } - (void)testShowCateogrySparse { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowCategorySparse.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowCategorySparse.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeCategories]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - [self checkParams:[NSMutableDictionary dictionary]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeCategories]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + [self checkParams:[NSMutableDictionary dictionary]]; } - (void)testShowCateogry { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowCategory.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowCategory.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeCategories]; - [showDisplayRequest setFilterForAttribute:@"Id" equality:BVEqualityEqualTo value:@"testCategory1011"]; - [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts forAttribute:@"Id" equality:BVEqualityEqualTo value:@"test2"]; - [showDisplayRequest addInclude:BVIncludeTypeProducts]; - showDisplayRequest.limit = 10; - [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; - showDisplayRequest.offset = 0; - [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; - [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts attribute:@"Id" ascending:YES]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"Reviews", @"Stats", @"Id:asc", @"Sort_Products", @"Id:eq:test2", @"Filter_Products", @"10", @"Limit_Products", @"Id:asc", @"Sort", @"Products", @"Include", @"0", @"Offset", @"Id:eq:testCategory1011", @"Filter", @"10", @"Limit", nil]]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeCategories]; + [showDisplayRequest setFilterForAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"testCategory1011"]; + [showDisplayRequest setFilterOnIncludedType:BVIncludeTypeProducts + forAttribute:@"Id" + equality:BVEqualityEqualTo + value:@"test2"]; + [showDisplayRequest addInclude:BVIncludeTypeProducts]; + showDisplayRequest.limit = 10; + [showDisplayRequest setLimitOnIncludedType:BVIncludeTypeProducts value:10]; + showDisplayRequest.offset = 0; + [showDisplayRequest addSortForAttribute:@"Id" ascending:YES]; + [showDisplayRequest addSortOnIncludedType:BVIncludeTypeProducts + attribute:@"Id" + ascending:YES]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self + checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys:@"Reviews", @"Stats", + @"Id:asc", @"Sort_Products", + @"Id:eq:test2", + @"Filter_Products", @"10", + @"Limit_Products", @"Id:asc", + @"Sort", @"Products", + @"Include", @"0", @"Offset", + @"Id:eq:testCategory1011", + @"Filter", @"10", @"Limit", + nil]]; } - (void)testShowStatistics { - + #if CONVERSATIONS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowStatistics.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowStatistics.json"]; #endif - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStatistics]; - [showDisplayRequest setFilterForAttribute:@"ProductId" equality:BVEqualityEqualTo values:[NSArray arrayWithObjects:@"test1", @"test2", @"test3", nil]]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeNativeReviews]; - - [showDisplayRequest sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"ProductId:eq:test1,test2,test3", @"Filter", @"Reviews,NativeReviews", @"Stats", nil]]; - + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStatistics]; + [showDisplayRequest + setFilterForAttribute:@"ProductId" + equality:BVEqualityEqualTo + values:[NSArray arrayWithObjects:@"test1", @"test2", + @"test3", nil]]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeNativeReviews]; + + [showDisplayRequest sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys: + @"ProductId:eq:test1,test2,test3", @"Filter", + @"Reviews,NativeReviews", @"Stats", nil]]; } // Bug: MOB-416 - Space in value of Content-Type response header - (void)testShowStatisticsSpaceInContentType { - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { // return normal user profile from /users API - return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(@"testShowStatistics.json", self.class) - statusCode:200 - headers:@{@"Content-Type":@"application/json; charset=utf-8"}] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; - - BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStatistics]; - [showDisplayRequest setFilterForAttribute:@"ProductId" equality:BVEqualityEqualTo values:[NSArray arrayWithObjects:@"test1", @"test2", @"test3", nil]]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; - [showDisplayRequest addStatsOn:BVIncludeStatsTypeNativeReviews]; - - [showDisplayRequest sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - [self checkParams:[NSMutableDictionary dictionaryWithObjectsAndKeys: - @"ProductId:eq:test1,test2,test3", @"Filter", @"Reviews,NativeReviews", @"Stats", nil]]; - + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(@"testShowStatistics.json", + self.class) + statusCode:200 + headers:@{ + @"Content-Type" : + @"application/json; charset=utf-8" + }] responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; + + BVGet *showDisplayRequest = [[BVGet alloc] initWithType:BVGetTypeStatistics]; + [showDisplayRequest + setFilterForAttribute:@"ProductId" + equality:BVEqualityEqualTo + values:[NSArray arrayWithObjects:@"test1", @"test2", + @"test3", nil]]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeReviews]; + [showDisplayRequest addStatsOn:BVIncludeStatsTypeNativeReviews]; + + [showDisplayRequest sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + [self checkParams:[NSMutableDictionary + dictionaryWithObjectsAndKeys: + @"ProductId:eq:test1,test2,test3", @"Filter", + @"Reviews,NativeReviews", @"Stats", nil]]; } - - #pragma mark BVPost tests -// A Note on test stubs for POST from: https://github.com/AliSoftware/OHHTTPStubs#Known_limitations +// A Note on test stubs for POST from: +// https://github.com/AliSoftware/OHHTTPStubs#Known_limitations /* -OHHTTPStubs don't simulate data upload. The NSURLProtocolClient @protocol does not provide a way to signal the delegate that data has been sent (only that some has been loaded), so any data in the HTTPBody or HTTPBodyStream of an NSURLRequest, or data provided to -[NSURLSession uploadTaskWithRequest:fromData:]; will be ignored, and more importantly, the -URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: delegate method will never be called when you stub the request using OHHTTPStubs. +OHHTTPStubs don't simulate data upload. The NSURLProtocolClient @protocol does +not provide a way to signal the delegate that data has been sent (only that some +has been loaded), so any data in the HTTPBody or HTTPBodyStream of an +NSURLRequest, or data provided to -[NSURLSession +uploadTaskWithRequest:fromData:]; will be ignored, and more importantly, the +-URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: +delegate method will never be called when you stub the request using +OHHTTPStubs. */ -- (void)testSubmissionReview -{ - NSLog(@"BVSDK Info: %@", [BVSDKManager sharedManager].description); - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; - request.productId = @"100003401"; - request.userId = [NSString stringWithFormat:@"123abcd%i", arc4random()]; - request.rating = 5; - request.title = @"Test title"; - request.reviewText = @"Some kind of review text."; - request.userNickname = @"testnickname"; - [request addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" withCaption:nil]; - [request addVideoUrl:@"https://www.youtube.com" withCaption:nil]; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -} - -- (void)testSubmissionReviewBackgroundThread -{ - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +- (void)testSubmissionReview { + NSLog(@"BVSDK Info: %@", [BVSDKManager sharedManager].description); + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; + request.productId = @"100003401"; + request.userId = [NSString stringWithFormat:@"123abcd%i", arc4random()]; + request.rating = 5; + request.title = @"Test title"; + request.reviewText = @"Some kind of review text."; + request.userNickname = @"testnickname"; + [request addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/" + @"5555/ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" + withCaption:nil]; + [request addVideoUrl:@"https://www.youtube.com" withCaption:nil]; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; +} + +- (void)testSubmissionReviewBackgroundThread { + + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; request.productId = @"100003401"; request.userId = [NSString stringWithFormat:@"123abcd%i", arc4random()]; @@ -608,112 +833,139 @@ - (void)testSubmissionReviewBackgroundThread request.title = @"Test title"; request.reviewText = @"Some kind of review text."; request.userNickname = @"testnickname"; - [request addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" withCaption:nil]; + [request addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/" + @"bvstaging/5555/" + @"ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" + withCaption:nil]; [request addVideoUrl:@"https://www.youtube.com" withCaption:nil]; - + [request sendRequestWithDelegate:self]; - }); - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + }); + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionQuestions { - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeQuestion]; - request.categoryId = @"1020"; - request.locale = @"en_US"; - request.userId = @"123abcd"; - request.questionSummary = @"Some kind of question"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeQuestion]; + request.categoryId = @"1020"; + request.locale = @"en_US"; + request.userId = @"123abcd"; + request.questionSummary = @"Some kind of question"; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionAnswers { - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeAnswer]; - request.questionId = @"6104"; - request.userId = @"123abcd"; - request.questionSummary = @"Some kind of answer"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeAnswer]; + request.questionId = @"6104"; + request.userId = @"123abcd"; + request.questionSummary = @"Some kind of answer"; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionStories { - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeStory]; - request.title = @"This is the title"; - request.storyText = @"This is my story"; - request.categoryId = @"1020235"; - request.userId = @"123abc"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeStory]; + request.title = @"This is the title"; + request.storyText = @"This is my story"; + request.categoryId = @"1020235"; + request.userId = @"123abc"; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionReviewComments { - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReviewComment]; - request.commentText = @"This is my comment text"; - request.reviewId = @"83964"; - request.userId = @"123abc"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -} + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReviewComment]; + request.commentText = @"This is my comment text"; + request.reviewId = @"83964"; + request.userId = @"123abc"; + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; +} - (void)testSubmissionStoryCommentsHostedAuth { - - // For Hosted Auth, we use a separately configured key. Keys can be configured for either anonymous posting with a user ID, or - // use the hostedAuth* parameters to specify the callback URL and authorization email. - [BVSDKManager sharedManager].apiKeyConversations = @"95fczpcfxcetspeg388pbrvy"; - [BVSDKManager sharedManager].staging = YES; - [BVSDKManager sharedManager].clientId = @"bv-mobilesdk"; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; - request.rating = 5; - request.title = @"Test title"; - request.productId = @"9876543"; - request.reviewText = @"This is a test for hosted authentication where we will send a post review email for the customer to verify their input."; - - request.hostedAuthCallbackUrl = @"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging"; - request.hostedAuthEmail = @"fakeemail@fakeemailareus.com"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + // For Hosted Auth, we use a separately configured key. Keys can be configured + // for either anonymous posting with a user ID, or use the hostedAuth* + // parameters to specify the callback URL and authorization email. + [BVSDKManager sharedManager].apiKeyConversations = + @"95fczpcfxcetspeg388pbrvy"; + [BVSDKManager sharedManager].staging = YES; + [BVSDKManager sharedManager].clientId = @"bv-mobilesdk"; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; + request.rating = 5; + request.title = @"Test title"; + request.productId = @"9876543"; + request.reviewText = @"This is a test for hosted authentication where we " + @"will send a post review email for the customer to " + @"verify their input."; + + request.hostedAuthCallbackUrl = + @"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging"; + request.hostedAuthEmail = @"fakeemail@fakeemailareus.com"; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionStoryCommentsAuthenticatedUser { - - // For Hosted Auth, we use a separately configured key. Once a user is confirmed and authenticated we can simply use the authentication string - // to allow the user to post reviews without confirmation. - [BVSDKManager sharedManager].apiKeyConversations = @"95fczpcfxcetspeg388pbrvy"; - [BVSDKManager sharedManager].staging = YES; - [BVSDKManager sharedManager].clientId = @"bv-mobilesdk"; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - - BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; - request.commentText = @"This is a test comment for hosted authentication. In this case we use the authention token."; - request.productId = @"967"; - request.authenticatedUser = @"6ed12da604cc75b8613f7e209d07987b696e7465726e616c5f7375626d7373696f6e3d74727565267573657269643d616a6d66716176737836786f7068626e7571656474726a347a26757365726e616d653d617069686f73746175746873756274657374657226686f737465643d564552494649454426646174653d3230313430353036266d61786167653d333635"; - - [request sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -} + // For Hosted Auth, we use a separately configured key. Once a user is + // confirmed and authenticated we can simply use the authentication string to + // allow the user to post reviews without confirmation. + [BVSDKManager sharedManager].apiKeyConversations = + @"95fczpcfxcetspeg388pbrvy"; + [BVSDKManager sharedManager].staging = YES; + [BVSDKManager sharedManager].clientId = @"bv-mobilesdk"; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; + + BVPost *request = [[BVPost alloc] initWithType:BVPostTypeReview]; + request.commentText = @"This is a test comment for hosted authentication. In " + @"this case we use the authention token."; + request.productId = @"967"; + request.authenticatedUser = + @"6ed12da604cc75b8613f7e209d07987b696e7465726e616c5f7375626d7373696f6e3d7" + @"4727565267573657269643d616a6d66716176737836786f7068626e7571656474726a34" + @"7a26757365726e616d653d617069686f73746175746873756274657374657226686f737" + @"465643d564552494649454426646174653d3230313430353036266d61786167653d3336" + @"35"; + + [request sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; +} //- (void)testSubmissionVideos { -// +// // /* // requestComplete = NO; // receivedProgressCallback = NO; @@ -722,145 +974,171 @@ - (void)testSubmissionStoryCommentsAuthenticatedUser { // mySubmission.parameters.video = @"https://www.youtube.com/"; // mySubmission.parameters.userId = @"123abc"; // mySubmission.delegate = self; -// +// // [mySubmission startAsynchRequest]; // NSRunLoop *theRL = [NSRunLoop currentRunLoop]; // // Begin a run loop terminated when the requestComplete it set to true -// while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -// +// while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode +// beforeDate:[NSDate distantFuture]]); +// // */ //} - - - - (void)testSubmissionPhotos { - - BVMediaPost *mySubmission = [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; - mySubmission.contentType = BVMediaPostContentTypeReview; - mySubmission.userId = @"123"; - - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *imagePath = [bundle pathForResource:@"bv533x533" ofType:@"png"]; - UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; - mySubmission.photo = image; - - [mySubmission sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + BVMediaPost *mySubmission = + [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; + mySubmission.contentType = BVMediaPostContentTypeReview; + mySubmission.userId = @"123"; + + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *imagePath = [bundle pathForResource:@"bv533x533" ofType:@"png"]; + UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; + mySubmission.photo = image; + + [mySubmission sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionPhotosRotated { - - BVMediaPost *mySubmission = [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; - mySubmission.contentType = BVMediaPostContentTypeReview; - mySubmission.userId = @"123"; - - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *imagePath = [bundle pathForResource:@"270cw" ofType:@"JPG"]; - - UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; - - mySubmission.photo = image; - - [mySubmission sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - // Check image manually - NSLog(@"%@", receivedResponse); - + + BVMediaPost *mySubmission = + [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; + mySubmission.contentType = BVMediaPostContentTypeReview; + mySubmission.userId = @"123"; + + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *imagePath = [bundle pathForResource:@"270cw" ofType:@"JPG"]; + + UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; + + mySubmission.photo = image; + + [mySubmission sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + // Check image manually + NSLog(@"%@", receivedResponse); } - (void)testSubmissionPhotoURL { - - BVMediaPost *mySubmission = [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; - mySubmission.contentType = BVMediaPostContentTypeReview; - mySubmission.userId = @"123"; - mySubmission.photoUrl = @"https://dogr.herokuapp.com/doge.png"; - - [mySubmission sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -} -- (void)testSubmissionVideo { - - BVMediaPost *mySubmission = [[BVMediaPost alloc] initWithType:BVMediaPostTypeVideo]; - mySubmission.contentType = BVMediaPostContentTypeReview; - mySubmission.userId = @"123"; - - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *videoPath = [bundle pathForResource:@"sample_mpeg4" ofType:@"mp4"]; - NSData *video = [NSData dataWithContentsOfFile:videoPath]; - [mySubmission setVideo:video withFormat:BVVideoFormatTypeMP4]; - [mySubmission sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - - // Check video manually - NSLog(@"%@", receivedResponse); + BVMediaPost *mySubmission = + [[BVMediaPost alloc] initWithType:BVMediaPostTypePhoto]; + mySubmission.contentType = BVMediaPostContentTypeReview; + mySubmission.userId = @"123"; + mySubmission.photoUrl = @"https://dogr.herokuapp.com/doge.png"; + + [mySubmission sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } +- (void)testSubmissionVideo { + BVMediaPost *mySubmission = + [[BVMediaPost alloc] initWithType:BVMediaPostTypeVideo]; + mySubmission.contentType = BVMediaPostContentTypeReview; + mySubmission.userId = @"123"; + + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + NSString *videoPath = [bundle pathForResource:@"sample_mpeg4" ofType:@"mp4"]; + NSData *video = [NSData dataWithContentsOfFile:videoPath]; + [mySubmission setVideo:video withFormat:BVVideoFormatTypeMP4]; + [mySubmission sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; + + // Check video manually + NSLog(@"%@", receivedResponse); +} - (void)testSubmissionFeedback { - - BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeFeedback]; - mySubmission.contentType = BVFeedbackContentTypeReview; - mySubmission.contentId = @"83964"; - mySubmission.userId = @"123abc"; - mySubmission.feedbackType = BVFeedbackTypeHelpfulness; - mySubmission.vote = BVFeedbackVoteTypeNegative; - [mySubmission sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); - + + BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeFeedback]; + mySubmission.contentType = BVFeedbackContentTypeReview; + mySubmission.contentId = @"83964"; + mySubmission.userId = @"123abc"; + mySubmission.feedbackType = BVFeedbackTypeHelpfulness; + mySubmission.vote = BVFeedbackVoteTypeNegative; + [mySubmission sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } - (void)testSubmissionFeedback2 { - - BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeFeedback]; - mySubmission.contentType = BVFeedbackContentTypeReview; - mySubmission.contentId = @"83964"; - mySubmission.userId = @"123abc"; - mySubmission.feedbackType = BVFeedbackTypeInappropriate; - mySubmission.reasonText = @"This post was not nice."; [mySubmission sendRequestWithDelegate:self]; - [mySubmission sendRequestWithDelegate:self]; - - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); -} + BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeFeedback]; + mySubmission.contentType = BVFeedbackContentTypeReview; + mySubmission.contentId = @"83964"; + mySubmission.userId = @"123abc"; + mySubmission.feedbackType = BVFeedbackTypeInappropriate; + mySubmission.reasonText = @"This post was not nice."; + [mySubmission sendRequestWithDelegate:self]; + [mySubmission sendRequestWithDelegate:self]; + + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; +} - (void)testParamsAttached { - - BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeReview]; - mySubmission.productId = @"10000sadfgasdg3401"; - mySubmission.userId = [NSString stringWithFormat:@"WHEEEEMYNAMEISSAME%i", arc4random()]; - mySubmission.rating = 5; - mySubmission.title = @"Test title"; - mySubmission.reviewText = @"Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text. Some kind of review text."; - mySubmission.userNickname = @"testnickname4"; - [mySubmission addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" withCaption:nil]; - [mySubmission addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/ps_amazon_s3_a11b8t4wlgb914fjaiudaadvo/photo.jpg" withCaption:@"This photo is cool!"]; - [mySubmission addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/ps_amazon_s3_5ugnhmmq24p1q35tlygrqalz9/photo.jpg" withCaption:nil]; - [mySubmission addTagForDimensionExternalId:@"Pro" value:@"fit"]; - [mySubmission addTagForDimensionExternalId:@"Pro" value:@"comfortable fit"]; - [mySubmission addTagIdForDimensionExternalId:@"Pro/ProService" value:true]; - [mySubmission addTagIdForDimensionExternalId:@"Con/ConFitness" value:true]; - - [mySubmission sendRequestWithDelegate:self]; - NSRunLoop *theRL = [NSRunLoop currentRunLoop]; - // Begin a run loop terminated when the requestComplete it set to true - while (!requestComplete && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); + + BVPost *mySubmission = [[BVPost alloc] initWithType:BVPostTypeReview]; + mySubmission.productId = @"10000sadfgasdg3401"; + mySubmission.userId = + [NSString stringWithFormat:@"WHEEEEMYNAMEISSAME%i", arc4random()]; + mySubmission.rating = 5; + mySubmission.title = @"Test title"; + mySubmission.reviewText = @"Some kind of review text. Some kind of review " + @"text. Some kind of review text. Some kind of " + @"review text. Some kind of review text. Some kind " + @"of review text. Some kind of review text. Some " + @"kind of review text. Some kind of review text."; + mySubmission.userNickname = @"testnickname4"; + [mySubmission + addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/" + @"ps_amazon_s3_3rgg6s4xvev0zhzbnabyneo21/photo.jpg" + withCaption:nil]; + [mySubmission + addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/" + @"ps_amazon_s3_a11b8t4wlgb914fjaiudaadvo/photo.jpg" + withCaption:@"This photo is cool!"]; + [mySubmission + addPhotoUrl:@"https://apitestcustomer.ugc.bazaarvoice.com/bvstaging/5555/" + @"ps_amazon_s3_5ugnhmmq24p1q35tlygrqalz9/photo.jpg" + withCaption:nil]; + [mySubmission addTagForDimensionExternalId:@"Pro" value:@"fit"]; + [mySubmission addTagForDimensionExternalId:@"Pro" value:@"comfortable fit"]; + [mySubmission addTagIdForDimensionExternalId:@"Pro/ProService" value:true]; + [mySubmission addTagIdForDimensionExternalId:@"Con/ConFitness" value:true]; + + [mySubmission sendRequestWithDelegate:self]; + NSRunLoop *theRL = [NSRunLoop currentRunLoop]; + // Begin a run loop terminated when the requestComplete it set to true + while (!requestComplete && + [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) + ; } @end diff --git a/Tests/Tests/BVCurationsTests.m b/Tests/Tests/BVCurationsTests.m index fd337c30..0f5b12c9 100644 --- a/Tests/Tests/BVCurationsTests.m +++ b/Tests/Tests/BVCurationsTests.m @@ -5,9 +5,9 @@ // Copyright © 2016 Bazaarvoice. All rights reserved. // -#import -#import #import "BVBaseStubTestCase.h" +#import +#import @interface BVCurationsTests : BVBaseStubTestCase @@ -16,616 +16,724 @@ @interface BVCurationsTests : BVBaseStubTestCase @implementation BVCurationsTests - (void)setUp { - [super setUp]; - - // Put setup code here. This method is called before the invocation of each test method in the class. - NSDictionary *configDict = @{@"apiKeyCurations": @"fakeymcfakersonfakekey", - @"clientId": @"test-classic"}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - + [super setUp]; + + // Put setup code here. This method is called before the invocation of each + // test method in the class. + NSDictionary *configDict = @{ + @"apiKeyCurations" : @"fakeymcfakersonfakekey", + @"clientId" : @"test-classic" + }; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; - + // Put teardown code here. This method is called after the invocation of each + // test method in the class. + [super tearDown]; } // Load image by name for the test bundle - (UIImage *)loadTestImageNamed:(NSString *)imageName { - return [UIImage imageNamed:imageName - inBundle:[NSBundle bundleForClass:[BVCurationsTests class]]compatibleWithTraitCollection:nil]; + return [UIImage imageNamed:imageName + inBundle:[NSBundle + bundleForClass:[BVCurationsTests class]] + compatibleWithTraitCollection:nil]; } // Test normal parse result from a feed - (void)testFetchCurations { - - [self addStubWith200ResponseForJSONFileNamed:@"curationsFeedTest1.json"]; - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchCurations"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv", @"test2", @"test3" ]]; - - // media={'video':{'width':480,'height':360}} - feedRequest.media = @{ - @"media" : @{ - @"width" : @"480", - @"height" : @"360" - } - }; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { + + [self addStubWith200ResponseForJSONFileNamed:@"curationsFeedTest1.json"]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testFetchCurations"]; + + BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] + initWithGroups:@[ @"livebv", @"test2", @"test3" ]]; + + // media={'video':{'width':480,'height':360}} + feedRequest.media = @{ @"media" : @{@"width" : @"480", @"height" : @"360"} }; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { // success! - - XCTAssertNotNil(feedItems, @"ERROR: feeItems should not be nil in curations api response."); - + + XCTAssertNotNil( + feedItems, + @"ERROR: feeItems should not be nil in curations api response."); + bool hasPhotos = NO; bool hasVideos = NO; - for (BVCurationsFeedItem *feedItem in feedItems){ - - if (feedItem.photos.count > 0){ - hasPhotos = YES; - } - if (feedItem.videos.count > 0){ - hasVideos = YES; - } - + for (BVCurationsFeedItem *feedItem in feedItems) { + + if (feedItem.photos.count > 0) { + hasPhotos = YES; + } + if (feedItem.videos.count > 0) { + hasVideos = YES; + } } - - XCTAssertTrue(hasPhotos, @"Test feed did not have photos, but should have."); - XCTAssertTrue(hasVideos, @"Test feed did not have videos, but should have."); - + + XCTAssertTrue(hasPhotos, + @"Test feed did not have photos, but should have."); + XCTAssertTrue(hasVideos, + @"Test feed did not have videos, but should have."); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; XCTAssert(errorString == nil, @"%@", errorString); - + [expectation fulfill]; - }]; - - [self waitForExpectations]; + }]; + + [self waitForExpectations]; } // Test fetching curations with user's geolocation - (void)testFetchCurationsWithLocation { - - [self addStubWith200ResponseForJSONFileNamed:@"curationsFeedTest1.json"]; - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchCurationsWithLocation"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"__all__" ]]; - [feedRequest setLatitude:30.2 longitude:-97.7]; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { + + [self addStubWith200ResponseForJSONFileNamed:@"curationsFeedTest1.json"]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testFetchCurationsWithLocation"]; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"__all__" ]]; + [feedRequest setLatitude:30.2 longitude:-97.7]; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { // success! - - XCTAssertNotNil(feedItems, @"ERROR: feeItems should not be nil in curations api response."); - + + XCTAssertNotNil( + feedItems, + @"ERROR: feeItems should not be nil in curations api response."); + int locationCount = 0; - for (BVCurationsFeedItem *feedItem in feedItems){ - - if (feedItem.coordinates != nil && feedItem.coordinates.latitude != nil && feedItem.coordinates.longitude != nil) { - locationCount += 1; - } - + for (BVCurationsFeedItem *feedItem in feedItems) { + + if (feedItem.coordinates != nil && + feedItem.coordinates.latitude != nil && + feedItem.coordinates.longitude != nil) { + locationCount += 1; + } } - - XCTAssertEqual(21, locationCount, @"There should be 11 feed items with coordinates attached to them"); - XCTAssertEqual(40, [feedItems count], @"There should be 20 total feed items"); - + + XCTAssertEqual( + 21, locationCount, + @"There should be 11 feed items with coordinates attached to them"); + XCTAssertEqual(40, [feedItems count], + @"There should be 20 total feed items"); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; XCTAssert(errorString == nil, @"%@", errorString); - + [expectation fulfill]; - }]; - - [self waitForExpectations]; + }]; + + [self waitForExpectations]; } // Test proper failure of malformed JSON - (void)testFetchCurationsMalformedJSON { - - [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchCurationsMalformedJSON"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { - - XCTAssert(NO, @"Success completion block should not have been called here."); - + + [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testFetchCurationsMalformedJSON"]; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { + + XCTAssert( + NO, @"Success completion block should not have been called here."); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; - XCTAssert(errorString != nil, @"Expected a non nil error string: %@", errorString); - + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; + XCTAssert(errorString != nil, @"Expected a non nil error string: %@", + errorString); + [expectation fulfill]; - }]; - - [self waitForExpectations]; -} + }]; + [self waitForExpectations]; +} // Test proper failure of empty body but 200 response - (void)testEmptyBodyFromFeedRequest { - - [self addStubWith200ResponseForJSONFileNamed:@""]; - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testEmptyBodyFromFeedRequest"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { - - XCTAssert(NO, @"Success completion block should not have been called here."); - + + [self addStubWith200ResponseForJSONFileNamed:@""]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testEmptyBodyFromFeedRequest"]; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { + + XCTAssert( + NO, @"Success completion block should not have been called here."); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; - XCTAssert(errorString != nil, @"Expected a non nil error string: %@", errorString); - + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; + XCTAssert(errorString != nil, @"Expected a non nil error string: %@", + errorString); + [expectation fulfill]; - }]; - - [self waitForExpectations]; -} + }]; + [self waitForExpectations]; +} // HTTP status 500 - (void)testServerError500 { - - [self addStubWithResultFile:@"" statusCode:500 withHeaders:@{@"Content-Type":@"application/json"}]; - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testEmptyBodyFromFeedRequest"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { - - XCTAssert(NO, @"Success completion block should not have been called here."); - + + [self addStubWithResultFile:@"" + statusCode:500 + withHeaders:@{@"Content-Type" : @"application/json"}]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testEmptyBodyFromFeedRequest"]; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { + + XCTAssert( + NO, @"Success completion block should not have been called here."); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; - XCTAssert(errorString != nil, @"Expected a non nil error string: %@", errorString); - + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; + XCTAssert(errorString != nil, @"Expected a non nil error string: %@", + errorString); + [expectation fulfill]; - }]; - - [self waitForExpectations]; -} + }]; + [self waitForExpectations]; +} // Test proper failure of empty body but 200 response - (void)testNon200Status { - - [self addStubWithResultFile:@"curations500Error.json" statusCode:200 withHeaders:@{ - @"Content-Type":@"application/json;charset=utf-8", - @"Access-Control-Allow-Origin" : @"*", - @"Connection" : @"keep-alive", - @"Date" : @"Wed, 30 Mar 2016 15:52:51 GMT", - @"Server" : @"nginx/1.6.2", - @"Vary" : @"Accept-Encoding", - @"X-Mashery-Responder" : @"prod-j-worker-bv-us-west-1c-06.mashery.com" - }]; - - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testNon200Status"]; - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; - - BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; - - [urlRequest loadFeedWithRequest:feedRequest completionHandler:^(NSArray *feedItems) { - - XCTAssert(NO, @"Success completion block should not have been called here."); - + + [self addStubWithResultFile:@"curations500Error.json" + statusCode:200 + withHeaders:@{ + @"Content-Type" : @"application/json;charset=utf-8", + @"Access-Control-Allow-Origin" : @"*", + @"Connection" : @"keep-alive", + @"Date" : @"Wed, 30 Mar 2016 15:52:51 GMT", + @"Server" : @"nginx/1.6.2", + @"Vary" : @"Accept-Encoding", + @"X-Mashery-Responder" : + @"prod-j-worker-bv-us-west-1c-06.mashery.com" + }]; + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testNon200Status"]; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"livebv" ]]; + + BVCurationsFeedLoader *urlRequest = [[BVCurationsFeedLoader alloc] init]; + + [urlRequest loadFeedWithRequest:feedRequest + completionHandler:^(NSArray *feedItems) { + + XCTAssert( + NO, @"Success completion block should not have been called here."); + [expectation fulfill]; - - } withFailure:^(NSError *error) { + + } + withFailure:^(NSError *error) { // failure : ( - - NSString *errorString = [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", error.localizedDescription]; - XCTAssert(errorString != nil, @"Expected a non nil error string: %@", errorString); - + + NSString *errorString = + [NSString stringWithFormat:@"ERROR: Curations API feed failure: %@", + error.localizedDescription]; + XCTAssert(errorString != nil, @"Expected a non nil error string: %@", + errorString); + [expectation fulfill]; - }]; - - [self waitForExpectations]; + }]; + + [self waitForExpectations]; } -// Test setting all the display api query string input and that we can fetch them out parameterized as NSURLQueryItem objects. +// Test setting all the display api query string input and that we can fetch +// them out parameterized as NSURLQueryItem objects. - (void)testCurationsFeedQueryStringParams { - - NSDictionary *expectedResults = @{ - @"passkey" : @"fakeymcfakersonfakekey", - @"client" : @"test-classic", - @"limit" : @"33", - @"groups" : @"group-1", - @"tags" : @"tag2", - @"before" : @"1451606400", - @"after" : @"1325376000", - @"author" : @"testAuthor", - @"featured" : @"3", - @"has_geotag" : @"true", - @"has_photo" : @"true", - @"has_link" : @"true", - @"has_video" : @"true", - @"withProductData" : @"true", - @"include_comments" : @"true", - @"externalId" : @"123abdDEF", - @"media" : @"{\"video\":{\"width\":\"480\",\"height\":\"360\"}}" - }; - - - BVCurationsFeedRequest *feedRequest = [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"group-1"]]; - - feedRequest.limit = 33; - feedRequest.tags = @[ @"tag2"]; - - // media={'video':{'width':480,'height':360}} - feedRequest.media = @{ - @"video" : @{ - @"width" : @"480", - @"height" : @"360" - } - }; - - feedRequest.author = @"testAuthor"; - feedRequest.externalId = @"123abdDEF"; - - feedRequest.featured = 3; - - feedRequest.hasGeotag = @YES; - feedRequest.hasLink = @YES; - feedRequest.hasVideo = @YES; - feedRequest.hasPhoto = @YES; - feedRequest.includeComments = @YES; - feedRequest.withProductData = @YES; - - feedRequest.before = [NSNumber numberWithLong:1451606400]; // Fri, 01 Jan 2016 00:00:00 GMT - feedRequest.after = [NSNumber numberWithLong:1325376000]; // Sun, 01 Jan 2012 00:00:00 GMT - - NSArray *queryParams = [feedRequest createQueryItems]; - - XCTAssertTrue([queryParams count] == [[expectedResults allKeys] count], @"Number of query items is not equal to the expected results dictionary"); - - for (NSURLQueryItem *qi in queryParams){ - - NSString *name = qi.name; - NSString *value = qi.value; - - NSString *expectedString = [expectedResults objectForKey:name]; - - XCTAssertTrue([expectedString isEqualToString:value], @"ERROR: Query string value %@ for name %@ is not equal to expected value %@", value, name, expectedString); - - } - + + NSDictionary *expectedResults = @{ + @"passkey" : @"fakeymcfakersonfakekey", + @"client" : @"test-classic", + @"limit" : @"33", + @"groups" : @"group-1", + @"tags" : @"tag2", + @"before" : @"1451606400", + @"after" : @"1325376000", + @"author" : @"testAuthor", + @"featured" : @"3", + @"has_geotag" : @"true", + @"has_photo" : @"true", + @"has_link" : @"true", + @"has_video" : @"true", + @"withProductData" : @"true", + @"include_comments" : @"true", + @"externalId" : @"123abdDEF", + @"media" : @"{\"video\":{\"width\":\"480\",\"height\":\"360\"}}" + }; + + BVCurationsFeedRequest *feedRequest = + [[BVCurationsFeedRequest alloc] initWithGroups:@[ @"group-1" ]]; + + feedRequest.limit = 33; + feedRequest.tags = @[ @"tag2" ]; + + // media={'video':{'width':480,'height':360}} + feedRequest.media = @{ @"video" : @{@"width" : @"480", @"height" : @"360"} }; + + feedRequest.author = @"testAuthor"; + feedRequest.externalId = @"123abdDEF"; + + feedRequest.featured = 3; + + feedRequest.hasGeotag = @YES; + feedRequest.hasLink = @YES; + feedRequest.hasVideo = @YES; + feedRequest.hasPhoto = @YES; + feedRequest.includeComments = @YES; + feedRequest.withProductData = @YES; + + feedRequest.before = + [NSNumber numberWithLong:1451606400]; // Fri, 01 Jan 2016 00:00:00 GMT + feedRequest.after = + [NSNumber numberWithLong:1325376000]; // Sun, 01 Jan 2012 00:00:00 GMT + + NSArray *queryParams = [feedRequest createQueryItems]; + + XCTAssertTrue( + [queryParams count] == [[expectedResults allKeys] count], + @"Number of query items is not equal to the expected results dictionary"); + + for (NSURLQueryItem *qi in queryParams) { + + NSString *name = qi.name; + NSString *value = qi.value; + + NSString *expectedString = [expectedResults objectForKey:name]; + + XCTAssertTrue([expectedString isEqualToString:value], + @"ERROR: Query string value %@ for name %@ is not equal to " + @"expected value %@", + value, name, expectedString); + } } -// Test for serialization/de-serialization of the API parameters for BVCurationsAddPostParams +// Test for serialization/de-serialization of the API parameters for +// BVCurationsAddPostParams - (void)testPostParamsOnly { - - // Test inputs - required - NSString *aliasInput = @"aliasText"; - NSString *tokenInput = @"tokenText"; - NSString *textInput = @"test text: barrells of monkeys u haz"; - NSArray *groupsInput = @[@"group1-one", @"group2-two", @"group3-three"]; - - // Test input - optionals - - NSString *proifleURLInput = @"http://profileurltest"; - NSString *avatarURLInput = @"http://avatarurl"; - double longInput = 21.98765; - double latInput = 0.12345; - - NSArray *tagsInput = @[@"tag1", @"tag2"]; - NSString *permalinkInput = @"http://permalinktest"; - NSString *placeInput = @"Austin, TX"; - NSString *teaserInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; - - NSTimeInterval timeStampInput = [[NSDate date] timeIntervalSince1970]; - - NSArray *linksInput = @[@"http://www.bazaarvoice.com/", @"http://acl-live.com/"]; - NSArray *photosInput = @[@"http://homeopathyplus.com/wp-content/uploads/2013/01/MotherTeresa-223x300.png", @"https://upload.wikimedia.org/wikipedia/commons/6/6f/Einstein-formal_portrait-35.jpg"]; - - UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; - - BVCurationsAddPostRequest *params = [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput withAuthorAlias:aliasInput withToken:tokenInput withText:textInput withImage:testImage]; - - params.latitude = latInput; - params.longitude = longInput; - - params.authorProfileURL = proifleURLInput; - params.authorAvatarURL = avatarURLInput; - - params.tags = tagsInput; - params.permalink = permalinkInput; - params.place = placeInput; - params.teaser = teaserInput; - params.unixTimeStamp = timeStampInput; - - params.links = linksInput; - params.photos = photosInput; - - NSData *jsonData = params.serializeParameters; - - NSError *error; - NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error]; - - if (error || jsonDict == nil){ - // fail - XCTAssertTrue(NO, @"An error occurred deserializing the parameters."); - } - - NSLog(@"%@", jsonDict); - - NSString *aliasTest= [[jsonDict objectForKey:@"author"] objectForKey:@"alias"]; - NSString *tokenTest = [[jsonDict objectForKey:@"author"] objectForKey:@"token"]; - NSString *textTest = [jsonDict objectForKey:@"text"]; - NSArray *groupsTest = [jsonDict objectForKey:@"groups"]; - NSArray *tagsTest = [jsonDict objectForKey:@"tags"]; - - NSString *authorProfileURLTest = [[jsonDict objectForKey:@"author"] objectForKey:@"profile"]; - NSString *avatarURLTest = [[jsonDict objectForKey:@"author"] objectForKey:@"avatar"]; - - NSString *permaLinkTest = [jsonDict objectForKey:@"permalink"]; - NSString *placeTest = [jsonDict objectForKey:@"place"]; - NSString *teaserTest = [jsonDict objectForKey:@"teaser"]; - NSTimeInterval timeStampTest = [[jsonDict objectForKey:@"timestamp"] doubleValue]; - - NSArray *linksTest = [jsonDict objectForKey:@"links"]; - NSArray *photosTest = [jsonDict objectForKey:@"photos"]; - - double latitudeTest = [[[jsonDict objectForKey:@"coordinates"] objectForKey:@"x"] doubleValue]; - double longitudeTest = [[[jsonDict objectForKey:@"coordinates"] objectForKey:@"y"] doubleValue]; - - - // Test validation - XCTAssertTrue([aliasInput isEqualToString:aliasTest], @"Equivalency test failed."); - XCTAssertTrue([tokenInput isEqualToString:tokenTest], @"Equivalency test failed."); - XCTAssertTrue([textInput isEqualToString:textTest], @"Equivalency test failed."); - XCTAssertTrue([groupsInput isEqualToArray:groupsTest], @"Equivalency test failed."); - - XCTAssertTrue([proifleURLInput isEqualToString:authorProfileURLTest], @"Equivalency test failed."); - XCTAssertTrue([avatarURLInput isEqualToString:avatarURLTest], @"Equivalency test failed."); - XCTAssertTrue([tagsInput isEqualToArray:tagsTest], @"Equivalency test failed."); - XCTAssertTrue([permalinkInput isEqualToString:permaLinkTest], @"Equivalency test failed."); - XCTAssertTrue([placeInput isEqualToString:placeTest], @"Equivalency test failed."); - XCTAssertTrue([teaserInput isEqualToString:teaserTest], @"Equivalency test failed."); - - XCTAssertEqual([linksTest count], 2, @"Links count was incorrect."); - XCTAssertEqual([photosTest count], 2, @"Photos count was incorrect."); - - double inputRounded = floorf(timeStampInput); - double testCaseRounded = floorf(timeStampTest); - - XCTAssertEqual(inputRounded, testCaseRounded, @"Equivalency test failed."); - XCTAssertEqual(latInput, latitudeTest, @"Latitude test failed."); - XCTAssertEqual(longInput, longitudeTest, @"Longitude test failed."); - - XCTAssertNotNil(params.image, @"Image should not have been nil"); - + + // Test inputs - required + NSString *aliasInput = @"aliasText"; + NSString *tokenInput = @"tokenText"; + NSString *textInput = @"test text: barrells of monkeys u haz"; + NSArray *groupsInput = @[ @"group1-one", @"group2-two", @"group3-three" ]; + + // Test input - optionals + + NSString *proifleURLInput = @"http://profileurltest"; + NSString *avatarURLInput = @"http://avatarurl"; + double longInput = 21.98765; + double latInput = 0.12345; + + NSArray *tagsInput = @[ @"tag1", @"tag2" ]; + NSString *permalinkInput = @"http://permalinktest"; + NSString *placeInput = @"Austin, TX"; + NSString *teaserInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; + + NSTimeInterval timeStampInput = [[NSDate date] timeIntervalSince1970]; + + NSArray *linksInput = + @[ @"http://www.bazaarvoice.com/", @"http://acl-live.com/" ]; + NSArray *photosInput = @[ + @"http://homeopathyplus.com/wp-content/uploads/2013/01/" + @"MotherTeresa-223x300.png", + @"https://upload.wikimedia.org/wikipedia/commons/6/6f/" + @"Einstein-formal_portrait-35.jpg" + ]; + + UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; + + BVCurationsAddPostRequest *params = + [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput + withAuthorAlias:aliasInput + withToken:tokenInput + withText:textInput + withImage:testImage]; + + params.latitude = latInput; + params.longitude = longInput; + + params.authorProfileURL = proifleURLInput; + params.authorAvatarURL = avatarURLInput; + + params.tags = tagsInput; + params.permalink = permalinkInput; + params.place = placeInput; + params.teaser = teaserInput; + params.unixTimeStamp = timeStampInput; + + params.links = linksInput; + params.photos = photosInput; + + NSData *jsonData = params.serializeParameters; + + NSError *error; + NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData + options:kNilOptions + error:&error]; + + if (error || jsonDict == nil) { + // fail + XCTAssertTrue(NO, @"An error occurred deserializing the parameters."); + } + + NSLog(@"%@", jsonDict); + + NSString *aliasTest = + [[jsonDict objectForKey:@"author"] objectForKey:@"alias"]; + NSString *tokenTest = + [[jsonDict objectForKey:@"author"] objectForKey:@"token"]; + NSString *textTest = [jsonDict objectForKey:@"text"]; + NSArray *groupsTest = [jsonDict objectForKey:@"groups"]; + NSArray *tagsTest = [jsonDict objectForKey:@"tags"]; + + NSString *authorProfileURLTest = + [[jsonDict objectForKey:@"author"] objectForKey:@"profile"]; + NSString *avatarURLTest = + [[jsonDict objectForKey:@"author"] objectForKey:@"avatar"]; + + NSString *permaLinkTest = [jsonDict objectForKey:@"permalink"]; + NSString *placeTest = [jsonDict objectForKey:@"place"]; + NSString *teaserTest = [jsonDict objectForKey:@"teaser"]; + NSTimeInterval timeStampTest = + [[jsonDict objectForKey:@"timestamp"] doubleValue]; + + NSArray *linksTest = [jsonDict objectForKey:@"links"]; + NSArray *photosTest = [jsonDict objectForKey:@"photos"]; + + double latitudeTest = + [[[jsonDict objectForKey:@"coordinates"] objectForKey:@"x"] doubleValue]; + double longitudeTest = + [[[jsonDict objectForKey:@"coordinates"] objectForKey:@"y"] doubleValue]; + + // Test validation + XCTAssertTrue([aliasInput isEqualToString:aliasTest], + @"Equivalency test failed."); + XCTAssertTrue([tokenInput isEqualToString:tokenTest], + @"Equivalency test failed."); + XCTAssertTrue([textInput isEqualToString:textTest], + @"Equivalency test failed."); + XCTAssertTrue([groupsInput isEqualToArray:groupsTest], + @"Equivalency test failed."); + + XCTAssertTrue([proifleURLInput isEqualToString:authorProfileURLTest], + @"Equivalency test failed."); + XCTAssertTrue([avatarURLInput isEqualToString:avatarURLTest], + @"Equivalency test failed."); + XCTAssertTrue([tagsInput isEqualToArray:tagsTest], + @"Equivalency test failed."); + XCTAssertTrue([permalinkInput isEqualToString:permaLinkTest], + @"Equivalency test failed."); + XCTAssertTrue([placeInput isEqualToString:placeTest], + @"Equivalency test failed."); + XCTAssertTrue([teaserInput isEqualToString:teaserTest], + @"Equivalency test failed."); + + XCTAssertEqual([linksTest count], 2, @"Links count was incorrect."); + XCTAssertEqual([photosTest count], 2, @"Photos count was incorrect."); + + double inputRounded = floorf(timeStampInput); + double testCaseRounded = floorf(timeStampTest); + + XCTAssertEqual(inputRounded, testCaseRounded, @"Equivalency test failed."); + XCTAssertEqual(latInput, latitudeTest, @"Latitude test failed."); + XCTAssertEqual(longInput, longitudeTest, @"Longitude test failed."); + + XCTAssertNotNil(params.image, @"Image should not have been nil"); } - (void)testPostPhotoSuccess { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPostPhotoSuccess"]; - - [self addStubWith200ResponseForJSONFileNamed:@"post_successfulCreation.json"]; - - // Construct the parmas with required - NSString *aliasInput = @"mobileUnitTest"; - NSString *tokenInput = @"mobilecoreteam@bazaarvoice.com"; - NSString *textInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; - NSArray *groupsInput = @[@"pie-test"]; - NSArray *tags = @[@"submission-app", @"product1", @"sampleCategory"]; - - UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; - - BVCurationsAddPostRequest *params = [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput withAuthorAlias:aliasInput withToken:tokenInput withText:textInput]; - - params.image = testImage; - params.tags = tags; - - // Hit the API - BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; - - [uploadAPI submitCurationsContentWithParams:params completionHandler:^(void) { + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPostPhotoSuccess"]; + + [self addStubWith200ResponseForJSONFileNamed:@"post_successfulCreation.json"]; + + // Construct the parmas with required + NSString *aliasInput = @"mobileUnitTest"; + NSString *tokenInput = @"mobilecoreteam@bazaarvoice.com"; + NSString *textInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; + NSArray *groupsInput = @[ @"pie-test" ]; + NSArray *tags = @[ @"submission-app", @"product1", @"sampleCategory" ]; + + UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; + + BVCurationsAddPostRequest *params = + [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput + withAuthorAlias:aliasInput + withToken:tokenInput + withText:textInput]; + + params.image = testImage; + params.tags = tags; + + // Hit the API + BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; + + [uploadAPI submitCurationsContentWithParams:params + completionHandler:^(void) { // completion [expectation fulfill]; - } withFailure:^(NSError *error) { + } + withFailure:^(NSError *error) { // error NSLog(@"ERROR: %@", error.localizedDescription); - XCTAssertTrue(NO, @"Error block called in test should not have been called."); + XCTAssertTrue( + NO, @"Error block called in test should not have been called."); [expectation fulfill]; - }]; - - [self waitForExpectations]; - + }]; + + [self waitForExpectations]; } +- (void)testPostPhotoFail { + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPostPhotoFail"]; + [self addStubWith200ResponseForJSONFileNamed:@"post_ErrorParsingBody.json"]; -- (void)testPostPhotoFail { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPostPhotoFail"]; - - [self addStubWith200ResponseForJSONFileNamed:@"post_ErrorParsingBody.json"]; - - // Construct the parmas with required - NSString *aliasInput = @"mobileUnitTest"; - NSString *tokenInput = @"mobilecoreteam@bazaarvoice.com"; - NSString *textInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; - NSArray *groupsInput = @[@"pie-test"]; - NSArray *tags = @[@"submission-app", @"product1", @"sampleCategory"]; - - UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; - - BVCurationsAddPostRequest *params = [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput withAuthorAlias:aliasInput withToken:tokenInput withText:textInput]; - - params.image = testImage; - params.tags = tags; - - // Hit the API - BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; - - [uploadAPI submitCurationsContentWithParams:params completionHandler:^(void) { + // Construct the parmas with required + NSString *aliasInput = @"mobileUnitTest"; + NSString *tokenInput = @"mobilecoreteam@bazaarvoice.com"; + NSString *textInput = @"Testing «ταБЬℓσ»: 1<2 & 4+1>3, now 20% off!"; + NSArray *groupsInput = @[ @"pie-test" ]; + NSArray *tags = @[ @"submission-app", @"product1", @"sampleCategory" ]; + + UIImage *testImage = [self loadTestImageNamed:@"test_pattern.jpg"]; + + BVCurationsAddPostRequest *params = + [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput + withAuthorAlias:aliasInput + withToken:tokenInput + withText:textInput]; + + params.image = testImage; + params.tags = tags; + + // Hit the API + BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; + + [uploadAPI submitCurationsContentWithParams:params + completionHandler:^(void) { // completion - XCTAssertTrue(NO, @"Success block called in test which should have failed."); + XCTAssertTrue( + NO, @"Success block called in test which should have failed."); [expectation fulfill]; - } withFailure:^(NSError *error) { + } + withFailure:^(NSError *error) { // error NSLog(@"ERROR: %@", error.localizedDescription); - + XCTAssertNotNil(error, @"Got a nil NSError object"); XCTAssertEqual(error.code, 500, @"Expected error code 500"); - + [expectation fulfill]; - }]; - - [self waitForExpectations]; - -} + }]; + [self waitForExpectations]; +} - (void)testPostMissingRequiredKey { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPostMissingRequiredKey"]; - - [self addStubWith200ResponseForJSONFileNamed:@"post_MissingRequiredKey.json"]; - - // Construct the parmas with required - NSString *aliasInput = @""; - NSString *tokenInput = @""; - NSString *textInput = @""; - NSArray *groupsInput = @[]; - - BVCurationsAddPostRequest *params = [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput withAuthorAlias:aliasInput withToken:tokenInput withText:textInput]; - - // Hit the API - BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; - - [uploadAPI submitCurationsContentWithParams:params completionHandler:^(void) { + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPostMissingRequiredKey"]; + + [self addStubWith200ResponseForJSONFileNamed:@"post_MissingRequiredKey.json"]; + + // Construct the parmas with required + NSString *aliasInput = @""; + NSString *tokenInput = @""; + NSString *textInput = @""; + NSArray *groupsInput = @[]; + + BVCurationsAddPostRequest *params = + [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput + withAuthorAlias:aliasInput + withToken:tokenInput + withText:textInput]; + + // Hit the API + BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; + + [uploadAPI submitCurationsContentWithParams:params + completionHandler:^(void) { // completion - XCTAssertTrue(NO, @"Success block called in test which should have failed."); + XCTAssertTrue( + NO, @"Success block called in test which should have failed."); [expectation fulfill]; - } withFailure:^(NSError *error) { + } + withFailure:^(NSError *error) { // error NSLog(@"ERROR: %@", error.localizedDescription); XCTAssertNotNil(error, @"Got a nil NSError object"); XCTAssertEqual(error.code, 400, @"Expected error code 400"); - + [expectation fulfill]; - }]; - - [self waitForExpectations]; - -} + }]; + [self waitForExpectations]; +} - (void)testPostMalformedJSONResponse { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPostMalformedJSONResponse"]; - - [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; - - // Construct the parmas with required - NSString *aliasInput = @""; - NSString *tokenInput = @""; - NSString *textInput = @""; - NSArray *groupsInput = @[]; - - BVCurationsAddPostRequest *params = [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput withAuthorAlias:aliasInput withToken:tokenInput withText:textInput]; - - // Hit the API - BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; - - [uploadAPI submitCurationsContentWithParams:params completionHandler:^(void) { + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPostMalformedJSONResponse"]; + + [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; + + // Construct the parmas with required + NSString *aliasInput = @""; + NSString *tokenInput = @""; + NSString *textInput = @""; + NSArray *groupsInput = @[]; + + BVCurationsAddPostRequest *params = + [[BVCurationsAddPostRequest alloc] initWithGroups:groupsInput + withAuthorAlias:aliasInput + withToken:tokenInput + withText:textInput]; + + // Hit the API + BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; + + [uploadAPI submitCurationsContentWithParams:params + completionHandler:^(void) { // completion - XCTAssertTrue(NO, @"Success block called in test which should have failed."); + XCTAssertTrue( + NO, @"Success block called in test which should have failed."); [expectation fulfill]; - } withFailure:^(NSError *error) { + } + withFailure:^(NSError *error) { // error XCTAssertNotNil(error, @"Got a nil NSError object"); XCTAssertEqual(error.code, -1, @"Expected error code -1"); - + [expectation fulfill]; - }]; - - [self waitForExpectations]; - -} + }]; + [self waitForExpectations]; +} - (void)testPostNilRequestObject { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPostNilRequestObject"]; - - // Hit the API - which should never make an API call and just return the error handler - BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; - - [uploadAPI submitCurationsContentWithParams:nil completionHandler:^(void) { + + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPostNilRequestObject"]; + + // Hit the API - which should never make an API call and just return the error + // handler + BVCurationsPhotoUploader *uploadAPI = [[BVCurationsPhotoUploader alloc] init]; + + [uploadAPI submitCurationsContentWithParams:nil + completionHandler:^(void) { // completion NSLog(@"success"); - XCTAssertTrue(NO, @"Success block called in test which should have failed."); + XCTAssertTrue( + NO, @"Success block called in test which should have failed."); [expectation fulfill]; - } withFailure:^(NSError *error) { + } + withFailure:^(NSError *error) { // error NSLog(@"ERROR: %@", error.localizedDescription); XCTAssertNotNil(error, @"Got a nil NSError object"); XCTAssertEqual(error.code, -1, @"Expected error code -1"); - - [expectation fulfill]; - }]; - - [self waitForExpectations]; - -} + [expectation fulfill]; + }]; -- (void) waitForExpectations{ - - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - - }]; + [self waitForExpectations]; } +- (void)waitForExpectations { -@end + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } + }]; +} +@end diff --git a/Tests/Tests/BVInternalAnalyticsTest.m b/Tests/Tests/BVInternalAnalyticsTest.m index 4b3aa903..dff94056 100644 --- a/Tests/Tests/BVInternalAnalyticsTest.m +++ b/Tests/Tests/BVInternalAnalyticsTest.m @@ -9,28 +9,29 @@ #import #import +#import +#import #import -#import #import -#import #import #import -#import -#import +#import +#import #import "BVBaseStubTestCase.h" -#define ANALYTICS_TEST_USING_MOCK_DATA 1 // Setting to 1 uses mock result. Set to 0 to make network request. +#define ANALYTICS_TEST_USING_MOCK_DATA \ + 1 // Setting to 1 uses mock result. Set to 0 to make network request. @interface BVAnalyticsManager (TestAccessors) -@property (strong) NSMutableArray* eventQueue; +@property(strong) NSMutableArray *eventQueue; @end @interface BVInternalAnalyticsTests : BVBaseStubTestCase { - XCTestExpectation *impressionExpectation; - XCTestExpectation *pageviewExpectation; - int numberOfExpectedImpressionAnalyticsEvents; - int numberOfExpectedPageviewAnalyticsEvents; + XCTestExpectation *impressionExpectation; + XCTestExpectation *pageviewExpectation; + int numberOfExpectedImpressionAnalyticsEvents; + int numberOfExpectedPageviewAnalyticsEvents; } @end @@ -39,413 +40,428 @@ @interface BVInternalAnalyticsTests : BVBaseStubTestCase { #define TEST_KEY_SHOPPER_ADVERTISING @"fakeshopperadskey" #define TEST_KEY_CONVERSATIONS @"faketestkeyconversations" - @implementation BVInternalAnalyticsTests -- (void)setUp -{ - [super setUp]; - NSDictionary *configDict = @{@"apiKeyConversations": TEST_KEY_CONVERSATIONS, - @"apiKeyShopperAdvertising": TEST_KEY_SHOPPER_ADVERTISING, - @"clientId": TEST_CLIENT_ID}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - - // Global logging level - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelAnalyticsOnly]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(analyticsPageviewEventCompleted:) - name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(analyticsImpressionEventCompleted:) - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; - - impressionExpectation = [self expectationWithDescription:@"Expecting impression analytics events"]; - pageviewExpectation = [self expectationWithDescription:@"Expecting pageview analytics events"]; - numberOfExpectedImpressionAnalyticsEvents = 0; - numberOfExpectedPageviewAnalyticsEvents = 0; +- (void)setUp { + [super setUp]; + NSDictionary *configDict = @{ + @"apiKeyConversations" : TEST_KEY_CONVERSATIONS, + @"apiKeyShopperAdvertising" : TEST_KEY_SHOPPER_ADVERTISING, + @"clientId" : TEST_CLIENT_ID + }; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + + // Global logging level + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelAnalyticsOnly]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(analyticsPageviewEventCompleted:) + name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(analyticsImpressionEventCompleted:) + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; + + impressionExpectation = [self + expectationWithDescription:@"Expecting impression analytics events"]; + pageviewExpectation = + [self expectationWithDescription:@"Expecting pageview analytics events"]; + numberOfExpectedImpressionAnalyticsEvents = 0; + numberOfExpectedPageviewAnalyticsEvents = 0; } --(void)tearDown { - - [super tearDown]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" - object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; +- (void)tearDown { + [super tearDown]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_PAGEVIEW_ANALYTICS_COMPLETED" + object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; } --(void)waitForAnalytics { - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - }]; +- (void)waitForAnalytics { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } + }]; } --(void)analyticsImpressionEventCompleted:(NSNotification *)notification { - - NSLog(@"analytics impression event fired in tests: %i", numberOfExpectedImpressionAnalyticsEvents); - - NSError *err = (NSError *)[notification object]; - if (err){ - XCTFail(@"ERROR: Analytic event failed %@", err); - } else { - NSLog(@"Analytic Impression HTTP success."); - } - - numberOfExpectedImpressionAnalyticsEvents -= 1; - [self checkComplete]; +- (void)analyticsImpressionEventCompleted:(NSNotification *)notification { + NSLog(@"analytics impression event fired in tests: %i", + numberOfExpectedImpressionAnalyticsEvents); + + NSError *err = (NSError *)[notification object]; + if (err) { + XCTFail(@"ERROR: Analytic event failed %@", err); + } else { + NSLog(@"Analytic Impression HTTP success."); + } + numberOfExpectedImpressionAnalyticsEvents -= 1; + [self checkComplete]; } --(void)analyticsPageviewEventCompleted:(NSNotification *)notification { +- (void)analyticsPageviewEventCompleted:(NSNotification *)notification { + NSLog(@"analytics pageview event fired in tests: %i", + numberOfExpectedPageviewAnalyticsEvents); - NSLog(@"analytics pageview event fired in tests: %i", numberOfExpectedPageviewAnalyticsEvents); - - NSError *err = (NSError *)[notification object]; - if (err){ - XCTFail(@"ERROR: Analytic event failed %@", err); - } else { - NSLog(@"Analytic Page View HTTP success."); - } - - numberOfExpectedPageviewAnalyticsEvents -= 1; - [self checkComplete]; + NSError *err = (NSError *)[notification object]; + if (err) { + XCTFail(@"ERROR: Analytic event failed %@", err); + } else { + NSLog(@"Analytic Page View HTTP success."); + } + numberOfExpectedPageviewAnalyticsEvents -= 1; + [self checkComplete]; } --(void)checkComplete { - if(numberOfExpectedImpressionAnalyticsEvents == 0){ - [impressionExpectation fulfill]; - numberOfExpectedImpressionAnalyticsEvents = -1; - } - if(numberOfExpectedPageviewAnalyticsEvents == 0){ - [pageviewExpectation fulfill]; - numberOfExpectedPageviewAnalyticsEvents = -1; - } +- (void)checkComplete { + if (numberOfExpectedImpressionAnalyticsEvents == 0) { + [impressionExpectation fulfill]; + numberOfExpectedImpressionAnalyticsEvents = -1; + } + if (numberOfExpectedPageviewAnalyticsEvents == 0) { + [pageviewExpectation fulfill]; + numberOfExpectedPageviewAnalyticsEvents = -1; + } } #pragma mark BVConversations analytics /* -(void)testAnalyticsCompletesSmall { - + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; #endif - + numberOfExpectedImpressionAnalyticsEvents = 1; numberOfExpectedPageviewAnalyticsEvents = 1; [self fireReviewAnalyticsCompletesWithLimit:1]; - + [[BVAnalyticsManager sharedManager] flushQueue]; } -(void)testAnalyticsCompletesBatched { - + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; #endif - + numberOfExpectedPageviewAnalyticsEvents = 1; numberOfExpectedImpressionAnalyticsEvents = 1; [self fireReviewAnalyticsCompletesWithLimit:60]; - + [[BVAnalyticsManager sharedManager] flushQueue]; } */ --(void)testBigAnalyticsEvent { - - // bomb the analytics event with a ton of events, to make sure it doesn't crash. - // The event queue is protected by a dispatch barrier to ensure the analytics manager is thread-safe. +- (void)testBigAnalyticsEvent { + // bomb the analytics event with a ton of events, to make sure it doesn't + // crash. The event queue is protected by a dispatch barrier to ensure the + // analytics manager is thread-safe. #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowReview.json"]; #endif - - numberOfExpectedPageviewAnalyticsEvents = 1; - numberOfExpectedImpressionAnalyticsEvents = 1; - - BVReviewsRequest* request = [[BVReviewsRequest alloc] initWithProductId:@"test1" limit:10 offset:20]; - [request load:^(BVReviewsResponse * _Nonnull response) { - // pass - } failure:^(NSArray * _Nonnull errors) { + + numberOfExpectedPageviewAnalyticsEvents = 1; + numberOfExpectedImpressionAnalyticsEvents = 1; + + BVReviewsRequest *request = + [[BVReviewsRequest alloc] initWithProductId:@"test1" limit:10 offset:20]; + [request load:^(BVReviewsResponse *__nonnull response) { + // pass + } + failure:^(NSArray *__nonnull errors) { // fail XCTFail("Failure block should not get hit."); - }]; - - BVReviewsRequest* request2 = [[BVReviewsRequest alloc] initWithProductId:@"test2" limit:10 offset:20]; - [request2 load:^(BVReviewsResponse * _Nonnull response) { - // pass - } failure:^(NSArray * _Nonnull errors) { + }]; + + BVReviewsRequest *request2 = + [[BVReviewsRequest alloc] initWithProductId:@"test2" limit:10 offset:20]; + [request2 load:^(BVReviewsResponse *__nonnull response) { + // pass + } + failure:^(NSArray *__nonnull errors) { // fail XCTFail("Failure block should not get hit."); - }]; - - - BVProductDisplayPageRequest* pdpRequest = [[BVProductDisplayPageRequest alloc] initWithProductId:@"test4"]; - [pdpRequest includeStatistics:PDPContentTypeReviews]; - [pdpRequest includeStatistics:PDPContentTypeQuestions]; - [pdpRequest load:^(BVProductsResponse * _Nonnull response) { - [[BVAnalyticsManager sharedManager] flushQueue]; - } - failure:^(NSArray * _Nonnull errors) { + }]; + + BVProductDisplayPageRequest *pdpRequest = + [[BVProductDisplayPageRequest alloc] initWithProductId:@"test4"]; + [pdpRequest includeStatistics:PDPContentTypeReviews]; + [pdpRequest includeStatistics:PDPContentTypeQuestions]; + [pdpRequest load:^(BVProductsResponse *__nonnull response) { + [[BVAnalyticsManager sharedManager] flushQueue]; + } + failure:^(NSArray *__nonnull errors) { XCTFail("Failure block should not get hit."); - }]; - - [self waitForAnalytics]; -} + }]; + [self waitForAnalytics]; +} --(void)testAnalyticsQuestion { - +- (void)testAnalyticsQuestion { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestion.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowQuestion.json"]; #endif - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 1; - - - BVQuestionsAndAnswersRequest *request = [[BVQuestionsAndAnswersRequest alloc] initWithProductId:@"test1" limit:20 offset:0]; - [request load:^(BVQuestionsAndAnswersResponse * _Nonnull response) { - // success - [[BVAnalyticsManager sharedManager] flushQueue]; - } failure:^(NSArray * _Nonnull errors) { - //error + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 1; + + BVQuestionsAndAnswersRequest *request = + [[BVQuestionsAndAnswersRequest alloc] initWithProductId:@"test1" + limit:20 + offset:0]; + [request load:^(BVQuestionsAndAnswersResponse *__nonnull response) { + // success + [[BVAnalyticsManager sharedManager] flushQueue]; + } + failure:^(NSArray *__nonnull errors) { + // error XCTFail("Accidental failure!"); - }]; - - - [self waitForAnalytics]; -} - + }]; + [self waitForAnalytics]; +} --(void)testAnalyticsProducts { - +- (void)testAnalyticsProducts { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"testShowProducts.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"testShowProducts.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 1; - - // Should send one product page view and one or impression for a review - BVProductDisplayPageRequest* pdpRequest = [[BVProductDisplayPageRequest alloc] initWithProductId:@"test4"]; - [pdpRequest includeStatistics:PDPContentTypeReviews]; - [pdpRequest includeStatistics:PDPContentTypeQuestions]; - [pdpRequest load:^(BVProductsResponse * _Nonnull response) { - [[BVAnalyticsManager sharedManager] flushQueue]; - } - failure:^(NSArray * _Nonnull errors) { + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 1; + + // Should send one product page view and one or impression for a review + BVProductDisplayPageRequest *pdpRequest = + [[BVProductDisplayPageRequest alloc] initWithProductId:@"test4"]; + [pdpRequest includeStatistics:PDPContentTypeReviews]; + [pdpRequest includeStatistics:PDPContentTypeQuestions]; + [pdpRequest load:^(BVProductsResponse *__nonnull response) { + [[BVAnalyticsManager sharedManager] flushQueue]; + } + failure:^(NSArray *__nonnull errors) { XCTFail("Failure block should not get hit."); - }]; - - [self waitForAnalytics]; + }]; + + [self waitForAnalytics]; } /* -(void)testAnalyticsStatistics { - + #if ANALYTICS_TEST_USING_MOCK_DATA == 1 [self addStubWith200ResponseForJSONFileNamed:@"testShowStatistics.json"]; #endif - + numberOfExpectedImpressionAnalyticsEvents = 0; numberOfExpectedPageviewAnalyticsEvents = 0; - + NSArray* productIds = @[@"test1", @"test2", @"test3", @"test4"]; - BVBulkRatingsRequest* request = [[BVBulkRatingsRequest alloc] initWithProductIds:productIds statistics:BulkRatingsStatsTypeAll]; - [request load:^(BVBulkRatingsResponse * _Nonnull response) { + BVBulkRatingsRequest* request = [[BVBulkRatingsRequest alloc] +initWithProductIds:productIds statistics:BulkRatingsStatsTypeAll]; + [request load:^(BVBulkRatingsResponse * nonnull response) { [[BVAnalyticsManager sharedManager] flushQueue]; - } failure:^(NSArray * _Nonnull errors) { + } failure:^(NSArray * nonnull errors) { XCTFail("Failure block should not get hit."); }]; - + [self waitForAnalytics]; } */ #pragma mark BVRecommendations - Feature Used Tests - -- (void)testProductWidgetPageView{ - +- (void)testProductWidgetPageView { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - // General recommendations - Test by sending a bunch of events on threads with different priorities. - - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:20]; - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - }); - - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(void){ - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:20 withProductId:@"product123"]; - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - }); - - dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){ - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:20 withCategoryId:@"categoryId"]; - [BVRecsAnalyticsHelper queueEmbeddedRecommendationsPageViewEvent:request withWidgetType:RecommendationsCarousel]; - }); - - [self waitForAnalytics]; - + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + // General recommendations - Test by sending a bunch of events on threads + // with different priorities. + + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:20]; + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; + }); + + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(void) { + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:20 + withProductId:@"product123"]; + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; + }); + + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) { + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:20 + withCategoryId:@"categoryId"]; + [BVRecsAnalyticsHelper + queueEmbeddedRecommendationsPageViewEvent:request + withWidgetType:RecommendationsCarousel]; + }); + + [self waitForAnalytics]; } -- (void)testProductWidgetSwiped{ - +- (void)testProductWidgetSwiped { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - [self addStubWith200ResponseForJSONFileNamed:@""]; - - [BVRecsAnalyticsHelper queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + [self addStubWith200ResponseForJSONFileNamed:@""]; + + [BVRecsAnalyticsHelper + queueAnalyticsEventForWidgetScroll:RecommendationsCarousel]; + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testProductRecommendationVisible{ - +- (void)testProductRecommendationVisible { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - BVRecommendedProduct *testProduct = [self createFakeProduct]; - - [BVRecsAnalyticsHelper queueAnalyticsEventForProductView:testProduct]; - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; - + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + BVRecommendedProduct *testProduct = [self createFakeProduct]; + + [BVRecsAnalyticsHelper queueAnalyticsEventForProductView:testProduct]; + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testProductFeatureUsed{ - +- (void)testProductFeatureUsed { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - [self addStubWith200ResponseForJSONFileNamed:@""]; - - BVRecommendedProduct *testProduct = [self createFakeProduct]; - [BVRecsAnalyticsHelper queueAnalyticsEventForProductTapped:testProduct]; - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; - -} + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + [self addStubWith200ResponseForJSONFileNamed:@""]; + + BVRecommendedProduct *testProduct = [self createFakeProduct]; + + [BVRecsAnalyticsHelper queueAnalyticsEventForProductTapped:testProduct]; + [[BVAnalyticsManager sharedManager] flushQueue]; -- (BVRecommendedProduct *)createFakeProduct{ - - NSDictionary* fakeProduct = @{ - @"client": @"apitestcustomer", - @"product": @"123456", - @"name": @"Converse shoes", - @"image_url": @"http://www.zomshopping.com/images/l/converse-shoes-black-chuck-taylor-all-star-classic-womens-mens-canvas-sneakers-low-40-178.jpg", - @"product_page_url": @"http://www.bazaarvoice.com/fakeurl", - @"interests": @[ - @"Home & Garden", - @"Tools" - ], - @"category_ids": @[ - @"apitestcustomer/101253", - @"apitestcustomer/100845", - @"apitestcustomer/100896", - @"apitestcustomer/102560", - @"apitestcustomer/104244" - ], - @"RS": @"vav", - @"sponsored": @"false" - }; - - NSDictionary* recStats = @{ - @"RKB": @"1", - @"RKI": @"2", - @"RKP": @"3", - @"RKT": @"5", - @"RKC": @"4", - }; - - BVRecommendedProduct *testProduct = [[BVRecommendedProduct alloc] initWithDictionary:fakeProduct withRecommendationStats:recStats]; - - return testProduct; + [self waitForAnalytics]; } -#pragma mark BVNotifications Tests +- (BVRecommendedProduct *)createFakeProduct { + NSDictionary *fakeProduct = @{ + @"client" : @"apitestcustomer", + @"product" : @"123456", + @"name" : @"Converse shoes", + @"image_url" : @"http://www.zomshopping.com/images/l/" + @"converse-shoes-black-chuck-taylor-all-star-classic-" + @"womens-mens-canvas-sneakers-low-40-178.jpg", + @"product_page_url" : @"http://www.bazaarvoice.com/fakeurl", + @"interests" : @[ @"Home & Garden", @"Tools" ], + @"category_ids" : @[ + @"apitestcustomer/101253", @"apitestcustomer/100845", + @"apitestcustomer/100896", @"apitestcustomer/102560", + @"apitestcustomer/104244" + ], + @"RS" : @"vav", + @"sponsored" : @"false" + }; + + NSDictionary *recStats = @{ + @"RKB" : @"1", + @"RKI" : @"2", + @"RKP" : @"3", + @"RKT" : @"5", + @"RKC" : @"4", + }; + + BVRecommendedProduct *testProduct = + [[BVRecommendedProduct alloc] initWithDictionary:fakeProduct + withRecommendationStats:recStats]; + + return testProduct; +} +#pragma mark BVNotifications Tests -- (void)testNotificationInView{ - +- (void)testNotificationInView { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewNotificationInView:@"StorePushNotification" withId:@"1000" andProductType:ProductTypeStore]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 1, @"There should be 1 event queued."); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; + + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; + + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewNotificationInView:@"StorePushNotification" + withId:@"1000" + andProductType:ProductTypeStore]; + + XCTAssert([[BVAnalyticsManager sharedManager] eventQueue].count == 1, + @"There should be 1 event queued."); + + [[BVAnalyticsManager sharedManager] flushQueue]; + + [self waitForAnalytics]; } -- (void)testNotificationUsedFeature{ - +- (void)testNotificationUsedFeature { #if ANALYTICS_TEST_USING_MOCK_DATA == 1 - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; #endif - - numberOfExpectedImpressionAnalyticsEvents = 1; - numberOfExpectedPageviewAnalyticsEvents = 0; - - [BVNotificationsAnalyticsHelper queueAnalyticEventForReviewUsedFeature:@"ok" withId:@"1000" andProductType:ProductTypeStore]; - - XCTAssert([[BVAnalyticsManager sharedManager]eventQueue].count == 1, @"There should be 1 event queued."); - - [[BVAnalyticsManager sharedManager] flushQueue]; - - [self waitForAnalytics]; -} + numberOfExpectedImpressionAnalyticsEvents = 1; + numberOfExpectedPageviewAnalyticsEvents = 0; -@end + [BVNotificationsAnalyticsHelper + queueAnalyticEventForReviewUsedFeature:@"ok" + withId:@"1000" + andProductType:ProductTypeStore]; + + XCTAssert([[BVAnalyticsManager sharedManager] eventQueue].count == 1, + @"There should be 1 event queued."); + [[BVAnalyticsManager sharedManager] flushQueue]; + [self waitForAnalytics]; +} + +@end diff --git a/Tests/Tests/BVLocationTests.m b/Tests/Tests/BVLocationTests.m index e22f0edb..e88c3554 100644 --- a/Tests/Tests/BVLocationTests.m +++ b/Tests/Tests/BVLocationTests.m @@ -5,115 +5,130 @@ // Copyright 2016 Bazaarvoice Inc. All rights reserved. // -#import #import +#import @interface BVLocationManager (UnitTest) -+(id)sharedManager; -- (void)callbackToDelegates:(SEL)selector withAttributes:(NSDictionary *)attributes; ++ (id)sharedManager; +- (void)callbackToDelegates:(SEL)selector + withAttributes:(NSDictionary *)attributes; @end -@interface ReleasedDelegate : NSObject +@interface ReleasedDelegate : NSObject @end @implementation ReleasedDelegate --(void)didBeginVisit:(BVVisit*)visit { - NSAssert(false, @"This callback should not be performed"); +- (void)didBeginVisit:(BVVisit *)visit { + NSAssert(false, @"This callback should not be performed"); } --(void)didEndVisit:(BVVisit*)visit { - NSAssert(false, @"This callback should not be performed"); +- (void)didEndVisit:(BVVisit *)visit { + NSAssert(false, @"This callback should not be performed"); } @end - -@interface BVLocationTests : XCTestCase +@interface BVLocationTests : XCTestCase @end -@implementation BVLocationTests -{ - NSString *_clientIdKey; - NSString *_clientId; +@implementation BVLocationTests { + NSString *_clientIdKey; + NSString *_clientId; - BOOL _delegateCalledBack; + BOOL _delegateCalledBack; } - (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. - - _clientIdKey = @"clientId"; - _clientId = @"test-classic"; - NSDictionary *configDict = @{_clientIdKey: _clientId}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; + [super setUp]; + // Put setup code here. This method is called before the invocation of each + // test method in the class. + + _clientIdKey = @"clientId"; + _clientId = @"test-classic"; + NSDictionary *configDict = @{_clientIdKey : _clientId}; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelVerbose]; } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; + // Put teardown code here. This method is called after the invocation of each + // test method in the class. + [super tearDown]; } --(void)testEnterOwnPlace{ - [BVLocationManager registerForLocationUpdates:self]; - - _delegateCalledBack = NO; - - NSDictionary *attributes = @{_clientIdKey: _clientId, @"type": @"geofence"}; - [[BVLocationManager sharedManager] callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributes]; - - XCTAssert(_delegateCalledBack, @"Delegate method was not performed"); +- (void)testEnterOwnPlace { + [BVLocationManager registerForLocationUpdates:self]; + + _delegateCalledBack = NO; + + NSDictionary *attributes = @{_clientIdKey : _clientId, @"type" : @"geofence"}; + [[BVLocationManager sharedManager] + callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributes]; + + XCTAssert(_delegateCalledBack, @"Delegate method was not performed"); } --(void)testEnterOtherPlace{ - [BVLocationManager registerForLocationUpdates:self]; - - _delegateCalledBack = NO; - - NSDictionary *attributes = @{_clientIdKey: @"otherClient", @"type": @"geofence"}; - [[BVLocationManager sharedManager] callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributes]; - - XCTAssert(!_delegateCalledBack, @"Delegate method should not have been performed"); +- (void)testEnterOtherPlace { + [BVLocationManager registerForLocationUpdates:self]; + + _delegateCalledBack = NO; + + NSDictionary *attributes = + @{_clientIdKey : @"otherClient", @"type" : @"geofence"}; + [[BVLocationManager sharedManager] + callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributes]; + + XCTAssert(!_delegateCalledBack, + @"Delegate method should not have been performed"); } --(void)testMemoryLeak{ - ReleasedDelegate *delegate = [[ReleasedDelegate alloc]init]; - - [BVLocationManager registerForLocationUpdates:delegate]; - - //BVLocationManager should not strong retain any delegates - delegate = nil; - NSDictionary *attributes = @{_clientIdKey: _clientId, @"type": @"geofence"}; - [[BVLocationManager sharedManager] callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributes]; +- (void)testMemoryLeak { + ReleasedDelegate *delegate = [[ReleasedDelegate alloc] init]; + + [BVLocationManager registerForLocationUpdates:delegate]; + + // BVLocationManager should not strong retain any delegates + delegate = nil; + NSDictionary *attributes = @{_clientIdKey : _clientId, @"type" : @"geofence"}; + [[BVLocationManager sharedManager] + callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributes]; } --(void)testUnregistering { - [BVLocationManager registerForLocationUpdates:self]; - - _delegateCalledBack = NO; - NSDictionary *attributes = @{_clientIdKey: _clientId, @"type": @"geofence"}; - [[BVLocationManager sharedManager] callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributes]; - //still registered should get callback - XCTAssert(_delegateCalledBack, @"Delegate method was not performed"); - - _delegateCalledBack = NO; - [BVLocationManager unregisterForLocationUpdates:self]; - [[BVLocationManager sharedManager] callbackToDelegates:@selector(didBeginVisit:) withAttributes:attributes]; - //was unregistered should not get callback - XCTAssert(!_delegateCalledBack, @"Delegate method should not have been performed"); +- (void)testUnregistering { + [BVLocationManager registerForLocationUpdates:self]; + + _delegateCalledBack = NO; + NSDictionary *attributes = @{_clientIdKey : _clientId, @"type" : @"geofence"}; + [[BVLocationManager sharedManager] + callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributes]; + // still registered should get callback + XCTAssert(_delegateCalledBack, @"Delegate method was not performed"); + + _delegateCalledBack = NO; + [BVLocationManager unregisterForLocationUpdates:self]; + [[BVLocationManager sharedManager] + callbackToDelegates:@selector(didBeginVisit:) + withAttributes:attributes]; + // was unregistered should not get callback + XCTAssert(!_delegateCalledBack, + @"Delegate method should not have been performed"); } --(void)didBeginVisit:(BVVisit*)visit { - _delegateCalledBack = YES; +- (void)didBeginVisit:(BVVisit *)visit { + _delegateCalledBack = YES; } --(void)didEndVisit:(BVVisit*)visit { +- (void)didEndVisit:(BVVisit *)visit { } @end diff --git a/Tests/Tests/BVNotificationConfigTests.m b/Tests/Tests/BVNotificationConfigTests.m index e628267d..818cdd4b 100644 --- a/Tests/Tests/BVNotificationConfigTests.m +++ b/Tests/Tests/BVNotificationConfigTests.m @@ -6,158 +6,216 @@ // #import "BVNotificationConfigTests.h" +#import #import #import -#import -#import "BVStoreNotificationConfigurationLoader+Private.h" #import "BVProductReviewNotificationConfigurationLoader+Private.h" +#import "BVStoreNotificationConfigurationLoader+Private.h" static const NSString *clientId = @"testingtesting"; @implementation BVNotificationConfigTests : BVBaseStubTestCase - (void)setUp { - - [super setUp]; - - NSDictionary *configDict = @{@"apiKeyConversationsStores": @"fakeymcfakersonfakekey", - @"clientId": clientId}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - + [super setUp]; + + NSDictionary *configDict = @{ + @"apiKeyConversationsStores" : @"fakeymcfakersonfakekey", + @"clientId" : clientId + }; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; + // Put teardown code here. This method is called after the invocation of + // each test method in the class. + [super tearDown]; } - -- (void)addStubForS3ResponseForConfigPath:(NSString *)path JSONFileNamed:(NSString *)resultFile{ - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.absoluteString isEqualToString: path]; - } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { +- (void)addStubForS3ResponseForConfigPath:(NSString *)path + JSONFileNamed:(NSString *)resultFile { + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.absoluteString isEqualToString:path]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { // return normal user profile from /users API - return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(resultFile, self.class) - statusCode:200 - headers:@{@"Content-Type":@"application/json;charset=utf-8"}] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; - + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(resultFile, self.class) + statusCode:200 + headers:@{ + @"Content-Type" : + @"application/json;charset=utf-8" + }] responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; } - - (void)testLoadStoreNotificationConfigAPI { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testLoadNotificationConfigAPI"]; - - [self addStubForS3ResponseForConfigPath:[NSString stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/%@/conversations-stores/geofenceConfig.json", @"https://s3.amazonaws.com", @"v1", clientId] JSONFileNamed:@"testNotificationConfig.json"]; - - // Testing private API - [[BVStoreNotificationConfigurationLoader sharedManager] loadStoreNotificationConfiguration:^(BVStoreReviewNotificationProperties * _Nonnull response) { + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testLoadNotificationConfigAPI"]; + + [self addStubForS3ResponseForConfigPath: + [NSString + stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/%@/" + @"conversations-stores/geofenceConfig.json", + @"https://s3.amazonaws.com", @"v1", clientId] + JSONFileNamed:@"testNotificationConfig.json"]; + + // Testing private API + [[BVStoreNotificationConfigurationLoader sharedManager] + loadStoreNotificationConfiguration:^( + BVStoreReviewNotificationProperties *__nonnull response) { // success - BVStoreReviewNotificationProperties *noteProps = [[BVStoreNotificationConfigurationLoader sharedManager] bvStoreReviewNotificationProperties]; - + BVStoreReviewNotificationProperties *noteProps = + [[BVStoreNotificationConfigurationLoader sharedManager] + bvStoreReviewNotificationProperties]; + XCTAssertNotNil(noteProps, @"Config note properties should not be nil"); XCTAssertEqual(noteProps.visitDuration, 5, @"Unexpected visitDuration"); - XCTAssertEqual(noteProps.notificationDelay, 5, @"Unexpected notificationDelay"); - XCTAssertEqual(noteProps.remindMeLaterDuration, 86400, @"Unexpected remindMeLaterDuration"); - - XCTAssertTrue([noteProps.customUrlScheme isEqualToString:@"bvsdkdemo"], @"customUrlScheme failure"); - - XCTAssertTrue([noteProps.reviewPromtDispayText isEqualToString:@"Thank you for visiting Endurance Cycles."], @"fail reviewPromtDispayText"); - XCTAssertTrue([noteProps.reviewPromptSubtitleText isEqualToString:@"How would you describe your experience?"], @"fail reviewPromptSubtitleText"); - XCTAssertTrue([noteProps.reviewPromtNoReview isEqualToString:@"I did not visit this store"], @"fail reviewPromtNoReview"); - XCTAssertTrue([noteProps.reviewPromptYesReview isEqualToString:@"Positive Experience"], @"fail reviewPromptYesReview"); - XCTAssertTrue([noteProps.reviewPromptRemindText isEqualToString:@"Bad Experience"], @"fail reviewPromptRemindText"); - - XCTAssertTrue(noteProps.requestReviewOnAppOpen, @"fail requestReviewOnAppOpen flag"); - XCTAssertTrue(noteProps.notificationsEnabled, @"fail notificationsEnabled flag"); - + XCTAssertEqual(noteProps.notificationDelay, 5, + @"Unexpected notificationDelay"); + XCTAssertEqual(noteProps.remindMeLaterDuration, 86400, + @"Unexpected remindMeLaterDuration"); + + XCTAssertTrue([noteProps.customUrlScheme isEqualToString:@"bvsdkdemo"], + @"customUrlScheme failure"); + + XCTAssertTrue( + [noteProps.reviewPromtDispayText + isEqualToString:@"Thank you for visiting Endurance Cycles."], + @"fail reviewPromtDispayText"); + XCTAssertTrue( + [noteProps.reviewPromptSubtitleText + isEqualToString:@"How would you describe your experience?"], + @"fail reviewPromptSubtitleText"); + XCTAssertTrue([noteProps.reviewPromtNoReview + isEqualToString:@"I did not visit this store"], + @"fail reviewPromtNoReview"); + XCTAssertTrue([noteProps.reviewPromptYesReview + isEqualToString:@"Positive Experience"], + @"fail reviewPromptYesReview"); + XCTAssertTrue([noteProps.reviewPromptRemindText + isEqualToString:@"Bad Experience"], + @"fail reviewPromptRemindText"); + + XCTAssertTrue(noteProps.requestReviewOnAppOpen, + @"fail requestReviewOnAppOpen flag"); + XCTAssertTrue(noteProps.notificationsEnabled, + @"fail notificationsEnabled flag"); + [expectation fulfill]; - } failure:^(NSError * _Nonnull errors) { + } + failure:^(NSError *__nonnull errors) { // fail - XCTFail("testLoadNotificationConfigAPI should not have called failure block"); - }]; + XCTFail("testLoadNotificationConfigAPI should not have called failure " + "block"); + }]; - [self waitForExpectations]; - + [self waitForExpectations]; } - - (void)testLoadPINNotificationConfigAPI { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testLoadNotificationConfigAPI"]; - - [self addStubForS3ResponseForConfigPath:[NSString stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/%@/pin/pinConfig.json", @"https://s3.amazonaws.com", @"v1", clientId] JSONFileNamed:@"testNotificationProductConfig.json"]; - // Testing private API - [[BVProductReviewNotificationConfigurationLoader sharedManager] loadPINConfiguration:^(BVProductReviewNotificationProperties * _Nonnull response) { + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testLoadNotificationConfigAPI"]; + + [self + addStubForS3ResponseForConfigPath: + [NSString stringWithFormat:@"%@/incubator-mobile-apps/sdk/%@/ios/" + @"%@/pin/pinConfig.json", + @"https://s3.amazonaws.com", @"v1", + clientId] + JSONFileNamed:@"testNotificationProductConfig.json"]; + // Testing private API + [[BVProductReviewNotificationConfigurationLoader sharedManager] + loadPINConfiguration:^( + BVProductReviewNotificationProperties *__nonnull response) { // success - BVProductReviewNotificationProperties *noteProps = [[BVProductReviewNotificationConfigurationLoader sharedManager] bvProductReviewNotificationProperties]; - + BVProductReviewNotificationProperties *noteProps = + [[BVProductReviewNotificationConfigurationLoader sharedManager] + bvProductReviewNotificationProperties]; + XCTAssertNotNil(noteProps, @"Config note properties should not be nil"); XCTAssertEqual(noteProps.visitDuration, 5, @"Unexpected visitDuration"); - XCTAssertEqual(noteProps.notificationDelay, 5, @"Unexpected notificationDelay"); - XCTAssertEqual(noteProps.remindMeLaterDuration, 86400, @"Unexpected remindMeLaterDuration"); - - XCTAssertTrue([noteProps.customUrlScheme isEqualToString:@"bvsdkdemo"], @"customUrlScheme failure"); - - XCTAssertTrue([noteProps.reviewPromtDispayText isEqualToString:@"Thank you for your Recent Purchase."], @"fail reviewPromtDispayText"); - XCTAssertTrue([noteProps.reviewPromptSubtitleText isEqualToString:@"Would you like to leave a Review?"], @"fail reviewPromptSubtitleText"); - XCTAssertTrue([noteProps.reviewPromtNoReview isEqualToString:@"No"], @"fail reviewPromtNoReview"); - XCTAssertTrue([noteProps.reviewPromptYesReview isEqualToString:@"Yes"], @"fail reviewPromptYesReview"); - XCTAssertTrue([noteProps.reviewPromptRemindText isEqualToString:@"Later"], @"fail reviewPromptRemindText"); - - XCTAssertTrue(noteProps.requestReviewOnAppOpen, @"fail requestReviewOnAppOpen flag"); - XCTAssertTrue(noteProps.notificationsEnabled, @"fail notificationsEnabled flag"); - + XCTAssertEqual(noteProps.notificationDelay, 5, + @"Unexpected notificationDelay"); + XCTAssertEqual(noteProps.remindMeLaterDuration, 86400, + @"Unexpected remindMeLaterDuration"); + + XCTAssertTrue([noteProps.customUrlScheme isEqualToString:@"bvsdkdemo"], + @"customUrlScheme failure"); + + XCTAssertTrue( + [noteProps.reviewPromtDispayText + isEqualToString:@"Thank you for your Recent Purchase."], + @"fail reviewPromtDispayText"); + XCTAssertTrue([noteProps.reviewPromptSubtitleText + isEqualToString:@"Would you like to leave a Review?"], + @"fail reviewPromptSubtitleText"); + XCTAssertTrue([noteProps.reviewPromtNoReview isEqualToString:@"No"], + @"fail reviewPromtNoReview"); + XCTAssertTrue([noteProps.reviewPromptYesReview isEqualToString:@"Yes"], + @"fail reviewPromptYesReview"); + XCTAssertTrue( + [noteProps.reviewPromptRemindText isEqualToString:@"Later"], + @"fail reviewPromptRemindText"); + + XCTAssertTrue(noteProps.requestReviewOnAppOpen, + @"fail requestReviewOnAppOpen flag"); + XCTAssertTrue(noteProps.notificationsEnabled, + @"fail notificationsEnabled flag"); + [expectation fulfill]; - - } failure:^(NSError * _Nonnull errors) { + + } + failure:^(NSError *__nonnull errors) { // fail - XCTFail("testLoadNotificationConfigAPI should not have called failure block"); - }]; - - [self waitForExpectations]; - + XCTFail("testLoadNotificationConfigAPI should not have called failure " + "block"); + }]; + + [self waitForExpectations]; } /* - (void)testLoadNotificationFailure { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testLoadNotificationFailure"]; - + + __weak XCTestExpectation *expectation = [self +expectationWithDescription:@"testLoadNotificationFailure"]; + // Testing private API [[BVSDKManager sharedManager] loadNotificationConfiguration]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * +NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // Check and see if we have all the notification configuration options - - BVStoreReviewNotificationProperties *noteProps = [BVSDKManager sharedManager].bvStoreReviewNotificationProperties; - + + BVStoreReviewNotificationProperties *noteProps = [BVSDKManager +sharedManager].bvStoreReviewNotificationProperties; + XCTAssertNil(noteProps, @"Config note properties should be nil"); - + [expectation fulfill]; - + }); - - + + [self waitForExpectations]; - - + + } */ -- (void) waitForExpectations{ - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - - }]; +- (void)waitForExpectations { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } + + }]; } @end diff --git a/Tests/Tests/BVPINTests.m b/Tests/Tests/BVPINTests.m index 9d1e9901..9188134a 100644 --- a/Tests/Tests/BVPINTests.m +++ b/Tests/Tests/BVPINTests.m @@ -5,9 +5,9 @@ // Copyright © 2017 Bazaarvoice. All rights reserved. // -#import -#import #import +#import +#import #import "BVBaseStubTestCase.h" @@ -18,112 +18,121 @@ @interface BVPINTests : BVBaseStubTestCase @implementation BVPINTests - (void)setUp { - [super setUp]; - - NSDictionary *configDict = @{@"apiKeyPIN": @"fakekey", - @"clientId": @"iosunittest"}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; - + [super setUp]; + + NSDictionary *configDict = + @{@"apiKeyPIN" : @"fakekey", @"clientId" : @"iosunittest"}; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; } - (void)tearDown { - - [super tearDown]; + [super tearDown]; } - (void)testFetchProductsToReview { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchProductsToReview"]; - - [self addStubWith200ResponseForJSONFileNamed:@"productsToReviewResult.json"]; - - [BVPINRequest getPendingPINs:^(NSArray * _Nonnull pins) { - // great success! - - XCTAssertTrue([pins count] == 3, @"PIN result should size should be 3"); - - BVPIN *pin = [pins objectAtIndex:0]; - - XCTAssertTrue([pin.productPageURL isEqualToString:@"http://www.endurancecycles.com/products/granola-bar-with-honey"]); - XCTAssertEqual([pin.averageRating integerValue], 5); - XCTAssertTrue([pin.name isEqualToString:@"Granola Bar with Honey"]); - XCTAssertTrue([pin.identifier isEqualToString:@"12-bv"]); - XCTAssertTrue([pin.imageUrl isEqualToString:@"http://cdn.shopify.com/s/files/1/0796/3917/files/Energy_bar_1.jpg?13414361435441223830"]); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the + // correct results. + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testFetchProductsToReview"]; + + [self addStubWith200ResponseForJSONFileNamed:@"productsToReviewResult.json"]; + + [BVPINRequest getPendingPINs:^(NSArray *__nonnull pins) { + // great success! + + XCTAssertTrue([pins count] == 3, @"PIN result should size should be 3"); + + BVPIN *pin = [pins objectAtIndex:0]; + + XCTAssertTrue([pin.productPageURL + isEqualToString:@"http://www.endurancecycles.com/products/" + @"granola-bar-with-honey"]); + XCTAssertEqual([pin.averageRating integerValue], 5); + XCTAssertTrue([pin.name isEqualToString:@"Granola Bar with Honey"]); + XCTAssertTrue([pin.identifier isEqualToString:@"12-bv"]); + XCTAssertTrue([pin.imageUrl + isEqualToString:@"http://cdn.shopify.com/s/files/1/0796/3917/files/" + @"Energy_bar_1.jpg?13414361435441223830"]); + + [expectation fulfill]; + } + failure:^(NSError *__nonnull error) { // error XCTFail(@"Error handler should not have been called"); [expectation fulfill]; - - }]; - - [self waitForExpectations]; + + }]; + + [self waitForExpectations]; } - (void)testPINMalformedJSON { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPINMalformedJSON"]; - - [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; - - [BVPINRequest getPendingPINs:^(NSArray * _Nonnull pins) { - - XCTFail(@"Success handler should not have been called"); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the + // correct results. + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPINMalformedJSON"]; + + [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; + + [BVPINRequest getPendingPINs:^(NSArray *__nonnull pins) { + + XCTFail(@"Success handler should not have been called"); + + [expectation fulfill]; + } + failure:^(NSError *__nonnull error) { // error - + XCTAssertEqual(error.code, BV_ERROR_PARSING_FAILED); - + [expectation fulfill]; - - }]; - - [self waitForExpectations]; -} + }]; + + [self waitForExpectations]; +} - (void)testPINEmptyJSON { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testPINEmptyJSON"]; - - [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; - - [BVPINRequest getPendingPINs:^(NSArray * _Nonnull pins) { - // great success! - XCTAssertTrue([pins count] == 0, @"PIN result should size should be 0"); - - [expectation fulfill]; - } failure:^(NSError * _Nonnull error) { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the + // correct results. + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testPINEmptyJSON"]; + + [self addStubWith200ResponseForJSONFileNamed:@"emptyJSON.json"]; + + [BVPINRequest getPendingPINs:^(NSArray *__nonnull pins) { + // great success! + XCTAssertTrue([pins count] == 0, @"PIN result should size should be 0"); + + [expectation fulfill]; + } + failure:^(NSError *__nonnull error) { // error - + XCTFail(@"Error handler should not have been called"); - + [expectation fulfill]; - - }]; - - [self waitForExpectations]; -} + }]; + + [self waitForExpectations]; +} +- (void)waitForExpectations { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } -- (void) waitForExpectations{ - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - - }]; + }]; } @end diff --git a/Tests/Tests/BVProductReviewNotificationConfigurationLoader+Private.h b/Tests/Tests/BVProductReviewNotificationConfigurationLoader+Private.h index 11e8265e..5619b6ef 100644 --- a/Tests/Tests/BVProductReviewNotificationConfigurationLoader+Private.h +++ b/Tests/Tests/BVProductReviewNotificationConfigurationLoader+Private.h @@ -10,7 +10,11 @@ @interface BVProductReviewNotificationConfigurationLoader (Testing) --(void)loadPINConfiguration:(void (^ _Nonnull)(BVProductReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull error))failure; +- (void)loadPINConfiguration: + (nonnull void (^)(BVProductReviewNotificationProperties *__nonnull + response))completion + failure: + (nonnull void (^)(NSError *__nonnull error))failure; @end diff --git a/Tests/Tests/BVRecommendationsTests.m b/Tests/Tests/BVRecommendationsTests.m index 7fd7ca50..598a06bf 100644 --- a/Tests/Tests/BVRecommendationsTests.m +++ b/Tests/Tests/BVRecommendationsTests.m @@ -5,185 +5,212 @@ // Copyright 2015 Bazaarvoice Inc. All rights reserved. // -#import #import #import +#import #import "BVBaseStubTestCase.h" -@interface BVRecommendationsTests : BVBaseStubTestCase{ - +@interface BVRecommendationsTests : BVBaseStubTestCase { } @end @implementation BVRecommendationsTests - (void)setUp { - - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. - - NSDictionary *configDict = @{@"apiKeyShopperAdvertising": @"fakekey", - @"clientId": @"iosunittest"}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; + [super setUp]; + // Put setup code here. This method is called before the invocation of each + // test method in the class. + + NSDictionary *configDict = + @{@"apiKeyShopperAdvertising" : @"fakekey", @"clientId" : @"iosunittest"}; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [[BVSDKManager sharedManager] setLogLevel:BVLogLevelError]; } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; - + // Put teardown code here. This method is called after the invocation of + // each test method in the class. + [super tearDown]; } -// Basic test that fetches the client's IDFA and returns an array of product recommenations. +// Basic test that fetches the client's IDFA and returns an array of product +// recommenations. - (void)testFetchProductRecommendations { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchProductRecommendations"]; - - [self addStubWith200ResponseForJSONFileNamed:@"recommendationsResult.json"]; - - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:10]; - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:^(NSArray * _Nonnull recommendations) { - - XCTAssertTrue([recommendations count] > 0, @"Recommendation result should not be size 0"); + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testFetchProductRecommendations"]; + + [self addStubWith200ResponseForJSONFileNamed:@"recommendationsResult.json"]; + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:10]; + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:^( + NSArray *__nonnull recommendations) { + + XCTAssertTrue([recommendations count] > 0, + @"Recommendation result should not be size 0"); [expectation fulfill]; - - } errorHandler:^(NSError * _Nonnull error) { - + + } + errorHandler:^(NSError *__nonnull error) { + XCTFail(@"Error handler should not have been called"); - + [expectation fulfill]; - - }]; - - [self waitForExpectations]; -} + }]; + + [self waitForExpectations]; +} - (void)testRecommendationsByProductId { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testRecommendationsByProductId"]; - - [self addStubWith200ResponseForJSONFileNamed:@"recommendationsByProductId.json"]; - - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:10 withProductId:@"client/productId1234"]; - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:^(NSArray * _Nonnull recommendations) { - - XCTAssertTrue([recommendations count] > 0, @"Recommendation result should not be size 0"); + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testRecommendationsByProductId"]; + + [self addStubWith200ResponseForJSONFileNamed: + @"recommendationsByProductId.json"]; + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:10 + withProductId:@"client/productId1234"]; + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:^( + NSArray *__nonnull recommendations) { + + XCTAssertTrue([recommendations count] > 0, + @"Recommendation result should not be size 0"); [expectation fulfill]; - - } errorHandler:^(NSError * _Nonnull error) { - + + } + errorHandler:^(NSError *__nonnull error) { + XCTFail(@"Error handler should not have been called"); [expectation fulfill]; - - }]; - - [self waitForExpectations]; - + + }]; + + [self waitForExpectations]; } - (void)testRecommendationsByCategoryId { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testRecommendationsByCategoryId"]; - - [self addStubWith200ResponseForJSONFileNamed:@"recommendationsByCategoryId.json"]; - - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:10 withCategoryId:@"client/categoryId0100"]; - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:^(NSArray * _Nonnull recommendations) { - - XCTAssertTrue([recommendations count] > 0, @"Recommendation result should not be size 0"); + __weak XCTestExpectation *expectation = + [self expectationWithDescription:@"testRecommendationsByCategoryId"]; + + [self addStubWith200ResponseForJSONFileNamed: + @"recommendationsByCategoryId.json"]; + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:10 + withCategoryId:@"client/categoryId0100"]; + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:^( + NSArray *__nonnull recommendations) { + + XCTAssertTrue([recommendations count] > 0, + @"Recommendation result should not be size 0"); [expectation fulfill]; - - } errorHandler:^(NSError * _Nonnull error) { - + + } + errorHandler:^(NSError *__nonnull error) { + XCTFail(@"Error handler should not have been called"); [expectation fulfill]; - - }]; - - [self waitForExpectations]; - -} + }]; + + [self waitForExpectations]; +} // Malformed JSON test - (void)testFetchProductRecommendationsMalformedJSONResponse { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchProductRecommendationsMalformedJSONResponse"]; - - [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; - - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:3]; - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:^(NSArray * _Nonnull recommendations) { - - XCTAssertTrue(NO, @"Success block called in test which should have failed."); + __weak XCTestExpectation *expectation = + [self expectationWithDescription: + @"testFetchProductRecommendationsMalformedJSONResponse"]; + + [self addStubWith200ResponseForJSONFileNamed:@"malformedJSON.json"]; + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:3]; + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:^( + NSArray *__nonnull recommendations) { + + XCTAssertTrue( + NO, @"Success block called in test which should have failed."); [expectation fulfill]; - - } errorHandler:^(NSError * _Nonnull error) { - + + } + errorHandler:^(NSError *__nonnull error) { + XCTAssertNotNil(error, @"Got a nil NSError object"); XCTAssertEqual(error.code, 3840, @"Expected error code -1"); - + [expectation fulfill]; - - }]; - - [self waitForExpectations]; -} + }]; -// values in JSON. Ensures if bad json is sent from server the serializer won't barf + [self waitForExpectations]; +} + +// values in JSON. Ensures if bad json is sent from server the serializer +// won't barf - (void)testFetchProductRecommendationsNullJSON { - - __weak XCTestExpectation *expectation = [self expectationWithDescription:@"testFetchProductRecommendationsNullJSON"]; - - [self addStubWith200ResponseForJSONFileNamed:@"recommendationsNullJSON.json"]; - - BVRecommendationsRequest* request = [[BVRecommendationsRequest alloc] initWithLimit:2]; - BVRecommendationsLoader* loader = [[BVRecommendationsLoader alloc] init]; - [loader loadRequest:request completionHandler:^(NSArray * _Nonnull recommendations) { - - XCTAssertTrue([recommendations count] == 1, @"Recommendation result should size should be 1"); + __weak XCTestExpectation *expectation = [self + expectationWithDescription:@"testFetchProductRecommendationsNullJSON"]; + + [self addStubWith200ResponseForJSONFileNamed:@"recommendationsNullJSON.json"]; + + BVRecommendationsRequest *request = + [[BVRecommendationsRequest alloc] initWithLimit:2]; + BVRecommendationsLoader *loader = [[BVRecommendationsLoader alloc] init]; + [loader loadRequest:request + completionHandler:^( + NSArray *__nonnull recommendations) { + + XCTAssertTrue([recommendations count] == 1, + @"Recommendation result should size should be 1"); [expectation fulfill]; - - } errorHandler:^(NSError * _Nonnull error) { - + + } + errorHandler:^(NSError *__nonnull error) { + XCTFail(@"Error handler should not have been called"); - + [expectation fulfill]; - - }]; - - [self waitForExpectations]; -} + }]; + + [self waitForExpectations]; +} // Test, just ensure BVShopperProfile initializes arrays by default - (void)testPraseEmptyShopperProfile { - - BVShopperProfile *profile = [[BVShopperProfile alloc] initWithDictionary:[NSDictionary dictionary]]; - - XCTAssertTrue(profile.recommendations.count == 0, @"Profile recommenations size was not zero"); - XCTAssertTrue(profile.interests.count == 0, @"Profile interests size was not zero"); - XCTAssertTrue(profile.brands.count == 0, @"Profile brands size was not zero"); - + BVShopperProfile *profile = + [[BVShopperProfile alloc] initWithDictionary:[NSDictionary dictionary]]; + + XCTAssertTrue(profile.recommendations.count == 0, + @"Profile recommenations size was not zero"); + XCTAssertTrue(profile.interests.count == 0, + @"Profile interests size was not zero"); + XCTAssertTrue(profile.brands.count == 0, @"Profile brands size was not zero"); } +- (void)waitForExpectations { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } -- (void) waitForExpectations{ - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - - }]; + }]; } @end diff --git a/Tests/Tests/BVStoreNotificationConfigurationLoader+Private.h b/Tests/Tests/BVStoreNotificationConfigurationLoader+Private.h index 261b7747..968ff757 100644 --- a/Tests/Tests/BVStoreNotificationConfigurationLoader+Private.h +++ b/Tests/Tests/BVStoreNotificationConfigurationLoader+Private.h @@ -10,7 +10,12 @@ @interface BVStoreNotificationConfigurationLoader (Testing) --(void)loadStoreNotificationConfiguration:(void (^ _Nonnull)(BVStoreReviewNotificationProperties * _Nonnull response))completion failure:(void (^ _Nonnull)(NSError * _Nonnull error))failure; +- (void) +loadStoreNotificationConfiguration: + (nonnull void (^)(BVStoreReviewNotificationProperties *__nonnull response)) + completion + failure:(nonnull void (^)(NSError *__nonnull error)) + failure; @end diff --git a/Tests/Tests/BVUserProfileTests.m b/Tests/Tests/BVUserProfileTests.m index a8080a9b..c0267a3a 100644 --- a/Tests/Tests/BVUserProfileTests.m +++ b/Tests/Tests/BVUserProfileTests.m @@ -5,8 +5,8 @@ // Copyright 2015 Bazaarvoice Inc. All rights reserved. // -#import #import +#import // 3rd Party #import @@ -16,126 +16,147 @@ @interface BVUserProfileTests : XCTestCase { - XCTestExpectation *userProfileExpectation; - XCTestExpectation *magpieEventExpectation; - + XCTestExpectation *userProfileExpectation; + XCTestExpectation *magpieEventExpectation; } @end @implementation BVUserProfileTests - (void)setUp { - - [super setUp]; - - // Put setup code here. This method is called before the invocation of each test method in the class. - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(userProfileUpdated:) - name:@"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(magpieEventReceived:) - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; - - // set up the BVAdsSDK with your clientId, and AdsPassKey - BVSDKManager *sdkManager = [BVSDKManager sharedManager]; - NSDictionary *configDict = @{@"apiKeyShopperAdvertising": @"fakekey", - @"clientId": @"iosunittest"}; - [BVSDKManager configureWithConfiguration:configDict configType:BVConfigurationTypeStaging]; - [sdkManager setLogLevel:BVLogLevelError]; + [super setUp]; + + // Put setup code here. This method is called before the invocation of each + // test method in the class. + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(userProfileUpdated:) + name:@"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(magpieEventReceived:) + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; + + // set up the BVAdsSDK with your clientId, and AdsPassKey + BVSDKManager *sdkManager = [BVSDKManager sharedManager]; + NSDictionary *configDict = + @{@"apiKeyShopperAdvertising" : @"fakekey", @"clientId" : @"iosunittest"}; + [BVSDKManager configureWithConfiguration:configDict + configType:BVConfigurationTypeStaging]; + [sdkManager setLogLevel:BVLogLevelError]; } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" - object:nil]; - - [[NSNotificationCenter defaultCenter] removeObserver:self - name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" - object:nil]; - - [OHHTTPStubs removeAllStubs]; + // Put teardown code here. This method is called after the invocation of each + // test method in the class. + [super tearDown]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_PROFILE_UPDATED_COMPLETED" + object:nil]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"BV_INTERNAL_MAGPIE_EVENT_COMPLETED" + object:nil]; + + [OHHTTPStubs removeAllStubs]; } +- (void)magpieEventReceived:(NSNotification *)notification { -- (void)magpieEventReceived:(NSNotification *)notification{ - - XCTAssertNotNil(notification, "Magpie event notification was nil"); - - // notification object comes back with a filled out NSError, which we expect to be nil - - XCTAssertNil(notification.object, @"Notification object should have been nil!"); - - [magpieEventExpectation fulfill]; - + XCTAssertNotNil(notification, "Magpie event notification was nil"); + + // notification object comes back with a filled out NSError, which we expect + // to be nil + + XCTAssertNil(notification.object, + @"Notification object should have been nil!"); + + [magpieEventExpectation fulfill]; } -- (void) waitForExpectations{ - [self waitForExpectationsWithTimeout:30.0 handler:^(NSError *error) { - - if(error) - { - XCTFail(@"Expectation Failed with error: %@", error); - } - - }]; +- (void)waitForExpectations { + [self waitForExpectationsWithTimeout:30.0 + handler:^(NSError *error) { + + if (error) { + XCTFail(@"Expectation Failed with error: %@", + error); + } + + }]; } -// For a typical user profile call, the client will simply pass in the UAS string and be done with it. -// The logic behind the call to BVSDKManager#setUserWithAuthString will handle all requirements for making +// For a typical user profile call, the client will simply pass in the UAS +// string and be done with it. The logic behind the call to +// BVSDKManager#setUserWithAuthString will handle all requirements for making // sure the profile is updated. // The text itself - (void)testSetUserProfile { - - userProfileExpectation = [self expectationWithDescription:@"Expecting user profile network event"]; - magpieEventExpectation = [self expectationWithDescription:@"Expecting magpie profile personalization event"]; - - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host containsString:@"bazaarvoice.com"]; - } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { + + userProfileExpectation = + [self expectationWithDescription:@"Expecting user profile network event"]; + magpieEventExpectation = + [self expectationWithDescription: + @"Expecting magpie profile personalization event"]; + + [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { + return [request.URL.host containsString:@"bazaarvoice.com"]; + } + withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) { // return normal user profile from /users API - return [[OHHTTPStubsResponse responseWithFileAtPath:OHPathForFile(@"userProfile1.json", self.class) - statusCode:200 - headers:@{@"Content-Type":@"application/json"}] - responseTime:OHHTTPStubsDownloadSpeedWifi]; - }]; - - - [[BVSDKManager sharedManager] setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d31323334267573657269643d5265636f6d6d656e646174696f6e7353646b54657374"]; // pre-populated with a small profile interested in "pets", "powersports", "gamefish", and others -- for testing purposes. - - [self waitForExpectations]; + return [[OHHTTPStubsResponse + responseWithFileAtPath:OHPathForFile(@"userProfile1.json", + self.class) + statusCode:200 + headers:@{@"Content-Type" : @"application/json"}] + responseTime:OHHTTPStubsDownloadSpeedWifi]; + }]; + + [[BVSDKManager sharedManager] + setUserWithAuthString:@"0ce436b29697d6bc74f30f724b9b0bb6646174653d3132333" + @"4267573657269643d5265636f6d6d656e646174696f6e7353" + @"646b54657374"]; // pre-populated with a small + // profile interested in "pets", + // "powersports", "gamefish", and + // others -- for testing purposes. + + [self waitForExpectations]; } -// This is the notification callback (for internal testing) that sends the BVAuthenticatedUser object with -// profile info -- (void)userProfileUpdated:(NSNotification *)notification{ - - XCTAssertNotNil(notification, @"Notifcation was nil from user profile fetch"); - - user = (BVAuthenticatedUser *)[notification object]; - XCTAssertNotNil(user, @"User profile is nil after profile fetch"); - NSDictionary *keywords = [user getTargetingKeywords]; - - NSString *brandsKeyWords = [keywords objectForKey:@"brands"]; - NSString *interestsKeyWords = [keywords objectForKey:@"interests"]; - - XCTAssertTrue([brandsKeyWords containsString:@"brand1_MED"], @"Expected brand1_MED in keyword result"); - XCTAssertTrue([brandsKeyWords containsString:@"anotherbrand_MED"], @"Expected anotherbrand_MED in keyword result"); - - XCTAssertTrue([interestsKeyWords containsString:@"womensshoes_LOW"], @"Expected womensshoes_LOW in keyword result"); - XCTAssertTrue([interestsKeyWords containsString:@"uncategorized_MED"], @"Expected uncategorized_MED in keyword result"); - XCTAssertTrue([interestsKeyWords containsString:@"apparelaccessories_HIGH"], @"Expected apparelaccessories_HIGH in keyword result"); - - [userProfileExpectation fulfill]; - -} +// This is the notification callback (for internal testing) that sends the +// BVAuthenticatedUser object with profile info +- (void)userProfileUpdated:(NSNotification *)notification { + + XCTAssertNotNil(notification, @"Notifcation was nil from user profile fetch"); + + user = (BVAuthenticatedUser *)[notification object]; + XCTAssertNotNil(user, @"User profile is nil after profile fetch"); + NSDictionary *keywords = [user getTargetingKeywords]; + NSString *brandsKeyWords = [keywords objectForKey:@"brands"]; + NSString *interestsKeyWords = [keywords objectForKey:@"interests"]; + + XCTAssertTrue([brandsKeyWords containsString:@"brand1_MED"], + @"Expected brand1_MED in keyword result"); + XCTAssertTrue([brandsKeyWords containsString:@"anotherbrand_MED"], + @"Expected anotherbrand_MED in keyword result"); + + XCTAssertTrue([interestsKeyWords containsString:@"womensshoes_LOW"], + @"Expected womensshoes_LOW in keyword result"); + XCTAssertTrue([interestsKeyWords containsString:@"uncategorized_MED"], + @"Expected uncategorized_MED in keyword result"); + XCTAssertTrue([interestsKeyWords containsString:@"apparelaccessories_HIGH"], + @"Expected apparelaccessories_HIGH in keyword result"); + + [userProfileExpectation fulfill]; +} @end