diff --git a/Sources/KIF/Additions/NSError-KIFAdditions.h b/Sources/KIF/Additions/NSError-KIFAdditions.h index 92325609..9a112bb2 100644 --- a/Sources/KIF/Additions/NSError-KIFAdditions.h +++ b/Sources/KIF/Additions/NSError-KIFAdditions.h @@ -13,4 +13,6 @@ + (instancetype)KIFErrorWithUnderlyingError:(NSError *)underlyingError format:(NSString *)format, ... NS_FORMAT_FUNCTION(2,3); + (instancetype)KIFErrorWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); ++ (instancetype)KIFErrorFromException:(NSException *)exception; + @end diff --git a/Sources/KIF/Additions/NSError-KIFAdditions.m b/Sources/KIF/Additions/NSError-KIFAdditions.m index 226309fd..67202012 100644 --- a/Sources/KIF/Additions/NSError-KIFAdditions.m +++ b/Sources/KIF/Additions/NSError-KIFAdditions.m @@ -36,4 +36,14 @@ + (instancetype)KIFErrorWithUnderlyingError:(NSError *)underlyingError format:(N return [self errorWithDomain:@"KIFTest" code:KIFTestStepResultFailure userInfo:userInfo]; } ++ (instancetype)KIFErrorFromException:(NSException *)exception { + NSMutableDictionary * info = [NSMutableDictionary dictionary]; + [info setValue:exception.name forKey:@"ExceptionName"]; + [info setValue:exception.reason forKey:@"ExceptionReason"]; + [info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"]; + [info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"]; + [info setValue:exception.userInfo forKey:@"ExceptionUserInfo"]; + return [self errorWithDomain:@"KIFTest" code:KIFTestStepResultFailure userInfo:info]; +} + @end diff --git a/Sources/KIF/Classes/KIFUIViewTestActor.h b/Sources/KIF/Classes/KIFUIViewTestActor.h index 1df5d02f..c75c73ca 100644 --- a/Sources/KIF/Classes/KIFUIViewTestActor.h +++ b/Sources/KIF/Classes/KIFUIViewTestActor.h @@ -137,18 +137,25 @@ extern NSString *const inputFieldTestString; @abstract Tap a view matching the tester's search predicate. @discussion The tester will evaluate the accessibility hierarchy against it's search predicate and perform a tap on the first match. */ -- (void)tap; +- (void)tap CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tapWithCompletionHandler:(void (^)(NSError*))completionHandler; + /*! @abstract Long Press a view matching the tester's search predicate. @discussion The tester will fist evaluate the accessibility hierarchy against it's search predicate and perform a long press on the first match. */ -- (void)longPress; +- (void)longPress CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)longPressWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Long Press a view matching the tester's search predicate. @discussion The tester will first evaluate the accessibility hierarchy against it's search predicate and perform a long press on the first match. @param duration The duration to hold the long press. */ -- (void)longPressWithDuration:(NSTimeInterval)duration; +- (void)longPressWithDuration:(NSTimeInterval)duration CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)longPressWithDuration:(NSTimeInterval)duration completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Taps the screen at a particular point. @@ -156,14 +163,18 @@ extern NSString *const inputFieldTestString; @param screenPoint The point in screen coordinates to tap. Screen points originate from the top left of the screen. */ -- (void)tapScreenAtPoint:(CGPoint)screenPoint; +- (void)tapScreenAtPoint:(CGPoint)screenPoint CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tapScreenAtPoint:(CGPoint)screenPoint completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Swipe a view matching the tester's search predicate. @discussion The tester will first evaluate the accessibility hierarchy against it's search predicate and perform a swipe in the given direction on the first match @param direction The direction to swipe in. */ -- (void)swipeInDirection:(KIFSwipeDirection)direction; +- (void)swipeInDirection:(KIFSwipeDirection)direction CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)swipeInDirection:(KIFSwipeDirection)direction completionHandler:(void (^)(NSError*))completionHandler; #pragma mark Waiting & Finding @@ -174,7 +185,9 @@ extern NSString *const inputFieldTestString; If the view you want to wait for is tappable, use the -waitToBecomeTappable method instead as it provides a more strict test. @return The found view, if applicable. */ -- (UIView *)waitForView; +- (UIView *)waitForView CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForViewWithCompletionHandler:(void (^)(UIView*, NSError*))completionHandler; /*! @abstract Waits until a view or accessibility element matching the tester's search predicate is present and available for tapping. @@ -182,18 +195,22 @@ extern NSString *const inputFieldTestString; @return The found view, if applicable. */ -- (UIView *)waitForTappableView; +- (UIView *)waitForTappableView CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForTappableViewWithCompletionHandler:(void (^)(UIView*, NSError*))completionHandler; /*! @abstract Waits until a view or accessibility element is no longer present. @discussion The view or accessibility element is searched for in the view hierarchy. If the element is found, then the step will attempt to wait until it isn't. Note that the view does not necessarily have to be visible on the screen, and may be behind another view or offscreen. Views with their hidden property set to YES are considered absent. */ -- (void)waitForAbsenceOfView; +- (void)waitForAbsenceOfView CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForAbsenceOfViewWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Waits until a view or accessibility element matching the tester's search predicate is present and available for tapping. @discussion The view or accessibility elemenr is searched for in the view hierarchy. If the element isn't found or isn't currently tappable, then the step will attempt to wait until it is. Whether or not a view is tappable is based on -[UIView hitTest:]. */ -- (void)waitToBecomeTappable DEPRECATED_MSG_ATTRIBUTE("Use 'waitForTappableView' instead."); +- (void)waitToBecomeTappable DEPRECATED_MSG_ATTRIBUTE("Use 'waitForTappableView' instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); /*! @abstract Waits until a view or accessibility element matching the tester's search predicate is the first responder. @@ -201,26 +218,34 @@ extern NSString *const inputFieldTestString; windows and its accessibility label is compared to the given value. If they match, the step returns success else it will attempt to wait until they do. */ -- (void)waitToBecomeFirstResponder; +- (void)waitToBecomeFirstResponder CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitToBecomeFirstResponderWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Confirms whether a view or accessibility element matching the tester's search predicate is present at the given moment. @discussion The view or accessibility element is searched for in the view hierarchy. If the element isn't found, then the step will not wait and instead immediately return NO. Note that the view does not necessarily have to be visible on the screen, and may be behind another view or offscreen. Views with their hidden property set to YES are ignored. @return a BOOL reflecting whether or not the view was found. */ -- (BOOL)tryFindingView; +- (BOOL)tryFindingView CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tryFindingViewWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler; /*! @abstract Confirms whether a view or accessibility element matching the tester's search predicate is present and tappable at the given moment. @discussion The view or accessibility element is searched for in the view hierarchy. If the element isn't found, then the step will not wait and instead immediately return NO. Note that the view does not necessarily have to be visible on the screen, and may be behind another view or offscreen. Views with their hidden property set to YES are ignored. Whether or not a view is tappable is based on -[UIView hitTest:]. @return a BOOL reflecting whether or not the view was found and tappable. */ -- (BOOL)tryFindingTappableView; +- (BOOL)tryFindingTappableView CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tryFindingTappableViewWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler; /*! @abstract Tries to guess if there are any unfinished animations and waits for a certain amount of time to let them finish. */ -- (void)waitForAnimationsToFinish; +- (void)waitForAnimationsToFinish CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForAnimationsToFinishWithCompletionHandler:(void (^)(NSError*))completionHandler; #pragma mark Scroll Views, Table Views and Collection Views @@ -232,7 +257,9 @@ extern NSString *const inputFieldTestString; @param indexPath Index path of the row to tap. */ -- (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath; +- (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Scrolls a table view matching the tester's search predicate while waiting for the cell at the given indexPath to appear. @@ -243,7 +270,9 @@ extern NSString *const inputFieldTestString; @param indexPath Index path of the cell. @return The table view cell at the given index path. */ -- (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath; +- (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(UITableViewCell*, NSError*))completionHandler; /*! @abstract Scrolls a table view matching the tester's search predicate while waiting for the cell at the given indexPath to appear. @@ -255,7 +284,9 @@ extern NSString *const inputFieldTestString; @param position Table View scroll position to scroll to. Useful for tall cells when the content needed is in a specific location. @return The table view cell at the given index path. */ -- (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath atPosition:(UITableViewScrollPosition)position; +- (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath atPosition:(UITableViewScrollPosition)position CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath atPosition:(UITableViewScrollPosition)position completionHandler:(void (^)(UITableViewCell*, NSError*))completionHandler; /*! @abstract Moves the row at sourceIndexPath to destinationIndexPath in a table view matching the tester's search predicate. @@ -266,7 +297,9 @@ extern NSString *const inputFieldTestString; @param sourceIndexPath Index path of the row to move. @param destinationIndexPath Desired final index path of the row after moving. */ -- (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; +- (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Taps the item at indexPath in a collection view matching the tester's search predicate. @@ -276,7 +309,9 @@ extern NSString *const inputFieldTestString; @param indexPath Index path of the item to tap. */ -- (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Scrolls a collection view while waiting for the cell at the given indexPath to appear. @@ -287,7 +322,9 @@ extern NSString *const inputFieldTestString; @param indexPath Index path of the cell. @return Collection view cell at index path */ -- (UICollectionViewCell *)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *)indexPath; +- (UICollectionViewCell *)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *)indexPath CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(UICollectionViewCell *, NSError*))completionHandler; /*! @abstract Scrolls a particular Scroll View in the view hierarchy by an amount indicated as a fraction of its size. @@ -295,7 +332,9 @@ extern NSString *const inputFieldTestString; @param horizontalFraction The horizontal displacement of the scroll action, as a fraction of the width of the view. @param verticalFraction The vertical displacement of the scroll action, as a fraction of the height of the view. */ -- (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction; +- (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction completionHandler:(void (^)(NSError*))completionHandler; #pragma mark Text Input @@ -304,7 +343,9 @@ extern NSString *const inputFieldTestString; @discussion If the element isn't currently tappable, then the step will attempt to wait until it is. Once the view is present and tappable, a tap event is simulated in the center of the view or element, then text is entered into the view by simulating taps on the appropriate keyboard keys. @param text The text to enter. */ -- (void)enterText:(NSString *)text; +- (void)enterText:(NSString *)text CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)enterText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Enters text into a particular view matching the tester's search predicate, then asserts that the view contains the expected text. @@ -312,41 +353,47 @@ extern NSString *const inputFieldTestString; @param text The text to enter. @param expectedResult What the text value should be after entry completes, including any formatting done by the field. If this is nil, the "text" parameter will be used. */ -- (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult; +- (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Enters text into a the current first responder. @discussion Text is entered into the view by simulating taps on the appropriate keyboard keys if the keyboard is already displayed. Useful to enter text in WKWebViews or components with no accessibility labels. @param text The text to enter. */ -- (void)enterTextIntoCurrentFirstResponder:(NSString *)text DEPRECATED_MSG_ATTRIBUTE("Use 'usingFirstResponder' matcher with 'enterText:' instead."); +- (void)enterTextIntoCurrentFirstResponder:(NSString *)text DEPRECATED_MSG_ATTRIBUTE("Use 'usingFirstResponder' matcher with 'enterText:' instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); /*! @abstract Enters text into a the current first responder. if KIF is unable to type with the keyboard (which could be dismissed or obscured) the tester will call setText on the fallback view directly. @discussion Text is entered into the view by simulating taps on the appropriate keyboard keys if the keyboard is already displayed. Useful to enter text in WKWebViews or components with no accessibility labels. @param text The text to enter. @param fallbackView The UIView to enter if keyboard input fails. */ -- (void)enterTextIntoCurrentFirstResponder:(NSString *)text fallbackView:(UIView *)fallbackView DEPRECATED_MSG_ATTRIBUTE("Please log a KIF Github issue if you have a use case for this."); +- (void)enterTextIntoCurrentFirstResponder:(NSString *)text fallbackView:(UIView *)fallbackView DEPRECATED_MSG_ATTRIBUTE("Please log a KIF Github issue if you have a use case for this.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); /*! @abstract Clears text from a particular view matching the tester's search predicate. @discussion If the element isn't currently tappable, then the step will attempt to wait until it is. Once the view is present and tappable, a tap event is simulated in the center of the view or element, then text is cleared from the view by simulating taps on the backspace key. */ -- (void)clearText; +- (void)clearText CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)clearTextWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Clears text from the current first responder. @discussion text is cleared from the first responder by simulating taps on the backspace key. */ -- (void)clearTextFromFirstResponder DEPRECATED_MSG_ATTRIBUTE("Use 'usingFirstResponder' matcher with 'clearText' instead."); +- (void)clearTextFromFirstResponder DEPRECATED_MSG_ATTRIBUTE("Use 'usingFirstResponder' matcher with 'clearText' instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); /*! @abstract Clears text from a particular view matching the tester's search predicate, then sets new text. @discussion If the element isn't currently tappable, then the step will attempt to wait until it is. Once the view is present and tappable, a tap event is simulated in the center of the view or element, then text is cleared from the view by simulating taps on the backspace key, the new text is then entered by simulating taps on the appropriate keyboard keys. @param text The text to enter after clearing the view. */ -- (void)clearAndEnterText:(NSString *)text; +- (void)clearAndEnterText:(NSString *)text CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)clearAndEnterText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Clears text from a particular view matching the tester's search predicate, sets new text, then asserts that the view contains the expected text. @discussion If the element isn't currently tappable, then the step will attempt to wait until it is. Once the view is present and tappable, a tap event is simulated in the center of the view or element, then text is cleared from the view by simulating taps on the backspace key, the new text is then entered by simulating taps on the appropriate keyboard keys, finally the text of the view is compared against the expected result. @@ -354,37 +401,49 @@ extern NSString *const inputFieldTestString; @param expectedResult What the text value should be after entry completes, including any formatting done by the field. If this is nil, the "text" parameter will be used. */ -- (void)clearAndEnterText:(NSString *)text expectedResult:(NSString *)expectedResult; +- (void)clearAndEnterText:(NSString *)text expectedResult:(NSString *)expectedResult CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)clearAndEnterText:(NSString *)text expectedResult:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Sets text into a particular view matching the tester's search predicate. @discussion The text is set on the view directly with 'setText:'. Does not result in first responder changes. Does not perform expected result validation. @param text The text to set. */ -- (void)setText:(NSString *)text; +- (void)setText:(NSString *)text CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)setText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Validates the text in a field matches the supplied expected value. @discussion Waits until the view is present (up to the standard timeout), and then ensures that it has expected text. @param expectedResult The text to expect the view to contain. */ -- (void)expectToContainText:(NSString *)expectedResult; +- (void)expectToContainText:(NSString *)expectedResult CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)expectToContainText:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Waits for the software keyboard to be visible. @discussion If input is also possible from a hardare keyboard @c waitForKeyInputReady may be more appropriate. */ -- (void)waitForSoftwareKeyboard; +- (void)waitForSoftwareKeyboard CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForSoftwareKeyboardWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract If present, waits for the software keyboard to dismiss. */ -- (void)waitForAbsenceOfSoftwareKeyboard; +- (void)waitForAbsenceOfSoftwareKeyboard CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForAbsenceOfSoftwareKeyboardWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Waits for the keyboard to be ready for input. This tests whether or not a hardware or software keyboard is available and if the keyboard has a responder to send events to. */ -- (void)waitForKeyInputReady; +- (void)waitForKeyInputReady CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)waitForKeyInputReadyWithCompletionHandler:(void (^)(NSError*))completionHandler; #pragma mark Specific Controls @@ -393,40 +452,52 @@ extern NSString *const inputFieldTestString; @discussion Searches for a UISlider matching the tester's search predicate. If the element isn't found or isn't currently tappable, then the step will attempt to wait until it is. Once the view is present, the step will attempt to drag the slider to the new value. The step will fail if it finds a view matching the tester's search predicate that is not a UISlider or if value is outside of the possible values. Because this step simulates drag events, the value reached may not be the exact value requested and the app may ignore the touch events if the movement is less than the drag gesture recognizer's minimum distance. @param value The desired value of the UISlider. */ -- (void)setSliderValue:(float)value; +- (void)setSliderValue:(float)value CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)setSliderValue:(float)value completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Toggles a UISwitch matching the tester's search predicate into a specified position. @discussion If the Switch isn't currently tappable, then the step will attempt to wait until it is. Once the view is present, the step will return if it's already in the desired position. If the switch is tappable but not in the desired position, a tap event is simulated in the center of the view or element, toggling the switch into the desired position. @param switchIsOn The desired position of the UISwitch. */ -- (void)setSwitchOn:(BOOL)switchIsOn; +- (void)setSwitchOn:(BOOL)switchIsOn CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)setSwitchOn:(BOOL)switchIsOn completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Pulls down on the view matching the tester's search predicate to trigger a pull to refresh. @discussion This will enact the pull to refresh by pulling down the distance of 1/2 the height of the view found by the tester's search predicate. */ -- (void)pullToRefresh; +- (void)pullToRefresh CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)pullToRefreshWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Pulls down on the view matching the tester's search predicate then hold for a given duration, then release to trigger a pull to refresh. @discussion This will enact the pull to refresh by pulling down the distance of 1/2 the height of the view found by the tester's search predicate. The view will be held down for the given duration and then released. @param pullDownDuration The enum describing the approximate time for the pull down to travel the entire distance */ -- (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration; +- (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Dismisses a popover on screen. @discussion With a popover up, tap at the top-left corner of the screen. */ -- (void)dismissPopover; +- (void)dismissPopover CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)dismissPopoverWithCompletionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Selects an item from a currently visible picker view. @discussion With a picker view already visible, this step will find an item with the given title, select that item, and tap the Done button. @param title The title of the row to select. */ -- (void)selectPickerViewRowWithTitle:(NSString *)title; +- (void)selectPickerViewRowWithTitle:(NSString *)title CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)selectPickerViewRowWithTitle:(NSString *)title completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Selects an item from a currently visible picker view in specified component. @@ -434,14 +505,18 @@ extern NSString *const inputFieldTestString; @param title The title of the row to select. @param component The component tester inteds to select the title in. */ -- (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component; +- (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Selects a value from an input date picker view. @discussion With a date picker view already visible in the input bar, this step will select the date within the date picker. @param date The date to be selected in the date picker. */ -- (void)selectDatePickerDate:(NSDate *)date; +- (void)selectDatePickerDate:(NSDate *)date CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)selectDatePickerDate:(NSDate *)date completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Selects a value from an input date picker view with the picker type UIDatePickerModeCountDownTimer. @@ -449,7 +524,9 @@ extern NSString *const inputFieldTestString; @param hours The hours in the coundown picker. @param minutes The minutes in the coundown picker. */ -- (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUInteger)minutes; +- (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUInteger)minutes CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUInteger)minutes completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Selects an item from a currently visible date picker view in specified component. This can only be used on UIDatePicker objects and not UIPickerView objects. @@ -457,7 +534,7 @@ extern NSString *const inputFieldTestString; @param title The title of the row to select. @param component The component tester inteds to select the title in. */ -- (void)selectDatePickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead."); +- (void)selectDatePickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); ; /*! @@ -465,7 +542,7 @@ extern NSString *const inputFieldTestString; @discussion With a date picker view already visible, this step will select the different rotating wheel values in order of how the array parameter is passed in. After it is done it will hide the date picker. It works with all 4 UIDatePickerMode* modes. The input parameter of type NSArray has to match in what order the date picker is displaying the values/columns. So if the locale is changing the input parameter has to be adjusted. Example: Mode: UIDatePickerModeDate, Locale: en_US, Input param: NSArray *date = @[@"June", @"17", @"1965"];. Example: Mode: UIDatePickerModeDate, Locale: de_DE, Input param: NSArray *date = @[@"17.", @"Juni", @"1965". @param datePickerColumnValues Each element in the NSArray represents a rotating wheel in the date picker control. Elements from 0 - n are listed in the order of the rotating wheels, left to right. */ -- (void)selectDatePickerValue:(NSArray *)datePickerColumnValues NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead."); +- (void)selectDatePickerValue:(NSArray *)datePickerColumnValues NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); ; /*! @@ -474,7 +551,7 @@ extern NSString *const inputFieldTestString; @param datePickerColumnValues Each element in the NSArray represents a rotating wheel in the date picker control. Elements from 0 - n are listed in the order of the rotating wheels, left to right. @param searchOrder The order in which the values are being searched for selection in each compotent. */ -- (void)selectDatePickerValue:(NSArray *)datePickerColumnValues withSearchOrder:(KIFPickerSearchOrder)searchOrder NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead."); +- (void)selectDatePickerValue:(NSArray *)datePickerColumnValues withSearchOrder:(KIFPickerSearchOrder)searchOrder NS_DEPRECATED_IOS(10_0, 13_4,"Use -[viewTester selectDatePickerWithDate:] or -[viewTester selectCountdownDatePickerWithHours:] instead.") CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); ; /*! @@ -484,19 +561,25 @@ extern NSString *const inputFieldTestString; @param row The row number in the album for the desired photo. (1-indexed) @param column The column number in the album for the desired photo. */ -- (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NSInteger)column; +- (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NSInteger)column CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NSInteger)column completionHandler:(void (^)(NSError*))completionHandler; /*! @abstract Taps the status bar at the top of the screen. This will fail if a status bar is not found. */ -- (void)tapStatusBar; +- (void)tapStatusBar CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)tapStatusBarWithCompletionHandler:(void (^)(NSError*))completionHandler; #if TARGET_IPHONE_SIMULATOR /*! @abstract If present, dismisses a system alert with the last button, usually 'Allow'. Returns YES if a dialog was dismissed, NO otherwise. @discussion Use this to dissmiss a location services authorization dialog or a photos access dialog by tapping the 'Allow' button. No action is taken if no alert is present. */ -- (BOOL)acknowledgeSystemAlert; +- (BOOL)acknowledgeSystemAlert CF_SWIFT_UNAVAILABLE_FROM_ASYNC("use async version"); + +- (void)acknowledgeSystemAlertWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler; #endif diff --git a/Sources/KIF/Classes/KIFUIViewTestActor.m b/Sources/KIF/Classes/KIFUIViewTestActor.m index 012e8691..33c24008 100644 --- a/Sources/KIF/Classes/KIFUIViewTestActor.m +++ b/Sources/KIF/Classes/KIFUIViewTestActor.m @@ -12,6 +12,7 @@ #import "KIFTestActor_Private.h" #import "KIFUIObject.h" #import "KIFUITestActor_Private.h" +#import "NSError-KIFAdditions.h" #import "NSPredicate+KIFAdditions.h" #import "NSString+KIFAdditions.h" #import "UIAccessibilityElement-KIFAdditions.h" @@ -162,6 +163,16 @@ - (BOOL)acknowledgeSystemAlert; { return [self.actor acknowledgeSystemAlert]; } + +- (void)acknowledgeSystemAlertWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler { + @try { + BOOL result = [self acknowledgeSystemAlert]; + completionHandler(result, nil); + } @catch (NSException *exception) { + completionHandler(false, [NSError KIFErrorFromException: exception]); + } +} + #endif - (void)tapStatusBar; @@ -169,11 +180,23 @@ - (void)tapStatusBar; [self.actor tapStatusBar]; } +- (void)tapStatusBarWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self tapStatusBar]; + } completionHandler:completionHandler]; +} + - (void)dismissPopover; { [self.actor dismissPopover]; } +- (void)dismissPopoverWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self dismissPopover]; + } completionHandler:completionHandler]; +} + #pragma mark - Waiting - (UIView *)waitForView; @@ -181,6 +204,13 @@ - (UIView *)waitForView; return [self _predicateSearchWithRequiresMatch:YES mustBeTappable:NO].view; } +- (void)waitForViewWithCompletionHandler:(void (^)(UIView*, NSError* error))completionHandler +{ + [self safeAsyncCallWithReturnValue:^{ + return [self waitForView]; + } completionHandler:completionHandler]; +} + - (void)waitForAbsenceOfView; { [self runBlock:^KIFTestStepResult(NSError **error) { @@ -203,11 +233,23 @@ - (void)waitForAbsenceOfView; }]; } +- (void)waitForAbsenceOfViewWithCompletionHandler:(void (^ __nullable)(NSError* error))completionHandler { + [self safeAsyncCall:^{ + [self waitForAbsenceOfView];; + } completionHandler:completionHandler]; +} + - (UIView *)waitForTappableView; { return [self _predicateSearchWithRequiresMatch:YES mustBeTappable:YES].view; } +- (void)waitForTappableViewWithCompletionHandler:(void (^)(UIView*, NSError*))completionHandler { + [self safeAsyncCallWithReturnValue:^{ + return [self waitForTappableView]; + } completionHandler:completionHandler]; +} + - (void)waitToBecomeTappable; { [self _predicateSearchWithRequiresMatch:YES mustBeTappable:YES]; @@ -231,26 +273,58 @@ - (void)waitToBecomeFirstResponder; }]; } +- (void)waitToBecomeFirstResponderWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self waitToBecomeFirstResponder]; + } completionHandler:completionHandler]; +} + - (void)waitForAnimationsToFinish; { [self.actor waitForAnimationsToFinishWithTimeout:self.animationWaitingTimeout stabilizationTime:self.animationStabilizationTimeout]; } +- (void)waitForAnimationsToFinishWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self waitForAnimationsToFinish]; + } completionHandler:completionHandler]; +} + #pragma mark Typist Waiting - (void)waitForSoftwareKeyboard; { [self.actor waitForSoftwareKeyboard]; } + +- (void)waitForSoftwareKeyboardWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self waitForSoftwareKeyboard]; + } completionHandler:completionHandler]; +} + - (void)waitForAbsenceOfSoftwareKeyboard; { [self.actor waitForAbsenceOfSoftwareKeyboard]; } + +- (void)waitForAbsenceOfSoftwareKeyboardWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self waitForAbsenceOfSoftwareKeyboard]; + } completionHandler:completionHandler]; +} + - (void)waitForKeyInputReady; { [self.actor waitForKeyInputReady]; } +- (void)waitForKeyInputReadyWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self waitForKeyInputReady]; + } completionHandler:completionHandler]; +} + #pragma mark - Conditionals - (BOOL)tryFindingView; @@ -258,11 +332,28 @@ - (BOOL)tryFindingView; return ([self _predicateSearchWithRequiresMatch:NO mustBeTappable:NO] != nil); } +- (void)tryFindingViewWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler { + @try { + BOOL result = [self tryFindingView]; + completionHandler(result, nil); + } @catch (NSException *exception) { + completionHandler(false, [NSError KIFErrorFromException: exception]); + } +} + - (BOOL)tryFindingTappableView; { return ([self _predicateSearchWithRequiresMatch:NO mustBeTappable:YES] != nil); } +- (void)tryFindingTappableViewWithCompletionHandler:(void (^)(BOOL, NSError*))completionHandler { + @try { + BOOL result = [self tryFindingTappableView]; + completionHandler(result, nil); + } @catch (NSException *exception) { + completionHandler(false, [NSError KIFErrorFromException: exception]); + } +} #pragma mark - Tap Actions @@ -274,11 +365,25 @@ - (void)tap; } } + + +-(void)tapWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self tap];; + } completionHandler:completionHandler]; +} + - (void)longPress; { [self longPressWithDuration:.5]; } +- (void)longPressWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self longPress]; + } completionHandler:completionHandler]; +} + - (void)longPressWithDuration:(NSTimeInterval)duration; { @autoreleasepool { @@ -287,6 +392,12 @@ - (void)longPressWithDuration:(NSTimeInterval)duration; } } +- (void)longPressWithDuration:(NSTimeInterval)duration completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self longPressWithDuration:duration]; + } completionHandler:completionHandler]; +} + #pragma mark - Text Actions; - (void)clearText; @@ -297,6 +408,12 @@ - (void)clearText; } } +- (void)clearTextWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self clearText]; + } completionHandler:completionHandler]; +} + - (void)clearTextFromFirstResponder; { [self.actor clearTextFromFirstResponder]; @@ -307,6 +424,12 @@ - (void)enterText:(NSString *)text; [self enterText:text expectedResult:nil]; } +- (void)enterText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self enterText:text]; + } completionHandler:completionHandler]; +} + - (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult; { if (!self.validateEnteredText && expectedResult) { @@ -319,17 +442,35 @@ - (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult; } } +- (void)enterText:(NSString *)text expectedResult:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self enterText:text expectedResult:expectedResult]; + } completionHandler:completionHandler]; +} + - (void)clearAndEnterText:(NSString *)text; { [self clearAndEnterText:text expectedResult:nil]; } +- (void)clearAndEnterText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self clearAndEnterText:text]; + } completionHandler:completionHandler]; +} + - (void)clearAndEnterText:(NSString *)text expectedResult:(NSString *)expectedResult; { [self clearText]; [self enterText:text expectedResult:expectedResult]; } +- (void)clearAndEnterText:(NSString *)text expectedResult:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self clearAndEnterText:text expectedResult:expectedResult]; + } completionHandler:completionHandler]; +} + - (void)enterTextIntoCurrentFirstResponder:(NSString *)text; { [self.actor enterTextIntoCurrentFirstResponder:text]; @@ -357,6 +498,12 @@ - (void)setText:(NSString *)text; }]; } +- (void)setText:(NSString *)text completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self setText:text]; + } completionHandler:completionHandler]; +} + - (void)expectToContainText:(NSString *)expectedResult; { @autoreleasepool { @@ -365,6 +512,12 @@ - (void)expectToContainText:(NSString *)expectedResult; } } +- (void)expectToContainText:(NSString *)expectedResult completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self expectToContainText:expectedResult]; + } completionHandler:completionHandler]; +} + #pragma mark - Touch Actions - (void)tapScreenAtPoint:(CGPoint)screenPoint; @@ -372,6 +525,12 @@ - (void)tapScreenAtPoint:(CGPoint)screenPoint; [self.actor tapScreenAtPoint:screenPoint]; } +- (void)tapScreenAtPoint:(CGPoint)screenPoint completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self.actor tapScreenAtPoint:screenPoint]; + } completionHandler:completionHandler]; +} + - (void)swipeInDirection:(KIFSwipeDirection)direction; { @autoreleasepool { @@ -380,6 +539,12 @@ - (void)swipeInDirection:(KIFSwipeDirection)direction; } } +- (void)swipeInDirection:(KIFSwipeDirection)direction completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self swipeInDirection:direction]; + } completionHandler:completionHandler]; +} + #pragma mark - Scroll/Table/CollectionView Actions - (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction; @@ -390,6 +555,12 @@ - (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(C } } +- (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self scrollByFractionOfSizeHorizontal:horizontalFraction vertical:verticalFraction]; + } completionHandler:completionHandler]; +} + - (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath; { @autoreleasepool { @@ -398,11 +569,28 @@ - (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath; } } +- (void)tapRowInTableViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self tapRowInTableViewAtIndexPath:indexPath]; + } completionHandler:completionHandler]; +} + - (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath; { return [self waitForCellInTableViewAtIndexPath:indexPath atPosition:UITableViewScrollPositionMiddle]; } +- (void)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(UITableViewCell*, NSError*))completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + UITableViewCell* cell = [self waitForCellInTableViewAtIndexPath:indexPath]; + completionHandler(cell, nil); + } @catch (NSException *exception) { + completionHandler(nil, [NSError KIFErrorFromException: exception]); + } + }); +} + - (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath atPosition:(UITableViewScrollPosition)position; { @autoreleasepool { @@ -411,6 +599,17 @@ - (UITableViewCell *)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath } } +- (void)waitForCellInTableViewAtIndexPath:(NSIndexPath *)indexPath atPosition:(UITableViewScrollPosition)position completionHandler:(void (^)(UITableViewCell*, NSError*))completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + UITableViewCell* cell = [self waitForCellInTableViewAtIndexPath:indexPath atPosition:position]; + completionHandler(cell, nil); + } @catch (NSException *exception) { + completionHandler(nil, [NSError KIFErrorFromException: exception]); + } + }); +} + - (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; { @autoreleasepool { @@ -419,6 +618,12 @@ - (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath } } +- (void)moveRowInTableViewAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self moveRowInTableViewAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + } completionHandler:completionHandler]; +} + - (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath; { @@ -428,6 +633,12 @@ - (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath; } } +- (void)tapCollectionViewItemAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self tapCollectionViewItemAtIndexPath:indexPath]; + } completionHandler:completionHandler]; +} + - (UICollectionViewCell *)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *)indexPath; { @autoreleasepool { @@ -436,6 +647,16 @@ - (UICollectionViewCell *)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *) } } +- (void)waitForCellInCollectionViewAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(UICollectionViewCell*, NSError*))completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + UICollectionViewCell* view = [self waitForCellInCollectionViewAtIndexPath:indexPath]; + completionHandler(view, nil); + } @catch (NSException *exception) { + completionHandler(nil, [NSError KIFErrorFromException: exception]); + } + }); +} #pragma mark - UIControl Actions @@ -447,6 +668,12 @@ - (void)setSliderValue:(float)value; } } +- (void)setSliderValue:(float)value completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self setSliderValue:value]; + } completionHandler:completionHandler]; +} + - (void)setSwitchOn:(BOOL)switchIsOn; { @autoreleasepool { @@ -455,6 +682,12 @@ - (void)setSwitchOn:(BOOL)switchIsOn; } } +- (void)setSwitchOn:(BOOL)switchIsOn completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self setSwitchOn:switchIsOn]; + } completionHandler:completionHandler]; +} + #pragma mark - Picker Actions - (void)selectPickerViewRowWithTitle:(NSString *)title; @@ -462,6 +695,12 @@ - (void)selectPickerViewRowWithTitle:(NSString *)title; [self selectPickerViewRowWithTitle:title inComponent:0]; } +- (void)selectPickerViewRowWithTitle:(NSString *)title completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self selectPickerViewRowWithTitle:title]; + } completionHandler:completionHandler]; +} + - (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component; { @autoreleasepool { @@ -471,6 +710,12 @@ - (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)co } } +- (void)selectPickerViewRowWithTitle:(NSString *)title inComponent:(NSInteger)component completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self selectPickerViewRowWithTitle:title inComponent:component]; + } completionHandler:completionHandler]; +} + #pragma mark - Date Picker Actions - (void)selectDatePickerDate:(NSDate *)date @@ -481,6 +726,12 @@ - (void)selectDatePickerDate:(NSDate *)date } } +- (void)selectDatePickerDate:(NSDate *)date completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self selectDatePickerDate:date]; + } completionHandler:completionHandler]; +} + - (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUInteger)minutes { @autoreleasepool { @@ -489,6 +740,12 @@ - (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUIntege } } +- (void)selectCountdownTimerDatePickerHours:(NSUInteger)hours minutes:(NSUInteger)minutes completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self selectCountdownTimerDatePickerHours:hours minutes:minutes]; + } completionHandler:completionHandler]; +} + #pragma mark - Deprecated Date Picker Actions @@ -521,6 +778,12 @@ - (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NS [self.actor choosePhotoInAlbum:albumName atRow:row column:column]; } +- (void)choosePhotoInAlbum:(NSString *)albumName atRow:(NSInteger)row column:(NSInteger)column completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self choosePhotoInAlbum:albumName atRow:row column:column]; + } completionHandler:completionHandler]; +} + #pragma mark - Pull to Refresh - (void)pullToRefresh; @@ -531,6 +794,12 @@ - (void)pullToRefresh; } } +- (void)pullToRefreshWithCompletionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self pullToRefresh]; + } completionHandler:completionHandler]; +} + - (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration; { @autoreleasepool { @@ -539,6 +808,12 @@ - (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration; } } +- (void)pullToRefreshWithDuration:(KIFPullToRefreshTiming)pullDownDuration completionHandler:(void (^)(NSError*))completionHandler { + [self safeAsyncCall:^{ + [self pullToRefreshWithDuration:pullDownDuration]; + } completionHandler:completionHandler]; +} + #pragma mark - Getters - (UIView *)view; @@ -617,4 +892,28 @@ - (KIFUIObject *)_predicateSearchWithRequiresMatch:(BOOL)requiresMatch mustBeTap return nil; } +-(void)safeAsyncCall:(void (^)(void))call completionHandler:(void (^)(NSError*))completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + call(); + completionHandler(nil); + } @catch (NSException *exception) { + completionHandler([NSError KIFErrorFromException: exception]); + } + }); +} + +-(void)safeAsyncCallWithReturnValue:(UIView* (^)(void))call completionHandler:(void (^)(UIView*, NSError*))completionHandler { + dispatch_async(dispatch_get_main_queue(), ^{ + @try { + UIView* view = call(); + completionHandler(view, nil); + } @catch (NSException *exception) { + completionHandler(nil, [NSError KIFErrorFromException: exception]); + } + }); +} + @end + +