• Skip to main content
  • Skip to primary sidebar

Ravi Shankar

Tweaking Apps

  • About
  • Portfolio
  • Privacy Policy

Archives for April 2014

Generate random numbers – Example of motion and touch event

April 29, 2014 By Ravi Shankar Leave a Comment

Let us see how to use motion and touch event in iOS by looking through a simple project that displays random number. This project displays random number on a label for a touch and motion event. Check out this link for more about events in iOS

201404291450.jpg

After creating a single view application, add a new class that contains the method for generating random. If you do not want to add new class just for a single method then you can add the method as part of your ViewController class itself. arc4random_uniform accepts a seed value (90) and generated number will be below this seed value.

-(NSInteger)generateNumber {

   return arc4random_uniform(90);

}

Now add a UILabel for displaying the generated random numbers. This includes both adding IBOutlet property and connecting it with UILabel on Interface Builder.


Motion Event

The are three motion-handling methods -(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event, -(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event and -(void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event

In this example, we can use the motionEnded method to call the displayNumber method

-(void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {

if (UIEventSubtypeMotionShake) {

[self displayNumber];

}

}

-(void)displayNumber {

RandomNumber *randomNumber = [[RandomNumber alloc] init];

self.displayRandomNumber.text = [NSString stringWithFormat:@”%d”,[randomNumber generateNumber]];

}

And to test this on Simulator, use the Shake Gesture menu under Hardware to simulate motion event.

201404291717.jpg

Touch Event

There are four touch events corresponding to each touch phase.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

In this example, we will use the touchesEnded method for displaying the random numbers.

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

[self displayNumber];

}

You can download the source code along with TDD classes for this beginner tutorial on touch and motion event from here.

Filed Under: ios, Programming, Xcode Tagged With: motion events, touch events, Xcode

Test Driven Development in iOS – Beginners tutorial – Part 2

April 14, 2014 By Ravi Shankar 3 Comments

This is in continuation of the Test Driven Development in iOS – Beginners tutorial and this covers writing XCTest user interfaces in iOS. AddingTwoNumbers app will have following controls

1. Three textfields – For entering the numbers and displaying the result.

2. Two buttons – Add button to perform addition and Reset button to clear all the textfields.

Let us create a new Test case class for testing the user interface, AddTwoNumberViewControllerTests.m

201404141732.jpg

201404141743.jpg

The first test written is for checking the App Delegate call, storyboard, view controller and view.

-(void)testViewControllerViewExists {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

XCTAssertNotNil([viewController view], @”ViewController should contain a view”);

}

The following needs to be done for passing this test.

1. Add new Objective-C class, subclass of UIViewController (RSViewController).

201404141858.jpg

2. Add a Storyboard file to your project which would contain the ViewController required for this app.

201404142108.jpg

3. Drag and drop, UIViewController on the storyboard and set the class name for the ViewController as RSViewController and StoryboardID as RSViewViewController.

201404142114.jpg

4. Now add the code required in App Delegate’s didFinishLaunchingWithOptions method. This should load the storyboard then instantiate the view controller and set it as RootViewController.

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:[NSBundle mainBundle]];

UIViewController *vc =[storyboard instantiateInitialViewController];

  

// Set root view controller and make windows visible

self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

self.window.rootViewController = vc;

[self.window makeKeyAndVisible];

return YES;

}

A lot has been added to just pass a single test case, probably this can be broken up by writing some more tests.

201404142126.jpg

Next add the test for checking the FirstNumber UITextField connection.

-(void)testFirstNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

Next add the test for checking the FirstNumber UITextField connection.

-(void)testFirstNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

This test would pass after adding the IBOutlet for UITextField in the interface file.

#import <UIKit/UIKit.h>

@interface RSViewController : UIViewController

@property (nonatomic,strong) IBOutlet UITextField *firstNumberTextField;

@end

Then add UITextField to the ViewController and make the connection to UITextField with IBOutlet property.

201404142141.jpg

201404142152.jpg

Then add similar test cases for secondNumber and result textfields. Also make corresponding changes to pass the test.

-(void)testSecondNumberTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController secondNumberTextField], @”secondNumberTextField should be connected”);

}

-(void)testresultTextFieldConnection {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

RSViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

XCTAssertNotNil([viewController resultTextField], @”resultTextField should be connected”);

}

201404142205.jpg
Before we move on to the Button test cases, let us refactor the test cases code and remove the duplication of code. The common code has been moved to setup method and now the test cases should loo lot simpler.

@implementation AddTwoNumberViewControllerTests

{

RSViewController *viewController;

}

– (void)setUp

{

[super setUp];

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@”Storyboard” bundle:nil];

viewController = [storyboard instantiateViewControllerWithIdentifier:

@”RSViewController”];

[viewController view];

}

– (void)tearDown

{

[super tearDown];

}

-(void)testViewControllerViewExists {

XCTAssertNotNil([viewController view], @”ViewController should contain a view”);

}

-(void)testFirstNumberTextFieldConnection {

XCTAssertNotNil([viewController firstNumberTextField], @”firstNumberTextField should be connected”);

}

-(void)testSecondNumberTextFieldConnection {

XCTAssertNotNil([viewController secondNumberTextField], @”secondNumberTextField should be connected”);

}

-(void)testresultTextFieldConnection {

XCTAssertNotNil([viewController resultTextField], @”resultTextField should be connected”);

}

Now add the failing test case for Add button and see if the button exists.

-(void)testAddButtonConnection {

XCTAssertNotNil([viewController addButton], @”add button should be connected”);

}

And to pass the above test, add button to ViewController, create a addButton IBOutlet property of type UIButton and make connection to IBOutlet and button field.

@property (nonatomic,strong) IBOutlet UIButton *addButton;


201404142226.jpg

Now add the next test case for checking IBAction method. The below method checks whether addButton call selector method addNumbers on UIControlEventTouchUpInside.

-(void)testAddButtonCheckIBAction {

NSArray *actions = [viewController.addButton actionsForTarget:viewController

forControlEvent:UIControlEventTouchUpInside];

XCTAssertTrue([actions containsObject:@”addNumbers:”], @””);

}

Add this method to RSViewController.m and link this to touchUpInside event of addButton.

-(IBAction)addNumbers:(id)sender {

  

}

Then add the below test cases for checking the addition of two numbers.

-(void)testAddingTenPlusTwentyShouldBeThirty {

viewController.firstNumberTextField.text = @”10″;

viewController.secondNumberTextField.text = @”20″;

[viewController.addButton sendActionsForControlEvents: UIControlEventTouchUpInside];

XCTAssertEqualObjects(viewController.resultTextField.text,@”30″,”Result of the textfield should be 30″);

}

Make changes to the addNumbers method in RSViewController.m by calling application logic method in Addition.m

-(IBAction)addNumbers:(id)sender {

RSAddition *addition = [[RSAddition alloc] init];

NSInteger result = [addition addNumberOne:[self.firstNumberTextField.text integerValue] withNumberTwo:[self.secondNumberTextField.text integerValue]];

self.resultTextField.text = [NSString stringWithFormat:@”%d”,result];

}
Now repeat the test cases for reset button which clears all the textfields.

-(void)testResetButtonConnection {

XCTAssertNotNil([viewController resetButton], @”reset button should be connected”);

}

-(void)testResetButtonCheckIBAction {

NSArray *actions = [viewController.resetButton actionsForTarget:viewController

forControlEvent:UIControlEventTouchUpInside];

XCTAssertTrue([actions containsObject:@”resetFields:”], @””);

}

-(void)testResetButtonShouldClearFields {

viewController.firstNumberTextField.text = @”10″;

viewController.secondNumberTextField.text = @”20″;

viewController.resultTextField.text = @”30″;

[viewController.resetButton sendActionsForControlEvents: UIControlEventTouchUpInside];

XCTAssertEqualObjects(viewController.firstNumberTextField.text, @””, @”FirstNumber textfield should be empty”);

XCTAssertEqualObjects(viewController.secondNumberTextField.text, @””, @”SecondNumber textfield should be empty”);

XCTAssertEqualObjects(viewController.resultTextField.text, @””, @”Result textfield should be empty”);

}

And the corresponding changes to the user interface and RSViewController.m file

-(IBAction)resetFields:(id)sender {

self.firstNumberTextField.text = @””;

self.secondNumberTextField.text = @””;

self.resultTextField.text = @””;

}

Though lot more test cases can be added, I believe this is a decent starter project for learning TDD in iOS. And it is always nice to see the green mark in test navigator.
201404142302.jpg
Now you should be able to run your app and test the functionality.

201404142303.jpg
Source code can be downloaded from here

Filed Under: ios, iPhone, Xcode Tagged With: TDD, User Interface, Xcode, XCTest

Test Driven Development in iOS – Beginners tutorial – Part 1

April 13, 2014 By Ravi Shankar 7 Comments

This is a beginner tutorial on TDD in iOS, explained with an example app that adds two numbers. Let us see try to use some XCTest assertions supported in iOS in this project.

Create a New iOS project select the template as Empty Application

201404132059.jpg

Enter the required details in Choose options for your new project screen.

201404132104.jpg

Step 3: Then specify the folder to save this project.

201404132106.jpg

The project navigator screen will have two targets, one for app and add another for XCTest framework. The folder that ends with “Tests” will contain the default XCTest file .

Lets use the class AddingTwoNumbersTests.m for testing the application logic. Open AddingTwoNumbersTests.m and delete default testExample method.

201404132127.jpg

Create a failing method that tests for existence of new “RSAddition” class that is going to have method for adding two numbers.

201404132155.jpg  

You should notice the errors displayed in the above screenshot after adding testAdditionClassExists. To fix these error, create a new class named RSAddition subclass of NSObject and add the class to both the targets. Then import the Addition.h in “AddingTwoNumbersTests.m”.

201404132152.jpg

201404132153.jpg

Now the tests should pass when it gets executed. You should notice the green tick mark before the class and method and shown in the below screenshot.

201404132158.jpg

Now add the following method to do a simple addition of 2+2.

-(void)testAddTwoPlusTwo {

RSAddition *addition = [[RSAddition alloc] init];

NSInteger result = [addition addNumberOne:2 withNumberTwo:2];

XCTAssertEqual(result, 4, @”Addition of 2 + 2 is 4″);

}

RSAddition class needs to have a new method addNumberOne: withNumberTwo: Add the method definition in RSAddition.h interface file.

-(NSInteger)addNumberOne:(NSInteger) firstNumber withNumberTwo:(NSInteger) secondNumber;

Then add the following method to RSAddition.m implementation file. Let us hardcode the result “4” for time being to pass this test.

-(NSInteger)addNumberOne:(NSInteger) firstNumber withNumberTwo:(NSInteger) secondNumber {

return 4;

}

Now the test should get executed successfully.
201404132209.jpg
Let us add another test to fix the hardcoding problem. This time we will add two different numbers 2 and 7. But before adding the test method, we need to refactor the existing code in the test file. The below code is used by both the test methods and this is the best candidate to be placed under setup method.

  RSAddition *addition = [[RSAddition alloc] init];

The changed code should look as shown in the below screenshot.

201404132223.jpg

Adding the below method should fail as the addition method is hardcoded to return value as 4.

-(void)testAddTwoPlusSeven {

NSInteger result = [addition addNumberOne:2 withNumberTwo:7];

XCTAssertEqual(result, 9, @”Addition of 2 + 7 is 9″);

}

201404132226.jpg

Now edit the addition method to fix this problem. It requires just a one line change and now the test should pass.

201404132229.jpg

201404132231.jpg

The next tutorial will cover the steps for adding the view controller and required fields for adding two numbers using the above added application logic class.

Download source code from here.

Filed Under: ios, iPhone, Xcode Tagged With: Assertions, iOS, TDD, Xcode, XCTest

Primary Sidebar

Recent Posts

  • We have blocked all requests from this device – Firebase Phone Authentication
  • iPhone is not available error message in Xcode
  • Clear CocoaPods cache, re-download and reinstall all pods
  • PDFKit – View, Annotate PDF file in Swift
  • Tab Bar Controller with WebView

Archives

  • September 2020
  • April 2020
  • December 2019
  • November 2019
  • October 2019
  • February 2019
  • October 2017
  • June 2017
  • May 2017
  • March 2017
  • September 2016
  • March 2016
  • February 2016
  • January 2016
  • December 2015
  • November 2015
  • October 2015
  • September 2015
  • August 2015
  • July 2015
  • June 2015
  • May 2015
  • April 2015
  • March 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • April 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • October 2013
  • August 2013
  • July 2013
  • June 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • November 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • April 2011
  • March 2011
  • January 2011
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • July 2009
  • March 2008

Copyright 2020 © rshankar.com