XCUITest using adjustToPickerWheelValue: && viewForRow: delegate results in assertion - objective-c

I ran into a situation where I could not get an XCUITest for a view controller containing a UIPickerView to work without an assertion being generated. The problem causing the assertion is clearly from a spurious "1 of n" being appended to each of the picker values by the accessibility functionality used by XCUITest.
Something as simple as this one line would assert during the test
[app.pickers.pickerWheels.element adjustToPickerWheelValue:#"ANYVALUE"];
This turns out to be a known bug with Xcode, and there is an open radar Cannot use UI Testing to adjust pickerView which uses a view-based delegate
This was my problem exactly as I use the viewForRow: delegate in order to use attributed strings in the picker values. The alternative, using the titleForRow: delegate, does NOT exhibit the problem.
I have tried to work around this by manually setting accessibility information, but to no avail.
And I cannot consider using the titleForRow: delegate, due to my clients requirements for the attributed text.
Yet the dilemma, to also deliver complete XCUITests for all views as another client requirement.
What viable workarounds are there?

This is a known bug in Xcode, and it has been opened for quite some time now, going back to at least this 2015 Apple Developer Forum Post
No fix coming obviously.
My solution to meet all client requirements was to create an additional build configuration called "Testing", which was duplicated from "Debug", so that I could define a new preprocessor macro XCUITEST. Then editing the scheme under Test to use this new build configuration, instead of either DEBUG or RELEASE.
With this change I could incorporate the delegate that works for XC testing and yet keep the one that meets the client app requirements like so:
#ifndef XCUITEST // for release or debug we use this method as we want attributed text strings
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
// blah blah
}
#endif
#ifdef XCUITEST // for testing we have to use this method so XCUITests will work with adjustToPickerWheelValue:
- (UIView *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
// blah blah
}
#endif

Related

How to specify accessibility identifiers for multiple UIPickerViews during XCUITest

When developing Xcode UI testcases for a view controller with multiple UIPickerViews I ran into several bugs preventing success all relating to being able to uniquely identify the pickers within XCUITest.
What "should" work is to simply set the accessibility identifier, or accessibility label, from within storyboard like so:
But this does not work at all for a UIPickerView, though I verified the accessibilityLabel and accessibilityIdentifier properties are set for the UIPickerView. And yes, I tried it with one or the other or both set. I even tried programmatically setting one or the other or both. The below lines within an XCUITest case fail to locate the picker regardless:
XCUIElement *shippingMethodPicker = app.pickerWheels[#"Shipping method"];
[shippingMethodPicker adjustToPickerWheelValue:#"USPS Media Mail"];
It would seem that this is a known issue, and that the solution would be to make the view controller also a UIPickerViewAccessibilityDelegate, and implement the - (NSString *)pickerView:(UIPickerView *)pickerView accessibilityLabelForComponent:(NSInteger)component delegate method.
The Apple API Documentation would seem to describe exactly what we need to uniquely apply an accessibility label to each pickerWheels component.
But this is also bugged, the pickerView parameter is not actually a UIPickerView *, as referenced in this stackoverflow link Unable to get pickerView.tag in -pickerView:accessibilityLabelForComponent: method
Due to the implementation defect with the delegate method, you cannot determine which UIPickerView the delegate is being called for rendering it useless for a view with more than one picker.
With the storyboard approach bugged, and the accessibility delegate also bugged, I could not locate a way to uniquely identify two or more UIPickerViews in a view controller from within a XCUITest testcase.
Anyone have a solution?
Focusing on the previous stackoverflow link comments, I determined a solution that worked for my requirements.
From the debugger we can confirm the previous links comment observation:
The accessibility delegate method,
- (NSString *)pickerView:(UIPickerView *)pickerView accessibilityLabelForComponent:(NSInteger)component;
is quite bugged and does not have a UIPickerView *, but rather a private class UIAccessibilityPickerComponent *. As we can note in the debugger, there is an actual UIPickerView * at the private _picker property of this private class.
Radar opened.
Well this is an internal test problem, it's not something we would ship in the app for the App Store. So we CAN use private interfaces to get around this problem. We will only compile this when we are performing UI testing.
First, create a new build configuration in Xcode that you would only use for Testing, duplicated from Debug. Within that create a new preprocessor define -DXCUITEST and be sure to set this new build config in your scheme for Test.
Then implement the accessibility delegate as follows:
#pragma mark - UIPickerViewAccessibilityDelegate
#ifdef XCUITEST
- (NSString *)pickerView:(UIPickerView *)pickerView accessibilityLabelForComponent:(NSInteger)component {
NSString *label;
UIPickerView *realPickerView;
Ivar picker;
// we are going to work around a bug where the pickerView on this delegate is the wrong class by
// pulling the UIPickerView * that we need from the private property of the UIAccessibilityPickerComponent class
picker = class_getInstanceVariable([NSClassFromString(#"UIAccessibilityPickerComponent") class], "_picker");
// check if the bug still exists and apply workaround only if necessary
if (![pickerView isKindOfClass:[UIPickerView class]])
realPickerView = object_getIvar(pickerView, picker);
else
realPickerView = pickerView;
if (realPickerView == self.shippingMethod)
label = #"Shipping method";
else if (realPickerView == self.someOtherPicker)
label = #"SomeOtherPicker";
return label;
}
#endif
With this workaround the XCUITest testcases finally executed as expected, successfully testing situations of two and even three UIPickerViews on a single view all uniquely identified. Note in my case these were single wheel pickers, if you wanted to solve the problem for multi-wheel pickers, then implement the component logic in the delegate, which is not bugged and works as expected.
Also, don't forget to add this header to the top of your view controller class file:
#ifdef XCUITEST
#import <objc/runtime.h>
#endif

iOS7 Strange behavior of UIPickerView with an UIImage as custom view, any official info?

I was following a tutorial on how to build a custom picker with an UIPickerView in Sams Teach Yourself IOS6 Application Development in 24 hours and I've noticed that simply returning an UIImage in (UIView*)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view method of the UIPickerViewDelegate does not work properly: the images do not appear correctly (they rotate on the spinning wheel but for example they disappear when they should move over the current selection's row). I've found a solution at this link on Stack Overflow and it effectively works, but I'd like to know if this strange behavior appeared in IOS 7 is due to a new way of using UIPickerView or if it is more a sort of bug.. I did not find anything useful in the official API Reference docs. Is there any official doc that is talking about the change in how we must return an UIImageView to be used in a UIPickerView
It looks like the answer is 'No'. I also looked through the Release Notes for 7.0 & 7.1 and looked at the differences in the references for UIPickerView, UIPickerViewDelegate, and UIView (among others) and couldn't find anything do explain why it behaves this way now and I couldn't find any changes to UIPickerView or anything it inherits from or conforms to that would explain it. (If there is something there that explains it then it was not immediately obvious to me.)
As far as I know, those are the only places that would have any official information on this.
I think the only way to know for sure whether this is deliberate or a bug is to report it.

How do you integrate a Leap Motion Controller into a Mac application?

I'm trying to start my own Leap Motion project on the Mac, but I'm having some problems.
I'd like to use Objective-C for this project, and I have read that there is a Leap Motion library for this language. However, I'm not sure how to integrate Leap Motion controls using this library into a Mac application.
Something similar was asked here, only they were asking using the Python Leap Motion library.
How can you add Leap Motion controls to an Objective-C Mac application?
I did this recently, so I can provide the steps that I used to add Leap Motion controls to a Mac application. In fact, the source code to my Leap-enabled Molecules application is available on GitHub if you want an example to work from. All you need to build it is the Leap SDK.
The Leap Motion Objective-C headers are basically wrappers around their underlying C++ API, but you don't need to care about that because you can access this via only Objective-C.
To add the library to your project, first install the Leap SDK somewhere in your system. From there, add references to the Leap.h, LeapMath.h, LeapObjectiveC.h, and LeapObjectiveC.mm files in your project. Add the libLeap.dylib library to your linked libraries.
To avoid compiler and linker errors during the beta period (which may have since been resolved), I needed to go to my build settings and change the C++ Standard Library to libstdc++.
You need to make sure the Leap library gets bundled with your application, so make sure it is copied over into the bundled frameworks during a build phase. I also needed to use the following Run Script build phase to make sure its internal path was set right (again, not sure if this is needed now):
echo TARGET_BUILD_DIR=${TARGET_BUILD_DIR}
echo TARGET_NAME=${TARGET_NAME}
cd "${TARGET_BUILD_DIR}/${TARGET_NAME}.app/Contents/MacOS"
ls -la
# then remap the loader path
install_name_tool -change #loader_path/libLeap.dylib #executable_path/../Resources/libLeap.dylib "${TARGET_NAME}"
One last caution when setting this up is that if you are sandboxing your Mac application, you need to enable the outgoing network connections entitlement, or the application will not be able to connect to the Leap Motion server application running on your Mac.
Once all the setup is completed, you can start getting input from the Leap Motion Controller. A central LeapController object is what will provide delegate callbacks for Leap Motion events. You set one up using code like the following:
controller = [[LeapController alloc] init];
[controller addListener:self];
Your delegate needs to satisfy the LeapListener protocol, which has a bunch of optional callback methods:
// Controller object has initialized
- (void)onInit:(NSNotification *)notification
// Controller has connected
- (void)onConnect:(NSNotification *)notification;
// Controller has disconnected
- (void)onDisconnect:(NSNotification *)notification;
// Exiting your LeapController object
- (void)onExit:(NSNotification *)notification;
// New frame data has arrived from the controller
- (void)onFrame:(NSNotification *)notification;
The connection and disconnection callbacks are obvious, although one thing you'll notice is that the disconnection callback is never triggered while debugging your application in Xcode. You need to run your application outside of a debugger to get that to fire when you disconnect the Leap Motion Controller.
You'll spend most of your time in the -onFrame: callback, because that's where you get your positioning updates. You get your LeapController back as the notification object from that, and you can extract the current frame data using the following:
LeapController *aController = (LeapController *)[notification object];
LeapFrame *frame = [aController frame:0];
The LeapFrame has within it all the scene data observed by the Leap, including hand and finger positions as well as overall scaling of those. The -hands method on LeapFrame gives you an array of all the detected LeapHand objects. In turn, you get each LeapFinger for a LeapHand from its -fingers method. You can also extract the palmPosition, palmNormal, and overall direction of the hand.
Directions are provided as LeapVectors, which are wrapper objects around 3-D vectors. You can extract X, Y, and Z components from them, or perform vector manipulations or comparisons between other LeapVectors.
In Molecules, I adjust the scale and orientation of my molecular structures by reading the movement in-and-out or left-to-right relative to the screen. I do this by comparing the position of an open hand from one frame to the next. I store the previous frame and then do a comparison between now and the last time we saw this hand using
LeapVector *handTranslation = [firstHand translation:previousLeapFrame];
You can also compare scale, rotation, etc. between frames for hands or for all objects in the frame as one group. From the frame-to-frame translation I obtained in the above code, I can extract the individual X, Y, and Z components to rotate my model in response to X and Y translations and scale based on the Z.
I use gross positioning for my application, but you can obviously go a lot finer by tracking individual fingertips. After you've got your application up and running with the device, I recommend reading through the Leap SDK header files for the Objective-C side to see what other capabilities are exposed to you. Also, experiment with different modes of interaction, because you might be surprised at what does and does not work well in 3-D space.
#Brad Larson's answer pretty much covered everything, so refer to it for more details. However, his answer shows how to use the LeapListener protocol which works using NSNotifications.
If you either don't like NSNotificationCenter :p, or don't care about what thread your updates happen on here's an example of how to use LeapDelegate.
#interface MyClass () < LeapDelegate >
#property (strong, nonatomic) LeapController *controller;
#end
- (void)startLeapMotion
{
if (!_controller) {
_controller = [[LeapController alloc] initWithDelegate:self];
//
// Could also be...
//
// [_controller addDelegate:self];
// [_controller removeDelegate];
}
}
- (void)onInit:(LeapController *)controller
{
NSLog(#"Init");
}
- (void)onConnect:(LeapController *)controller
{
NSLog(#"Connect");
// Some settings that came bundled with the Leap sample project. Use if needed
//
// [controller setPolicyFlags:LEAP_POLICY_DEFAULT];
// [controller enableGesture:LEAP_GESTURE_TYPE_CIRCLE enable:YES];
// [controller enableGesture:LEAP_GESTURE_TYPE_KEY_TAP enable:YES];
// [controller enableGesture:LEAP_GESTURE_TYPE_SCREEN_TAP enable:YES];
// [controller enableGesture:LEAP_GESTURE_TYPE_SWIPE enable:YES];
}
- (void)onFocusGained:(LeapController *)controller
{
NSLog(#"Focus Gained");
}
- (void)onFrame:(LeapController *)controller
{
// Write awesome code here!!!
NSLog(#"Frame");
}
- (void)onFocusLost:(LeapController *)controller
{
NSLog(#"Focus Lost");
}
- (void)onDisconnect:(LeapController *)controller
{
NSLog(#"Disconnected");
}
- (void)onExit:(LeapController *)controller
{
NSLog(#"Exited");
}

How to activate a custom screensaver preview in Cocoa/Obj-C?

I have created a fairly simple screensaver that runs on Mac OS 10.6.5 without issue.
The configuration screen has accumulated quite a few different options and I'm trying to implement my own preview on the configureSheet window so the user (just me, currently) can immediately see the effect of a change without having to OK and Test each change.
I've added an NSView to the configureSheet and set the custom class in Interface Builder to my ScreenSaverView subclass. I know that drawRect: is firing, because I can remove the condition for clearing the view to black, and my custom preview no longer appears with the black background.
Here is that function (based on several fine tutorials on the Internet):
- (void)drawRect:(NSRect)rect
{
if ( shouldDrawBackground )
{
[super drawRect:rect];
shouldDrawBackground = NO;
}
if (pausing == NO)
[spiroForm drawForm];
}
The spiroForm class simply draws itself into the ScreenSaverView frame using NSBezierPath and, as mentioned, is not problematical for the actual screensaver or the built-in System Preferences preview. The custom preview (configureView) frame is passed into the init method for, um, itself (since its custom class is my ScreenSaverView subclass.) The -initWithFrame method is called in configureSheet before returning the configureSheet object to the OS:
[configureView initWithFrame:[configureView bounds] isPreview:YES];
Maybe I don't have to do that? It was just something I tried to see if it was required for drawing.
I eventually added a delegate to the configureSheet to try triggering the startAnimation and stopAnimation functions of my preview via windowWillBeginSheet and windowWillEndSheet notifications, but those don't appear to be getting called for some reason. The delegate is declared as NSObject <NSWindowDelegate> and I set the delegate in the configureSheet method before returning the configureSheet object.
I've been working on this for days, but haven't been able to find anything about how the OS manages the ScreenSaverView objects (which I think is what I'm trying to emulate by running my own copy.)
Does anybody have any suggestions on how to manage this or if Apple documents it somewhere that I haven't found? This isn't really required for the screensaver to work, I just think it would be fun (I also looked for a way to use the OS preview, but it's blocked while the configureSheet is activated.)
OK, there are a couple of 'duh' moments involved with the solution:
First of all, I was setting the delegate for the sheet notifications to the sheet itself. The window that the sheet belongs to gets the notifications.
Secondly, that very window that the sheet belongs to is owned by System Preferences, I don't see any way to set my delegate class as a delegate to that window, so the whole delegate thing doesn't appear to be a viable solution.
I ended up subclassing NSWindow for the configureSheet so that I could start and stop animation on my preview by over-riding the makeKeyWindow and close methods.
- (void) makeKeyWindow
{
if (myPreview != nil)
if ( ! [myPreview isAnimating])
{
[myPreview startAnimation];
}
[super makeKeyWindow];
}
I also had to add an IBOutlet for my preview object itself and connect it in Interface Builder.
Still working out a couple of issues, but now when I click on my screensaver Options button, my configureSheet drops down and displays its own preview while you set options. Sheesh. The hoops I jump through for these little niceties. Anyway, I like it. Onward and upward.

Objective-C tab settings and whitespace style

Apple's Objective-C documentation, references, and generated code seem to be totally inconsistent with their coding style. I can not determine the "preferred" style (if one exists) for Objective-C and Cocoa source code.
Here's what I've ran into so far.
Tab settings
Xcode's default setting is set to tabs. However, generated projects are always created with 4 spaces regardless of your tab settings. Worse, some generated code has stray tabs hanging around. Downloaded code examples seem to vary from project to project as well.
Method definition spacing
In Apple's example code, different styles of method definition even vary within a single file. But the most common style of spacing seems to include a space between the - or + and spaces only between addition arguments. This style is also used in the Xcode reference library which leads me to believe it is the preferred style.
- (IBAction)reloadData:(id)sender;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
Inconsistent variates:
- (IBAction) reloadData:(id)sender;
- (IBAction)reloadData:(id) sender;
-(IBAction)reloadData:(id)sender;
- (IBAction) reloadData: (id)sender;
- (id)tableView: (NSTableView *)aTableView objectValueForTableColumn: (NSTableColumn *)aTableColumn row: (NSInteger)rowIndex
Brace style
Another thing that is widely inconsistent is the brace style. There are two popular styles:
Open brace on its own line:
#interface Foo : NSObject
{
}
- (id)init
{
}
Open brace on the same line as the method declaration:
#interface Foo : NSObject {
}
- (id)init {
}
Question
Is there a recommended coding style for Objective-C by Apple? If not, what style do you prefer and why?
I found that I was constantly setting the generated projects to my own preferred style, so I edited the template files to my preferences (tabs for indent, braces on a line of their own). The files are here:
/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/
I select everything, get rid of all indentation with cmd-[ (as many times as the document needs) and do Edit->Format->re-indent. I have this hooked up to shift-cmd-[ for convenience.
I've seen the differences in style for example code and project templates and just assumed it was due to the age of the files being different. I guess they have had different internal coding style guides which have changed.
Here's the Google Obj-C style guide:
http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml
and another nice one from CocoaDevCentral:
http://www.cocoadevcentral.com/articles/000082.php
of if you really want to follow Apple rules, the Objective C programming guide has a bunch of code examples to take rules from.
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/