From ec80ada293a4516e873e311e0bc1d993f7a9371a Mon Sep 17 00:00:00 2001
From: Frank Schmitt <frank@apptentive.com>
Date: Mon, 27 Apr 2020 14:45:14 -0700
Subject: [PATCH] Apptentive IOS SDK 5.2.11

---
 .../Apptentive.xcodeproj/project.pbxproj      |  8 +-
 Apptentive/Apptentive/Apptentive.h            |  2 +-
 Apptentive/Apptentive/Info.plist              |  2 +-
 .../ApptentiveSurveyViewController.m          | 59 ++++++-----
 .../ApptentiveSurveyViewModel.h               |  3 +
 .../ApptentiveSurveyViewModel.m               | 98 ++++++++++++++-----
 .../ApptentiveTests/ApptentiveSurveyTests.m   | 55 ++++++++++-
 Apptentive/ApptentiveTests/Info.plist         |  2 +-
 CHANGELOG.md                                  |  6 ++
 Example/Podfile.lock                          |  4 +-
 apptentive-ios.podspec                        |  2 +-
 11 files changed, 183 insertions(+), 58 deletions(-)

diff --git a/Apptentive/Apptentive.xcodeproj/project.pbxproj b/Apptentive/Apptentive.xcodeproj/project.pbxproj
index 2aaefcdab..98e070bfd 100644
--- a/Apptentive/Apptentive.xcodeproj/project.pbxproj
+++ b/Apptentive/Apptentive.xcodeproj/project.pbxproj
@@ -2405,7 +2405,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
@@ -2463,7 +2463,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -2495,7 +2495,7 @@
 				DEFINES_MODULE = YES;
 				DEVELOPMENT_TEAM = 86WML2UN43;
 				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 38;
+				DYLIB_CURRENT_VERSION = 39;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				GCC_PREFIX_HEADER = "Apptentive/Misc/ApptentiveConnect-Prefix.pch";
 				GCC_PREPROCESSOR_DEFINITIONS = "APPTENTIVE_DEBUG=1";
@@ -2515,7 +2515,7 @@
 				DEFINES_MODULE = YES;
 				DEVELOPMENT_TEAM = 86WML2UN43;
 				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 38;
+				DYLIB_CURRENT_VERSION = 39;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				GCC_PREFIX_HEADER = "Apptentive/Misc/ApptentiveConnect-Prefix.pch";
 				INFOPLIST_FILE = Apptentive/Info.plist;
diff --git a/Apptentive/Apptentive/Apptentive.h b/Apptentive/Apptentive/Apptentive.h
index 0603a91c0..1a386c71a 100644
--- a/Apptentive/Apptentive/Apptentive.h
+++ b/Apptentive/Apptentive/Apptentive.h
@@ -20,7 +20,7 @@ FOUNDATION_EXPORT double ApptentiveVersionNumber;
 FOUNDATION_EXPORT const unsigned char ApptentiveVersionString[];
 
 /** The version number of the Apptentive SDK. */
-#define kApptentiveVersionString @"5.2.10"
+#define kApptentiveVersionString @"5.2.11"
 
 /** The version number of the Apptentive API platform. */
 #define kApptentiveAPIVersionString @"9"
diff --git a/Apptentive/Apptentive/Info.plist b/Apptentive/Apptentive/Info.plist
index c344ac0df..189efe76f 100644
--- a/Apptentive/Apptentive/Info.plist
+++ b/Apptentive/Apptentive/Info.plist
@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>5.2.10</string>
+	<string>5.2.11</string>
 	<key>CFBundleVersion</key>
 	<string>$(CURRENT_PROJECT_VERSION)</string>
 	<key>NSPrincipalClass</key>
diff --git a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m
index aaad3ec1e..982783788 100644
--- a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m	
+++ b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewController.m	
@@ -61,6 +61,8 @@ @interface ApptentiveSurveyViewController ()
 @property (assign, nonatomic) CGFloat toolbarInset;
 @property (assign, nonatomic) BOOL keyboardVisible;
 
+@property (assign, nonatomic) BOOL shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation;
+
 @end
 
 
@@ -177,15 +179,28 @@ - (IBAction)submit:(id)sender {
 			HUD.imageView.image = [ApptentiveUtilities imageNamed:@"at_thanks"];
 		}
 	} else {
-		NSIndexPath *firstInvalidQuestionIndex = self.viewModel.firstInvalidAnswerIndexPath;
-		ApptentiveAssertNotNil(firstInvalidQuestionIndex, @"Expected non-nil index");
-		if (firstInvalidQuestionIndex) {
-			[self.collectionView scrollToItemAtIndexPath:firstInvalidQuestionIndex atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
-			UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, [self.viewModel errorMessageAtIndex:firstInvalidQuestionIndex.section]);
+		NSIndexPath *firstInvalidQuestionIndexPath = self.viewModel.firstInvalidAnswerIndexPath;
+		ApptentiveAssertNotNil(firstInvalidQuestionIndexPath, @"Expected non-nil index");
+		if (firstInvalidQuestionIndexPath) {
+			// Defer moving VoiceOver focus to first invalid question until after scrolling completes
+			self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation = YES;
+
+			[self.collectionView scrollToItemAtIndexPath:firstInvalidQuestionIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
 		}
 	}
 }
 
+- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
+	if (self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation) {
+		NSIndexPath *firstInvalidQuestionIndexPath = self.viewModel.firstInvalidAnswerIndexPath;
+
+		UIView *firstInvalidQuestionView = [self.collectionView supplementaryViewForElementKind:UICollectionElementKindSectionHeader atIndexPath:firstInvalidQuestionIndexPath];
+		UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, firstInvalidQuestionView);
+
+		self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation = NO;
+	}
+}
+
 - (IBAction)close:(id)sender {
 	UIViewController *presentingViewController = self.presentingViewController;
 	[self dismissViewControllerAnimated:YES
@@ -238,6 +253,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
 			cell.textView.text = [self.viewModel textOfAnswerAtIndexPath:indexPath];
 			cell.placeholderLabel.attributedText = [self.viewModel placeholderTextOfAnswerAtIndexPath:indexPath];
 			cell.placeholderLabel.hidden = cell.textView.text.length > 0;
+			cell.placeholderLabel.isAccessibilityElement = NO;
 			cell.textView.delegate = self;
 			cell.textView.tag = [self.viewModel textFieldTagForIndexPath:indexPath];
 			cell.textView.accessibilityLabel = cell.placeholderLabel.text;
@@ -254,7 +270,6 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
 			cell.textField.delegate = self;
 			cell.textField.tag = [self.viewModel textFieldTagForIndexPath:indexPath];
 			cell.textField.font = [self.viewModel.styleSheet fontForStyle:ApptentiveTextStyleTextInput];
-			cell.textField.accessibilityLabel = cell.textField.placeholder;
 			cell.textField.textColor = [self.viewModel.styleSheet colorForStyle:ApptentiveTextStyleTextInput];
 
 			return cell;
@@ -263,30 +278,33 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
 		case ATSurveyQuestionTypeSingleSelect:
 		case ATSurveyQuestionTypeMultipleSelect: {
 			NSString *reuseIdentifier, *buttonImageName, *detailText;
-			NSString *accessibilityHintDetails = nil;
+            NSString *accessibilityLabel = nil;
+            NSString *accessibilityHint = nil;
 
 			switch ([self.viewModel typeOfQuestionAtIndex:indexPath.section]) {
 				case ATSurveyQuestionTypeRange:
 					if (indexPath.item == 0) {
 						reuseIdentifier = @"RangeMinimum";
 						detailText = [self.viewModel minimumLabelForQuestionAtIndex:indexPath.section];
-						accessibilityHintDetails = [self.viewModel minimumLabelForQuestionAtIndex:indexPath.section];
 					} else if (indexPath.item == [self.viewModel numberOfAnswersForQuestionAtIndex:indexPath.section] - 1) {
 						reuseIdentifier = @"RangeMaximum";
 						detailText = [self.viewModel maximumLabelForQuestionAtIndex:indexPath.section];
-						accessibilityHintDetails = [self.viewModel maximumLabelForQuestionAtIndex:indexPath.section];
 					} else {
 						reuseIdentifier = @"Range";
 					}
 					buttonImageName = @"at_circle";
+                    accessibilityLabel = [self.viewModel rangeOptionAccessibilityLabelForQuestionAtIndexPath:indexPath];
+                    accessibilityHint = [self.viewModel rangeOptionAccessibilityHintForQuestionAtIndexPath:indexPath];
 					break;
 				case ATSurveyQuestionTypeMultipleSelect:
 					reuseIdentifier = @"Checkbox";
 					buttonImageName = @"at_checkmark";
+                    accessibilityLabel = [self.viewModel textOfChoiceAtIndexPath:indexPath];
 					break;
 				default:
 					reuseIdentifier = @"Radio";
 					buttonImageName = @"at_circle";
+                    accessibilityLabel = [self.viewModel textOfChoiceAtIndexPath:indexPath];
 					break;
 			}
 
@@ -306,17 +324,11 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
 			cell.detailTextLabel.text = detailText;
 			cell.detailTextLabel.font = [self.viewModel.styleSheet fontForStyle:ApptentiveTextStyleSurveyInstructions];
 			cell.detailTextLabel.textColor = [self.viewModel.styleSheet colorForStyle:ApptentiveTextStyleSurveyInstructions];
-
-			if (detailText) {
-				cell.accessibilityHint = detailText;
-			}
-
-			if (accessibilityHintDetails.length > 0) {
-				cell.accessibilityLabel = [NSString stringWithFormat:@"%@, %@", accessibilityHintDetails, [self.viewModel textOfChoiceAtIndexPath:indexPath]];
-			} else {
-				cell.accessibilityLabel = [self.viewModel textOfChoiceAtIndexPath:indexPath];
-			}
+            
+            cell.accessibilityLabel = accessibilityLabel;
+            cell.accessibilityHint = accessibilityHint;
 			cell.accessibilityTraits |= UIAccessibilityTraitButton;
+            
 			cell.button.image = buttonImage;
 			cell.button.highlightedImage = highlightedButtonImage;
 			[cell.button sizeToFit];
@@ -352,12 +364,15 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
 		view.textLabel.text = [self.viewModel textOfQuestionAtIndex:indexPath.section];
 		view.textLabel.font = [self.viewModel.styleSheet fontForStyle:UIFontTextStyleBody];
 		view.textLabel.textColor = [self.viewModel.styleSheet colorForStyle:UIFontTextStyleBody];
-		view.textLabel.accessibilityHint = [self.viewModel accessibilityHintForQuestionAtIndexPath:indexPath];
 		view.instructionsTextLabel.attributedText = [self.viewModel instructionTextOfQuestionAtIndex:indexPath.section];
 		view.instructionsTextLabel.font = [self.viewModel.styleSheet fontForStyle:ApptentiveTextStyleSurveyInstructions];
 
 		view.separatorView.backgroundColor = [self.viewModel.styleSheet colorForStyle:ApptentiveColorSeparator];
 
+        view.isAccessibilityElement = YES;
+        view.accessibilityLabel = [self.viewModel accessibilityLabelForQuestionAtIndexPath:indexPath];
+        view.accessibilityHint = [self.viewModel accessibilityHintForQuestionAtIndexPath:indexPath];
+
 		return view;
 	} else {
 		ApptentiveSurveyQuestionFooterView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"Footer" forIndexPath:indexPath];
@@ -611,7 +626,7 @@ - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRan
 		return NO;
 	}
     
-    if([UIApplication.sharedApplication canOpenURL:URL]) {
+    if ([UIApplication.sharedApplication canOpenURL:URL]) {
         [ApptentiveURLOpener openURL:URL completionHandler: NULL];
     }
     
@@ -652,7 +667,7 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio
 #pragma mark - View model delegate
 
 - (void)viewModelValidationChanged:(ApptentiveSurveyViewModel *)viewModel isValid:(BOOL)valid {
-	[self.collectionViewLayout invalidateLayout];
+	[self.collectionView reloadData];
 
 	[self setToolbarHidden:valid];
 
diff --git a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.h b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.h
index e99ede484..905373194 100644
--- a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.h	
+++ b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.h	
@@ -47,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
 - (NSString *)textOfQuestionAtIndex:(NSInteger)index;
 - (nullable NSAttributedString *)instructionTextOfQuestionAtIndex:(NSInteger)index;
 - (NSAttributedString *)placeholderTextOfAnswerAtIndexPath:(NSIndexPath *)indexPath;
+- (nullable NSString *)rangeOptionAccessibilityLabelForQuestionAtIndexPath:(NSIndexPath *)indexPath;
+- (nullable NSString *)rangeOptionAccessibilityHintForQuestionAtIndexPath:(NSIndexPath *)indexPath;
+- (nullable NSString *)accessibilityLabelForQuestionAtIndexPath:(NSIndexPath *)indexPath;
 - (nullable NSString *)accessibilityHintForQuestionAtIndexPath:(NSIndexPath *)indexPath;
 - (ATSurveyQuestionType)typeOfQuestionAtIndex:(NSInteger)index;
 - (ApptentiveSurveyAnswerType)typeOfAnswerAtIndexPath:(NSIndexPath *)indexPath;
diff --git a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.m b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.m
index 05b286365..944839b7c 100644
--- a/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.m	
+++ b/Apptentive/Apptentive/Surveys/View Controllers/ApptentiveSurveyViewModel.m	
@@ -161,13 +161,59 @@ - (BOOL)answerIsSelectedAtIndexPath:(NSIndexPath *)indexPath {
 	return [self.selectedIndexPaths containsObject:indexPath];
 }
 
-- (nullable NSString *)accessibilityHintForQuestionAtIndexPath:(NSIndexPath *)indexPath {
+- (nullable NSString *)rangeOptionAccessibilityLabelForQuestionAtIndexPath:(NSIndexPath *)indexPath {
+    NSString *result = [self textOfChoiceAtIndexPath:indexPath];
+    
+    return result;
+}
+
+- (nullable NSString *)rangeOptionAccessibilityHintForQuestionAtIndexPath:(NSIndexPath *)indexPath {
+    NSInteger questionIndex = indexPath.section;
+    
+    NSIndexPath *minValueIndexPath = [NSIndexPath indexPathForRow:0 inSection: questionIndex];
+    NSString *minValueLabel = [self textOfChoiceAtIndexPath:minValueIndexPath];
+    NSString *minLabel = [self minimumLabelForQuestionAtIndex:questionIndex];
+    
+    NSInteger numberOfAnswers = [self numberOfAnswersForQuestionAtIndex:questionIndex];
+    NSIndexPath *maxValueIndexPath = [NSIndexPath indexPathForRow:numberOfAnswers - 1 inSection: questionIndex];
+    NSString *maxValueLabel = [self textOfChoiceAtIndexPath:maxValueIndexPath];
+    NSString *maxLabel = [self maximumLabelForQuestionAtIndex:questionIndex];
+    
+    NSString *result = [NSString stringWithFormat:@"where %@ is %@ and %@ is %@", minValueLabel, minLabel, maxValueLabel, maxLabel];
+
+    return result;
+  }
+  
+- (nullable NSString *)accessibilityLabelForQuestionAtIndexPath:(NSIndexPath *)indexPath {
 	ApptentiveSurveyQuestion *question = [self questionAtIndex:indexPath.section];
+
+	NSMutableArray *resultParts = [[NSMutableArray alloc] init];
+
+	if ([self.invalidQuestionIndexes containsIndex:indexPath.section] && question.errorMessage != nil) {
+		[resultParts addObject:question.errorMessage];
+	}
+
+	if (question.value) {
+		[resultParts addObject:question.value];
+	}
+
 	if (question.required) {
-		return ApptentiveLocalizedString(@"required", @"Required answer hint");
+		NSString *requiredHint = self.survey.requiredText ?: ApptentiveLocalizedString(@"required", @"Required answer hint");
+
+		if (requiredHint) {
+			[resultParts addObject:requiredHint];
+		}
 	}
-	
-	return nil;
+
+	NSString *result = [resultParts count] > 0 ? [resultParts componentsJoinedByString:@", "] : nil;
+
+	return result;
+}
+
+- (nullable NSString *)accessibilityHintForQuestionAtIndexPath:(NSIndexPath *)indexPath {
+	ApptentiveSurveyQuestion *question = [self questionAtIndex:indexPath.section];
+
+	return question.instructions;
 }
 
 - (ATSurveyQuestionType)typeOfQuestionAtIndex:(NSInteger)index {
@@ -187,7 +233,9 @@ - (NSString *)maximumLabelForQuestionAtIndex:(NSInteger)index {
 }
 
 - (nullable NSString *)errorMessageAtIndex:(NSInteger)index {
-	return [self questionAtIndex:index].errorMessage;
+	ApptentiveSurveyQuestion *question = [self questionAtIndex:index];
+
+	return question.errorMessage;
 }
 
 - (BOOL)answerIsValidForQuestionAtIndex:(NSInteger)index {
@@ -207,35 +255,35 @@ - (NSInteger)textFieldTagForIndexPath:(NSIndexPath *)indexPath {
 }
 
 -(BOOL)isNonEmptyText:(NSString *)text {
-    NSString *trimmed = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
-    BOOL isValid = [trimmed length] > 0;
-    
-    return isValid;
+	NSString *trimmed = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+	BOOL isValid = [trimmed length] > 0;
+
+	return isValid;
 }
 
 - (nullable NSAttributedString *)termsAndConditionsAttributedText:(TermsAndConditions *)termsAndConditions {
-    NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init];
-    
-    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
-    paragraphStyle.alignment = NSTextAlignmentCenter;
-    
-    NSDictionary *attributes = @{
-        NSParagraphStyleAttributeName: paragraphStyle,
-        NSFontAttributeName: [self.styleSheet fontForStyle:ApptentiveTextStyleSurveyInstructions],
-        NSForegroundColorAttributeName: [self.styleSheet colorForStyle:ApptentiveTextStyleBody],
-    };
-    
-    NSString *bodyText = termsAndConditions.bodyText;
-    BOOL hasBodyText = [self isNonEmptyText:bodyText];
-    
-    if (hasBodyText) {
+	NSMutableAttributedString *result = [[NSMutableAttributedString alloc] init];
+
+	NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
+	paragraphStyle.alignment = NSTextAlignmentCenter;
+
+	NSDictionary *attributes = @{
+		NSParagraphStyleAttributeName: paragraphStyle,
+		NSFontAttributeName: [self.styleSheet fontForStyle:ApptentiveTextStyleSurveyInstructions],
+		NSForegroundColorAttributeName: [self.styleSheet colorForStyle:ApptentiveTextStyleBody],
+	};
+
+	NSString *bodyText = termsAndConditions.bodyText;
+	BOOL hasBodyText = [self isNonEmptyText:bodyText];
+
+	if (hasBodyText) {
         [result appendAttributedString:[[NSAttributedString alloc] initWithString:bodyText attributes:attributes]];
     }
     
     NSURL *linkURL = termsAndConditions.linkURL;
     BOOL hasValidURL = linkURL && [linkURL scheme] && [linkURL host];
     
-    if(hasValidURL) {
+    if (hasValidURL) {
         if (hasBodyText) {
             [result appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n\n" attributes:attributes]];
         }
diff --git a/Apptentive/ApptentiveTests/ApptentiveSurveyTests.m b/Apptentive/ApptentiveTests/ApptentiveSurveyTests.m
index 17e121a21..e3624d6f2 100644
--- a/Apptentive/ApptentiveTests/ApptentiveSurveyTests.m
+++ b/Apptentive/ApptentiveTests/ApptentiveSurveyTests.m
@@ -246,6 +246,59 @@ - (void)testErrorMessages {
 	XCTAssertEqualObjects([self.viewModel errorMessageAtIndex:4], @"You have selected too many or too few answers.");
 }
 
+-(void)testRangeOptionAccessibilityLabel {
+    [self assertRangeOptionAccessibilityLabelForQuestionAtIndexPath:[NSIndexPath indexPathForItem:5 inSection:10] withExpectedResult:@"0"];
+    [self assertRangeOptionAccessibilityLabelForQuestionAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:11] withExpectedResult:@"3"];
+}
+
+-(void)assertRangeOptionAccessibilityLabelForQuestionAtIndexPath:(NSIndexPath *) indexPath withExpectedResult:(NSString *)expectedResult {
+    [self.viewModel selectAnswerAtIndexPath:indexPath];
+    
+    NSString *result = [self.viewModel rangeOptionAccessibilityLabelForQuestionAtIndexPath:indexPath];
+    
+    XCTAssertEqualObjects(result, expectedResult);
+}
+
+-(void)testAccessibilityLabelForQuestionAtIndexPath {
+    [self.viewModel validate:YES];
+    
+    [self assertAccessibilityLabelForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:2] expectedResult:@"Multiselect Optional"];
+    [self assertAccessibilityLabelForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:5] expectedResult:@"Multiselect Required With Limits, Required"];
+    [self assertAccessibilityLabelForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:10] expectedResult:nil];
+    [self assertAccessibilityLabelForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:11] expectedResult:@"Required"];
+    [self assertAccessibilityLabelForQuestionAtIndexPath: [NSIndexPath indexPathForItem:0 inSection:1] expectedResult:@"You have to select one., Multichoice Required, Required"];
+}
+
+-(void) assertAccessibilityLabelForQuestionAtIndexPath:(NSIndexPath*) indexPath expectedResult:(NSString*)expectedResult {
+    NSString *result = [self.viewModel accessibilityLabelForQuestionAtIndexPath:indexPath];
+    
+    XCTAssertEqualObjects(result, expectedResult);
+}
+
+-(void)testRangeOptionAccessibilityHint {
+    [self assertRangeOptionAccessibilityHintForQuestionAtIndexPath:[NSIndexPath indexPathForItem:5 inSection:10] withExpectedResult:@"where -5 is Negative and 5 is Positive"];
+    [self assertRangeOptionAccessibilityHintForQuestionAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:11] withExpectedResult:@"where 0 is Not at all likely and 10 is Extremely likely"];
+}
+
+-(void)assertRangeOptionAccessibilityHintForQuestionAtIndexPath:(NSIndexPath *) indexPath withExpectedResult:(NSString *)expectedResult {
+    [self.viewModel selectAnswerAtIndexPath:indexPath];
+    
+    NSString *result = [self.viewModel rangeOptionAccessibilityHintForQuestionAtIndexPath:indexPath];
+    
+    XCTAssertEqualObjects(result, expectedResult);
+}
+
+-(void)testAccessibilityHintForQuestionAtIndexPath {
+    [self assertAccessibilityHintForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:2] expectedResult:@"select all that apply"];
+    [self assertAccessibilityHintForQuestionAtIndexPath: [NSIndexPath indexPathForItem:2 inSection:10] expectedResult:nil];
+}
+
+-(void) assertAccessibilityHintForQuestionAtIndexPath:(NSIndexPath*) indexPath expectedResult:(NSString*)expectedResult {
+    NSString *result = [self.viewModel accessibilityHintForQuestionAtIndexPath:indexPath];
+    
+    XCTAssertEqualObjects(result, expectedResult);
+}
+
 -(void)testTermsAndConditions {
     NSString *bodyText = @"body";
     NSString *linkText = @"link";
@@ -275,4 +328,4 @@ -(void)assertTermsAndConditonsBodyText:(NSString*)bodyText linkText:(NSString*)l
     XCTAssertEqualObjects([result string], expectedText);
 }
 
-@end
+@end
\ No newline at end of file
diff --git a/Apptentive/ApptentiveTests/Info.plist b/Apptentive/ApptentiveTests/Info.plist
index cb1e171db..036166b11 100644
--- a/Apptentive/ApptentiveTests/Info.plist
+++ b/Apptentive/ApptentiveTests/Info.plist
@@ -15,7 +15,7 @@
 	<key>CFBundlePackageType</key>
 	<string>BNDL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>5.2.10</string>
+	<string>5.2.11</string>
 	<key>CFBundleVersion</key>
 	<string>1</string>
 </dict>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c77617099..fccc20096 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 2020-04-27 - v5.2.11
+
+#### Improvements
+
+* Improve accessibility in surveys for VoiceOver users.
+
 # 2020-04-14 - v5.2.10
 
 #### Improvements
diff --git a/Example/Podfile.lock b/Example/Podfile.lock
index 00eb58260..40ceb5458 100644
--- a/Example/Podfile.lock
+++ b/Example/Podfile.lock
@@ -1,5 +1,5 @@
 PODS:
-  - apptentive-ios (5.2.10)
+  - apptentive-ios (5.2.11)
 
 DEPENDENCIES:
   - apptentive-ios (from `..`)
@@ -9,7 +9,7 @@ EXTERNAL SOURCES:
     :path: ".."
 
 SPEC CHECKSUMS:
-  apptentive-ios: 036d17e11d518bb82c9f59ee952b19323fa1f884
+  apptentive-ios: 307d75a04a6f51097459dc3756ad1146eda072bd
 
 PODFILE CHECKSUM: 89d2b5f4683b04482e89df6d46b268cc9ed1ef79
 
diff --git a/apptentive-ios.podspec b/apptentive-ios.podspec
index 6e1d83e6d..92daf0f02 100644
--- a/apptentive-ios.podspec
+++ b/apptentive-ios.podspec
@@ -1,7 +1,7 @@
 Pod::Spec.new do |s|
   s.name     = 'apptentive-ios'
   s.module_name = 'Apptentive'
-  s.version  = '5.2.10'
+  s.version  = '5.2.11'
   s.license  = 'BSD'
   s.summary  = 'Apptentive Customer Communications SDK.'
   s.homepage = 'https://www.apptentive.com/'