Quantcast
Channel: Envato Tuts+ Code - Mobile Development
Viewing all 1836 articles
Browse latest View live

Create a Collect-the-Pieces Game with Corona

$
0
0

In this tutorial, I will show you how to create a collect-the-pieces game using the Corona SDK and the Lua programming language. We will explore how to use touch controls and events, create shapes using the graphics API, and we will also make use of physics collisions and timers. Let’s get started.


1. Application Overview

In this game, the player will be able to control a puck on the screen to collect other pieces with the same color. The player has only a limited time to collect as many pieces as possible. If the player touches a piece from another color, the game is over.

App Overview
This application uses the Graphics 2.0 Library. If you’re using an older version of the Corona SDK, you may run into problems. Consult the migration guide for more information.

In this project you will learn the following skills:

  • physics collisions
  • create text fields
  • user timers and images
  • how to use touch controls and events
  • create shapes using the new Graphics API

2. Target Device

Target Device

The first thing we have to do is select the platform we want to run our application on. This enables us to choose the correct sizes for the artwork that we’ll use.

The iOS platform has the following requirements:

  • iPad 1/2/Mini: 1024px x 768px, 132 PPI
  • iPad Retina: 2048px x 1536px, 264 PPI
  • iPhone/iPod Touch: 320px x 480px, 163 PPI
  • iPhone/iPod Retina: 960px x 640px, 326 PPI
  • iPhone 5/iPod Touch: 1136px x 640px, 326 PPI

Since Android is a more open platform, there are many devices and possible resolutions. A few of the more common ones are listed below:

  • Asus Nexus 7 Tablet: 800px x 1280px, 216 PPI
  • Motorola Droid X: 854px x 480px, 228 PPI
  • Samsung Galaxy SIII: 720px x 1280px, 306 PPI

In this tutorial, we’ll be focusing on the iOS platform and the iPhone and iPod Touch in particular. However, the code used in this tutorial can also be used for the Android platform.


3. Interface

Interface

We’ll use a simple and friendly interface with multiple shapes, buttons, bitmaps and more. The artwork for the interface can be found in the source files of this tutorial.


4. Export Graphics

Export Graphics

Depending on the device you’ve selected, you may need to convert the graphics to the recommended resolution (PPI), which you can do in your favorite image editor. I used the Adjust Size… option in the Tools menu of the Preview application on OS X. Remember to give the images a descriptive name and save them in your project folder.


5. Application Configuration

We’ll use a configuration file, config.lua, to make the application go full screen across devices. The configuration file shows the original screen size and the method used to scale the content in case the application is run on a different resolution.

application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = 'letterbox'
    },
}

6. main.lua

Let’s write the actual application. Open your preferred Lua editor. Any plain text editor will work, but it is recommended to use a text editor that has syntax highlighting. Create a new file and save it as main.lua in your project folder.


7. Code Structure

We’ll structure our code as if it were a class. If you’re familiar with ActionScript or Java, you should find the project structure familiar.

Necessary Classes
Variables and Constants
Declare Functions
    constructor (Main function)
    class methods (other functions)
call Main function

8. Hide Status Bar

display.setStatusBar(display.HiddenStatusBar)

This code snippet hides the status bar. The status bar is the bar at the top of the device’s screen that shows the time, signal, and other indicators.


9. Default Anchors

Setting the display’s default anchors is useful if you’re porting an application from the previous Graphics library, that is, the projects you’ve created with previous version of the Corona SDK. Since the release of the Graphics 2.0 library, the reference point of every image has changed from its top-left to its center. To change this in every image that you use in your project, we can invoke the setDefault method and pass a value from 0.0 to 1.0, with 0.0 being the left if you change the x anchor and the top if you change the y anchor.

display.setDefault('anchorX', 0)
display.setDefault('anchorY', 0)

10. Import Physics

We’ll be using the physics library to handle collisions. Import and start the library using the code snippet shown below.

-- Physics
local physics = require('physics')
physics.start()

11. Background

Background

A simple background for the application’s user interface. The code snippet below draws the background to the screen.

-- Graphics
-- [Background]
local gameBg = display.newImage('gameBg.png')

12. Title View

Title View

This is the title view. It’s the first interactive screen to appear in our game. These variables store its components.

-- [Title View]
local title
local playBtn
local creditsBtn
local titleView

13. Credits View

Credits View

The credits view shows the credits and copyright of the application. The creditsView variable is used to store it.

-- [CreditsView]
local creditsView

14. Score Text Field

Score

We’ll create a text field for showing the player’s score a bit later in this tutorial. We store a reference to this text field in the scoreTF variable.

-- Score TextField
local scoreTF

15. Pucks

Pucks

The pucks in the game are created and distributed randomly on the stage. The pucks group will be used to group them so that we can manipulate them easily.

-- Pucks
local pucks

16. Alert

Alert

The alertView variable keeps a reference to the alert view that is displayed when a puck of the wrong color is touched by the player. The alert view will show the player the game is over.

-- Alert
local alertView

17. Sounds

Sounds

We’ll use sound effects to give the game extra character. The sounds that I’ve used in this project were obtained from as3sfxr. You can find the background music on Play On Loop.

-- Sounds
local bgMusic = audio.loadStream('POL-sky-sanctuary-short.mp3')
local blip = audio.loadSound('blip.wav')
local wrong = audio.loadSound('lose.wav')

18. Variables

The next code snippet lists the variables that we’ll use in the game. The totalPucks variable stores the number of pucks that are placed on the stage, timerSrc keeps a reference to the game’s timer, and time references the rectangle that show the remaining time.

-- Variables
local totalPucks = 20
local timerSrc
local time

19. Declare Functions

We declare the functions as local at the very beginning. In Lua, you can forward declare a function by declaring its name before implementing the function’s body. This makes it easier to keep track of the various functions that we’ll use in this project.

-- Functions
local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local createPuck = {}
local movePuck = {}
local reduceTime = {}
local alert = {}

20. Constructor

Let’s start by creating a stub implementation for the function that will initialize the game logic, the Main function.

function Main()
end

21. Add Title View

Next, we draw the title view to the screen and add a tap listener to each button. The newImage method is used to load the images and display them on the screen using the positions passed to the function. We also create a group named titleView that serves as a container for the newly created elements.

function Main()
  titleBg = display.newImage('titleBg.png')
  playBtn = display.newImage('playBtn.png', 220, 168)
  creditsBtn = display.newImage('creditsBtn.png', 204, 230)
  titleView = display.newGroup(titleBg, playBtn, creditsBtn)
  startButtonListeners('add')
end

22. Start Button Listeners

In startButtonListeners, we add the event listeners to the title view’s buttons. When the play button is tapped, we show and start the game. When the credits button is tapped, we show the game’s credits.

function startButtonListeners(action)
  if(action == 'add') then
    playBtn:addEventListener('tap', showGameView)
    creditsBtn:addEventListener('tap', showCredits)
  else
    playBtn:removeEventListener('tap', showGameView)
    creditsBtn:removeEventListener('tap', showCredits)
  end
end

23. Show Credits

In showCredits, we hide the buttons, display the credits, and add a tap listener to hide the credits when the player taps the credits.

function showCredits:tap(e)
  playBtn.isVisible = false
  creditsBtn.isVisible = false
  creditsView = display.newImage('credits.png', -110, display.contentHeight-80)
  transition.to(creditsView, {time = 300, x = 0, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end

24. Hide Credits

When the player taps the credits, the view is tweened from the stage and removed.

function hideCredits:tap(e)
  playBtn.isVisible = true
  creditsBtn.isVisible = true
  transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
end

25. Show Game View

When the player taps the play button to start a new game, the title view is tweened from the stage and hidden. This shows the game view. The game view is the heart of the game. Let’s break the rest of the showGameView step by step.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
end

26. Score Text Field

We start by creating the score text field as shown below.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
  -- TextFields
  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)
end

The newText method accept a number of arguments.

  • The initial text of the text field.
  • The text field’s x coordinate.
  • The text field’s y coordinate.
  • The text field’s font and font size.

On iOS, you have access to a wide range of fonts. On Android, there are only three fonts available, Droid Sans, Droid Serif, and Droid Sans Mono.


27. Timer

In the next step, we create the timer’s rectangle shape using Corona built-in vector graphics library. The newRect function creates a rectangle that is 20pt wide, 6pt tall, and it places it in the top-right of the stage. The default color of new shapes is white so we need to change the rectangle’s color by invoking setFillColor and passing in an RGB value. The Graphics 2.0 library doesn’t use values ranging from 0 to 255 for RGB values. Instead a RGB value is expected to range from 0.0 to 1.0.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
  -- TextFields
  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)
  -- Timer
  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)
end

A quick and easy approach for converting old RGB value to new values is by dividing the old value by 255. For example, 123 becomes 123/255, which translates to 0.48.


28. Pucks

The pucks group stores all the pucks so that we can manipulate them all at once.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
  -- TextFields
  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)
  -- Timer
  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)
  -- Pucks
  pucks = display.newGroup()
end

29. Start Game

To finish the showGameViewMethod, we install the game listeners and start the background music. By setting the loops parameter to -1, the background music will loop until we tell it to stop.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
  -- TextFields
  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)
  -- Timer
  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)
  -- Pucks
  pucks = display.newGroup()
  gameListeners('add')
  audio.play(bgMusic, {loops = -1, channel = 1})
end

30. Game Listeners

In showGameView, we invoked the gameListeners function in which create the pucks by invoking createPucks and we also create a time.

function gameListeners(action)
  if(action == 'add') then
    createPucks()
    timerSrc = timer.performWithDelay(1000, reduceTime, 20)
  else
    for i = 1, pucks.numChildren do
      pucks[i]:removeEventListener('touch', movePuck)
    end
    timer.cancel(timerSrc)
    timerSrc = nil
  end
end

31. Create Pucks

In createPucks, we use a for loop to instantiate the pucks. The number of pucks is stored in totalPucks, which we set to 20 a bit earlier in this tutorial.

A random position is calculated for each puck using math.random. We also use math.random to help us load a different color from the project’s images. How does this work? We generate a random integer between 1 and 4, and add the result to the name of the image that we want to load. For example, if the random number is equal to 3, the game loads an image named puck3.png.

function createPucks()
  for i = 1, totalPucks do
    local p
    local rnd = math.floor(math.random() * 4) + 1
    p = display.newImage('puck' .. tostring(rnd) .. '.png', math.floor(math.random() * display.contentWidth), math.floor(math.random() * display.contentHeight))
    p.name = 'p' .. tostring(rnd)
    p:addEventListener('touch', movePuck)
    -- Physics
    physics.addBody(p, 'dynamic', {radius = 12})
    p.isSensor = true
    pucks:insert(p)
  end
end

32. Move Function

We use touch events to let the player move the pucks. When the player touches a puck, it is aligned with the position of the touch, the player’s finger, and is then moved by updating its position. We also add a collision listener to the active puck to detect when the puck collides with another puck. This event listener is removed when the player releases the puck by lifting their finger from the screen.

function movePuck(e)
  if(e.phase == 'began') then
    -- Collision
    e.target.x = e.x - e.target.width/2
    e.target.y = e.y - e.target.height/2
    e.target:addEventListener('collision', onCollision)
  end
  if(e.phase == 'moved') then
    e.target.x = e.x - e.target.width/2
    e.target.y = e.y- e.target.height/2
  end
  if(e.phase == 'ended') then
    e.target:addEventListener('collision', onCollision)
  end
end

33. Timer

The reduceTimer function is in charge of the timer rectangle we created earlier. Every second, the width of the shape is reduced by setting the its xScale property and it is removed from the stage when it’s no longer visible. An alert view is shown to the player when the time’s up.

function reduceTime(e)
  time.xScale = time.xScale - 0.05
  time.x = 450
  if(time.xScale <= 0.2) then
    display.remove(time)
    alert()
  end
end

34. Handling Collisions

The onCollision function is in charge of handling collisions between pucks.

function onCollision(e)
  if(e.other ~= nil) then
    if(e.other.name == e.target.name) then
      audio.play(blip)
      -- Local Score
      local score = display.newText('50', e.other.x, e.other.y, 'Courier New Bold', 14)
      transition.to(score, {time = 500, xScale = 1.5, yScale = 1.5, y = score.y - 20, onComplete = function() display.remove(score) score = nil end })
      -- Remove
      display.remove(e.other)
      e.other = nil
      -- Total Score
      scoreTF.text = tostring(tonumber(scoreTF.text) + 50)
      scoreTF.x = 15
    else
      audio.play(wrong)
      alert('lose')
    end
  end
end

This is what happens when a collision occurs.

  • We first check whether the names of the pucks are the same to see if their color matches.
  • If they have the same color, we play a sound effect.
  • The player’s score is increased by 50 points and the score text field is updated.
  • If the color of the pucks don’t match, a different sound effect is played and the game over alert view is shown to the player.

35. Alert

The alert function is a simple helper function to show an alert view and animated it. We stop the audio after 700 milliseconds to make sure that we can play a sound effect. The game listeners are removed to end the game and we show an appropriate image to the player.

function alert(action)
  timer.performWithDelay(700, function() audio.stop(1) audio.dispose(bgMusic) bgMusic = nil end, 1)
  gameListeners('rmv')
  if(action == 'lose') then
    alertView = display.newImage('gameOver.png', 155, 125)
  else
    alertView = display.newImage('timeUp.png', 155, 125)
    local score = display.newText(scoreTF.text, 225, 160, 'Courier New Bold', 20)
    score:setFillColor(255, 255, 255)
  end
  transition.from(alertView, {time = 300, xScale = 0.5, yScale = 0.5})
  -- Wait 100 ms to stop physics
  timer.performWithDelay(10, function() physics.stop() end, 1)
end

36. Call Main

To start the game, we invoke the Main function as shown below.

Main()

37. Loading Screen

Loading Screen

On the iOS platform, the file named Default.png is displayed while the application is launching. Add this image to your project’s source folder, it will be automatically added by the Corona compiler.


38. Icon

Icon

Using the graphics you created earlier, you can now create a nice icon. The dimensions of the icon size for a non-retina iPhone are 57px x 57px, while the retina version needs to be 114px x 114px. The artwork for iTunes is required to be 1024px x 1024px. I suggest creating the iTunes artwork first and then creating the smaller sized images by scaling the iTunes artwork down to the correct dimensions. There is no need to make the application icon glossy or add rounded corners as this is taken care of by the operating system for you.


39. Testing in the Simulator

Testing

It’s time to test our application in the simulator. Open the Corona Simulator, browse to your project folder, and click Open. If everything works as expected, you’re ready for the final step.


40. Build

Build

In the Corona Simulator, go to File > Build and select the target device. Fill out the required fields and click Build. Wait a few seconds and your application is ready to test on a device and/or to be submitted for distribution.


Conclusion

In this tutorial, we’ve learned about touch listeners, collision detection, physics as well as a few other skills that can be useful in a wide number of games. Experiment with the final result and try to modify the game to create your own version of the game. I hope you liked this tutorial and found it helpful. Thank you for reading.


Working with NSURLSession: AFNetworking 2.0

$
0
0
This entry is part 5 of 5 in the series Working with NSURLSession

In the previous installments of this series, we’ve taken a close look at the NSURLSession API introduced in iOS 7 and OS X Mavericks. Networking on iOS and OS X has become much simpler and more flexible thanks to the NSURLSession API. Does this mean that you should stop using AFNetworking for your networking needs? And what about AFNetworking 2.0, which was introduced a few months ago? In this final installment, I will tell you about AFNetworking 2.0 and how it compares to the NSURLSession API.


Introduction

As Mattt Thompson points out on NSHipster, AFNetworking has become the de facto networking library for Cocoa applications. At the time of writing, the library has surpassed 10,000 stars on GitHub and was forked close to 3,000 times.

If you’re new to AFNetworking and would like to read more about its history and features, then I suggest to read Mattt’s post about AFNetworking 2.0 on NSHipster. In this article, I’d like to focus on two aspects, what is new in AFNetworking 2.0 and how does it compare to the NSURLSession API . The question you should be asking yourself is “Should I still be using AFNetworking with NSURLSession in my toolbox?” That’s the question I plan to answer in this article.


Requirements

Before we take a closer look at what AFNetworking 2.0 has to offer, it’s important to know about the library’s new requirements. It shouldn’t be a surprise that the requirements of AFNetworking 2.0 are stricter than those of AFNetworking 1.x. As its version number indicates, AFNetworking includes breaking changes, which are the result of its new architecture. This also means that migrating a project from AFNetworking 1.x to 2.0 should be done with caution. The migration guide is the ideal place to start if you plan on migrating a project to AFNetworking 2.0.

AFNetworking no longer supports iOS 4.3 and 5. The minimum deployment targets for iOS and OS X are iOS 6 and OS X 10.8 respectively. AFNetworking 2.0 also requires Xcode 5. If you’re still using Xcode 4, this may be a good time to make the transition to Xcode 5.


Solid Foundation

AFURLConnectionOperation

As many of you probably know, AFNetworking is built on top of NSURLConnection and NSOperation, which has proven to be a powerful and elegant combination. The result of this combination is AFURLConnectionOperation, an NSOperation subclass that manages an NSURLConnection instance and implements the NSURLConnectionDelegate protocol. Even though this robust foundation remains unchanged in AFNetworking 2.0, it is supplemented by the NSURLSession API, which I’ll cover in more detail later.

Separation of Responsibilities

In AFNetworking 1.x, the AFHTTPRequestOperation class was in charge of request and response serialization and validation. This is no longer true in AFNetworking 2.0. The AFHTTPRequestOperation class and its subclasses are no longer directly responsible for serializing and validating requests and responses. This responsibility has been moved to the and protocols. Each protocol declares a single delegate method to handle request and response serialization and validation respectively.

To make your life easier, however, AFNetworking 2.0 ships with AFHTTPRequestSerializer and AFHTTPResponseSerializer as well as a number of subclasses for common content types, such as JSON and XML. To see how this works in practice, let’s take a look at an example in which I query the Forecast API. This doesn’t look all that different from how you made a request with AFNetworking 1.x. The main difference is that you’ll be using the AFHTTPRequestOperation class more often. The AFJSONRequestOperation and AFXMLRequestOperation classes are no longer present in AFNetworking 2.0.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/37.8267,-122.423", key]];
// Initialize Request Operation
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:URL]];
// Configure Request Operation
[requestOperation setResponseSerializer:[AFJSONResponseSerializer serializer]];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    // Process Response Object
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    // Handle Error
}];
// Start Request Operation
[requestOperation start];

The AFJSONResponseSerializer class inherits from AFHTTPResponseSerializer and should be used for JSON responses. If you need to process XML responses, then AFXMLNResponseSerializer will help you out. For other content types, you need to subclass AFHTTPResponseSerializer.

AFHTTPRequestOperationManager

Another major change in AFNetworking 2.0 is the removal of the AFHTTPClient class, which was in charge of talking to web services. The responsibilities of AFHTTPClient have been split up into a number of classes and protocols. To talk to a web service, you now use AFHTTPRequestOperationManager and AFHTTPSessionManager. Just like AFHTTPRequestOperation, AFHTTPRequestOperationManager and AFHTTPSessionManager delegate serialization of requests and responses to separate objects. Both AFHTTPRequestOperationManager and AFHTTPSessionManager have a requestSerializer and responseSerializer property for this purpose. Let’s revisit the above example to see how this works in practice.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/", key]];
// Initialize Request Operation Manager
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
// Configure Request Operation Manager
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
// Send Request
[manager GET:@"37.8267,-122.423" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    // Process Response Object
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    // Handle Error
}];

What About NSURLSession?

AFURLSessionManager

The key question when discussing AFNetworking 2.0 in the light of the NSURLSession API is whether we still need AFNetworking. AFNetworking adds a number of convenience classes and methods to the NSURLSession API and this is only possible thanks to the separation of responsibilities I discussed earlier. The most significant improvement that AFNetworking provides on top of the NSURLSession API are the AFURLSessionManager and AFHTTPSessionManager classes.

The AFURLSessionManager helps you manage an NSURLSession object. Even though I love the NSURLSession API, I have to agree with Mattt that its API is incomplete. The AFURLSessionManager class adds a number of convenience methods that make the NSURLSession API even better. Serialization and validation, for example, are much easier and intuitive with AFNetworking. Just like the AFHTTPRequestOperationManager, instances of AFURLSessionManager have a requestSerializer and responseSerializer property that makes serialization of requests and responses seamless and intuitive.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/37.8267,-122.423", key]];
// Initialize Session Configuration
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
// Initialize Session Manager
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfiguration];
// Configure Manager
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
// Send Request
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[[manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    // Process Response Object
}] resume];

I hope you can see the benefit of using AFNetworking in combination with the NSURLSession API. It makes working with the NSURLSession API much more intuitive especially if you’re already used to the way AFNetworking 2.0 separates serialization and validation from the actual task or request operation.

AFHTTPSessionManager

AFNetworking 2.0 also incudes a subclass of AFURLSessionManager, AFHTTPSessionManager, which makes interacting with web services a breeze. The AFHTTPSessionManager class includes a number of conveniences methods, such as GET:parameters:success:failure: and POST:parameters:constructingBodyWithBlock:success:failure: that make the migration process from AFHTTPClient to AFHTTPSessionManager easy. Similar methods are also available in the AFHTTPRequestOperationManager class, which I discussed earlier.


Reachability

Reachability is managed by the AFURLRequestOperationManager and AFURLSessionManager classes. When instances of these classes have a valid baseURL, then a reachability manager is automatically instantiated and set up. Of course, it is also possible to explicitly create an instance of the AFNetworkReachabilityManager class.


Icing On The Cake

AFNetworking 2.0 has a number of other features, such as built-in support for SSL pinning and various categories on UIKit classes. Take a look at this example in which I leverage AFNetworking’s category on UIProgressView to update a progress view while downloading a remote image.

// Initialize Request
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]];
// Initialize Session Manager
self.sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
// Initialize Download Task
NSURLSessionDownloadTask *downloadTask = [self.sessionManager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    // Handle Success
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    // Handle Failure
}];
[self.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
[downloadTask resume];

Verdict

If your project doesn’t rely on networking, then you won’t need all the power and convenience that AFNetworking has to offer. As a matter of fact, I think it’s important that every iOS or OS X developer is familiar with NSURLSession and NSURLConnection, because they are key components of the platform.

Even though the NSURLSession API is great and easy to use, the networking APIs provided by the Foundation framework aren’t perfect. As Mattt points out, even the NSURLSession API has a few notable gaps. AFNetworking tries to elegantly fill these gaps. The combination of the NSURLSession API and AFNetworking is a marriage made in heaven. It truly is a joy to use.

I really like the NSURLSession API and I hope I’ve convinced you of its power in this series. However, this doesn’t mean that AFNetworking has become obsolete. In my opinion, AFNetworking is the perfect companion of the NSURLSession API. AFNetworking has matured and it is indeed the best and most robust networking library available. Its modular architecture means that you don’t need to include every class or category it has to offer. You are free to pick and choose from the components that it includes. AFNetworking supports CocoaPods subspecs, which make this process trivial.


Conclusion

I couldn’t be more happy with the second major release of AFNetworking. The library’s modularity and separation of responsibilities is what makes it shine in my opinion. It complements the NSURLSession API so well that you won’t even notice that you’re using AFNetworking. Mattt Thompson and the more than 130 contributors of AFNetworking have done a formidable job. It’s good to be a Cocoa developer.

Networking with NSURLSession: Part 2

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21581

In the previous tutorial, I introduced you to NSURLSession. I talked about the advantages it has over NSURLConnection and how to use NSURLSession for simple tasks, such as fetching data from a web service and downloading an image from the web. In this tutorial, we'll take a closer look at the configuration options of NSURLSession and how to cancel and resume a download task. We've got a lot of ground to cover so let's get started.


Session Configuration

As we saw in the previous tutorial, a session, an instance of NSURLSession, is a configurable container for putting network requests into. The configuration of the session is handled by an instance of NSURLSessionConfiguration.

A session configuration object is nothing more than a dictionary of properties that defines how the session it is tied to behaves. A session has one session configuration object that dictates cookie, security, and cache policies, the maximum number of connections to a host, resource and network timeouts, etc. This is a significant improvement over NSURLConnection, which relied on a global configuration object with much less flexibility.

Mutability

Once a session is created and configured by a NSURLSessionConfiguration instance, the session's configuration cannot be modified. If you need to modify a session's configuration, you have to create a new session. Keep in mind that it is possible to copy a session's configuration and modify it, but the changes have no effect on the session from which the configuration was copied.

Default Configuration

The NSURLSessionConfiguration class provides three factory constructors for instantiating standard configurations, defaultSessionConfiguration, ephemeralSessionConfiguration, and backgroundSessionConfiguration. The first method returns a copy of the default session configuration object, which results in a session that behaves similarly to an NSURLConnection object in its standard configuration. Altering a session configuration obtained through the defaultSessionConfiguration factory method doesn't change the default session configuration which it's a copy of.

Ephemeral Configuration

A session configuration object created by invoking the ephemeralSessionConfiguration factory method ensures that the resulting session uses no persistent storage for cookies, caches, or credentials. In other words, cookies, caches, and credentials are kept in memory. Ephemeral sessions are therefore ideal if you need to implement private browsing, something that simply wasn't possible before the introduction of NSURLSession.

Background Configuration

The backgroundSessionConfiguration: factory method creates a session configuration object that enables out-of-process uploads and downloads. The upload and download tasks are managed by a background daemon and continue to run even if the application is suspended or crashes. We'll talk more about background sessions later in this series.

Session Configuration

As we saw in the previous tutorial, creating a session configuration object is simple. In the example shown below, I used the defaultSessionConfiguration factory method to create a NSURLSessionConfiguration instance. Configuring a session configuration object is as simple as modifying its properties as shown in the example. We can then use the session configuration object to instantiate a session object. The session object serves as a factory for data, upload, and download tasks, with each task corresponding to a single request. In the example below, we query the iTunes Search API as we did in the previous tutorial.

// Create Session Configuration
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

// Configure Session Configuration
[sessionConfiguration setAllowsCellularAccess:YES];
[sessionConfiguration setHTTPAdditionalHeaders:@{ @"Accept" : @"application/json" }];

// Create Session
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

// Send Request
NSURL *url = [NSURL URLWithString:@"https://itunes.apple.com/search?term=apple&media=software"];
[[session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]);
}] resume];

The example also illustrates how easy it is to add custom headers by setting the HTTPAdditionalHeaders property of the session configuration object. The beauty of the NSURLSession API is that every request that passes through the session is configured by the session's configuration object. Adding authentication headers to a set of requests, for example, becomes easy as pie.


Canceling and Resuming Downloads

In the previous tutorial, I showed you how to download an image using the NSURLSession API. However, network connections are unreliable and it happens all too often that a download fails due to a flaky network connection. Fortunately, resuming a download isn't difficult with the NSURLSession API. In the next example, I'll show you how to cancel and resume the download of an image.

Before we take a closer look at resuming a download task, it is important to understand the difference between canceling and suspending a download task. It is possible to suspend a download task and resume it at a later time. Canceling a download task, however, stops the task and it isn't possible to resume it at a later time. There is one alternative, though. It is possible to cancel a download task by calling cancelByProducingResumeData: on it. It accepts a completion handler that accepts one parameter, an NSData object that is used to resume the download at a later time by invoking downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: on a session object. The NSData object contains the necessary information to resume the download task where it left off.

Step 1: Outlets and Actions

Open the project we created in the previous tutorial or download it here. We start by adding two buttons to the user interface, one to cancel the download and one to resume the download. In the view controller's header file, create an outlet and an action for each button as shown below.

#import <UIKit/UIKit.h>

@interface MTViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
@property (weak, nonatomic) IBOutlet UIButton *resumeButton;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

- (IBAction)cancel:(id)sender;
- (IBAction)resume:(id)sender;

@end

Step 2: User Interface

Open the project's main storyboard and add two buttons to the view controller's view. Position the buttons as shown in the screenshot below and connect each button with its corresponding outlet and action.

Update the user interface.

Step 3: Refactoring

We'll need to do some refactoring to make everything work correctly. Open MTViewController.m and declare one instance variable and two properties. The instance variable, session, will keep a reference to the session we'll use for downloading the image.

#import "MTViewController.h"

@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate> {
    NSURLSession *_session;
}

@property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask;
@property (strong, nonatomic) NSData *resumeData;

@end

We also need to refactor the viewDidLoad method, but first I'd like to implement a getter method for the session. Its implementation is pretty straightforward as you can see below. We create a session configuration object using the defaultSessionConfiguration factory method and instantiate the session object with it. The view controller serves as the session's delegate.

- (NSURLSession *)session {
    if (!_session) {
        // Create Session Configuration
        NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

        // Create Session
        _session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
    }

    return _session;
}

With the session accessor implemented, the viewDidLoad method becomes much simpler. We create a download task, as we did in the previous tutorial, and store a reference to the task in downloadTask. We then tell the download task to resume.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Create Download Task
    self.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]];

    // Resume Download Task
    [self.downloadTask resume];
}

Step 4: Canceling the Download

The cancel: action contains the logic for canceling the download task we just created. If downloadTask is not nil, we call cancelByProducingResumeData: on the task. This method accepts one parameter, a completion block. The completion block also takes one parameter, an instance of NSData. If resumeData is not nil, we store a reference to the data object in view controller's resumeData property.

If a download is not resumable, the completion block's resumeData parameter is nil. Not every download is resumable so it's important to check if resumeData is a valid NSData object.

- (IBAction)cancel:(id)sender {
    if (!self.downloadTask) return;

    // Hide Cancel Button
    [self.cancelButton setHidden:YES];

    [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
        if (!resumeData) return;
        [self setResumeData:resumeData];
        [self setDownloadTask:nil];
    }];
}

Step 5: Resuming the Download

Resuming the download task after it was canceled is easy. In the resume: action, we check if the view controller's resumeData property is set. If resumeData is a valid NSData object, we tell the session object to create a new download task and pass it the NSData object. This is all the session object needs to recreate the download task that we canceled in the cancel: action. We then tell the download task to resume and set resumeData to nil.

- (IBAction)resume:(id)sender {
    if (!self.resumeData) return;

    // Hide Resume Button
    [self.resumeButton setHidden:YES];

    // Create Download Task
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];

    // Resume Download Task
    [self.downloadTask resume];

    // Cleanup
    [self setResumeData:nil];
}

Build the project and run the application in the iOS Simulator or on a physical device. The download should start automatically. Tap the cancel button to cancel the download and tap the resume button to resume the download.

Step 6: Finishing Touches

There are a number of details we need to take care of. First of all, the buttons shouldn't always be visible. We'll use key value observing to show and hide the buttons when necessary. In viewDidLoad, hide the buttons and add the view controller as an observer of itself for the resumeData and downloadTask key paths.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Add Observer
    [self addObserver:self forKeyPath:@"resumeData" options:NSKeyValueObservingOptionNew context:NULL];
    [self addObserver:self forKeyPath:@"downloadTask" options:NSKeyValueObservingOptionNew context:NULL];

    // Setup User Interface
    [self.cancelButton setHidden:YES];
    [self.resumeButton setHidden:YES];

    // Create Download Task
    self.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]];

    // Resume Download Task
    [self.downloadTask resume];
}

In observeValueForKeyPath:ofObject:change:context:, we hide the cancel button if resumeData is nil and we hide the resume button if downloadTask is nil. Build the project and run the application one more time to see the result. This is better. Right?

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"resumeData"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.resumeButton setHidden:(self.resumeData == nil)];
        });
    } else if ([keyPath isEqualToString:@"downloadTask"]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.cancelButton setHidden:(self.downloadTask == nil)];
        });
    }
}
As George Yang points out in the comments, we don't know whether observeValueForKeyPath:ofObject:change:context: is called on the main thread. It is therefore important to update the user interface in a GCD (Grand Central Dispatch) block that is invoked on the main queue.

Step 7: Invalidating the Session

There is one key aspect of NSURLSession that I haven't talked about yet, session invalidation. The session keeps a strong reference to its delegate, which means that the delegate isn't released as long as the session is active. To break this reference cycle, the session needs to be invalidated. When a session is invalidated, active tasks are canceled or finished, and the delegate is sent a URLSession:didBecomeInvalidWithError: message and the session releases its delegate.

There are several places that we can invalidate the session. Since the view controller downloads only one image, the session can be invalidated when the download finishes. Take a look at the updated implementation of URLSession:downloadTask:didFinishDownloadingToURL:. The cancel button is also hidden when the download finishes.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    NSData *data = [NSData dataWithContentsOfURL:location];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self.cancelButton setHidden:YES];
        [self.progressView setHidden:YES];
        [self.imageView setImage:[UIImage imageWithData:data]];
    });

    // Invalidate Session
    [session finishTasksAndInvalidate];
}

Conclusion

The example project we created in this tutorial is a simplified implementation of how to cancel and resume downloads. In your applications, it may be necessary to write the resumeData object to disk for later use and it may be possible that several download tasks are running at the same time. Even though this adds complexity, the basic principles remain the same. Be sure to prevent memory leaks by always invalidating a session that you no longer need.

2014-01-13T12:30:22.000Z2014-01-13T12:30:22.000ZBart Jacobshttp://code.tutsplus.com/tutorials/networking-with-nsurlsession-part-2--mobile-21581

Introducing the New Mobile Development Editor: Bart Jacobs

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-22132

We're excited to announce a few changes to the Tuts+ team this week. After working as mobile editor for over three and a half years, we're saying goodbye to Mark Hammonds, and offering a warm welcome to Bart Jacobs!


Thanks to Mark Hammonds

Everyone at Tuts+ would like to thank Mark for everything he has brought to the team over the years and the incredible work he has done here for mobile development education. He has been here since the very beginning, but felt it was the right time to focus fully on his own mobile and web app development projects over at Bitmotive. We wish him well with everything he's moving on to!


Welcome to Bart Jacobs

bart

We are incredibly excited to officially introduce Bart Jacobs as our new Mobile Development Editor. Bart runs Code Foundry, a mobile and web development company based in Belgium. His primary area of expertise is iOS development, but he's also skilled in Web, JavaScript, Ruby, and Cocoa development, and an avid fan of film music and coffee—a winning combination! You can follow Bart on Twitter and read his tutorials on Tuts+.


The Future & Your Suggestions

We’re extremely excited to have Bart on board to take mobile development on Tuts+ into the future. There are lots of exciting new developments coming up in the next few months and Bart is the perfect person to make mobile development education even better. Be sure to give him a warm welcome!

We’d also love to take this opportunity to ask for your suggestions and ideas for the mobile development topics you’d like to see covered over the next few months. Let us know what you’d like to see, and how we can best serve your development education needs.

2014-01-15T05:59:41.000Z2014-01-15T05:59:41.000ZJoel Bankheadhttp://code.tutsplus.com/articles/introducing-the-new-mobile-development-editor-bart-jacobs--mobile-22132

Corona SDK: Create a Helicopter Obstacles Game

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21216

In this tutorial, I'll be showing you how to create an obstacles avoiding game using the Corona SDK. You'll learn more about touch controls, collision detection, and physics. The objective of the game is to avoid as many obstacles as you can to obtain the highest score. Let's get started.


1. Application Overview

App Overview

Using ready-made graphics, we'll create an entertaining game using the Lua programming language and the Corona SDK APIs. The player will be able to move a helicopter on the screen and needs to avoid any obstacles it comes across. You can modify the parameters in the code to customize the game.


2. Target Device

Target Device

The first thing we have to do is select the platform we want to run our application on so we're able to choose the size for the images we'll use.

The iOS platform has the following requirements:

  • iPad 1/2/Mini: 1024px x 768px, 132 ppi
  • iPad Retina: 2048px x 1536px, 264 ppi
  • iPhone/iPod Touch: 320px x 480px, 163 ppi
  • iPhone/iPod Retina: 960px x 640px, 326 ppi
  • iPhone 5/iPod Touch: 1136px x 640px, 326 ppi

Because Android is an open platform, there are many different devices and resolutions. A few of the more common screen characteristics are:

  • Asus Nexus 7 Tablet: 800px x 1280px, 216 ppi
  • Motorola Droid X: 854px x 480px, 228 ppi
  • Samsung Galaxy SIII: 720px x 1280px, 306 ppi

In this tutorial, we'll be focusing on the iOS platform in terms of graphics. In particular, we'll be developing for the iPhone and iPod Touch. However, the code of this tutorial can also be used in you target the Android platform.


3. Interface

Interface

We'll use a simple user interface involving multiple shapes, buttons, bitmaps, and more. The graphics that we'll use for this tutorial can be found in the project included with this tutorial.


4. Export Graphics

Export Graphics

Depending on the device you've selected, you may need to convert the graphics to the recommended resolution (ppi), which you can do in your favorite image editor. I used the Adjust Size… option in the Tools menu in the Preview application on OS X. Remember to give the images a descriptive name and save them in your project folder.


5. Application Configuration

We'll use a configuration file, config.lua, to make the application go full screen across devices. The configuration file shows the original screen size and the method used to scale the content in case the application is run on a different resolution.

application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = "letterbox"
    },
}

6.main.lua

Let's write the actual application. Open your preferred Lua editor. Any plain text editor will work, but it is recommended to use a text editor that has syntax highlighting. Create a new file and save it as main.lua in your project folder.


7. Code Structure

We'll structure our code as if it were a class. If you're familiar with ActionScript or Java, you should find the project structure familiar.

Necessary Classes

Variables and Constants

Declare Functions

    constructor (Main function)

    class methods (other functions)

call Main function

8. Hide Status Bar

display.setStatusBar(display.HiddenStatusBar)

This code snippet hides the status bar. The status bar is the bar at the top of the device's screen that shows the time, signal, and other indicators.


9. Import Physics

We'll use the physics library to handle collisions. Import the library using the code snippet shown below.

-- Physics

local physics = require('physics')
physics.start()

10. Background

Background

A simple background for the application's user interface. The code snippet below draws the background to the screen.

-- Graphics

-- [Background]

local gameBg = display.newImage('gameBg.png')

11. Title View

Title View

This is the title view. It's the first interactive screen to appear in our game. These variables store its components.

-- [Title View]

local title
local playBtn
local creditsBtn
local titleView

12. Credits View

Credits View

The credits view shows the credits and copyright of the application. This variable is used to store it.

-- [CreditsView]

local creditsView

13. Instructions Message

Instructions

A message with instructions will appear at the start of the game and it will disappear after the first tap.

-- Instructions

local ins

14. Helicopter

Helicopter

This is the helicopter graphic. The player will control the helicopter using touch controls.

-- Helicopter

local helicopter

15. Blocks

Blocks

The blocks are the obstacles the player needs to avoid.

-- Blocks

local blocks = {}

16. Alert

Alert

The alert is displayed when the player misses the ball and the game is over. It displays a message and ends the game.

-- Alert

local alertView

17. Sounds

Sounds

To liven up the game, we'll use sound effects. The sounds used in the game were obtained from as3soundfxr. I found the background music on playonloop.

-- Sounds

local bgMusic = audio.loadStream('POL-rocket-station-short.wav')
local explo = audio.loadSound('explo.wav')

18. Variables

The following code snippet shows the variables that we'll use. Read the comments to understand what each variable is used for.

-- Variables

local timerSrc --Blocks timer
local yPos = {90, 140, 180} --Possible positions for the blocks
local speed = 5 --Block speed
local speedTimer --Increases blocks speed
local up = false --Used to determine if helicopter is going up
local impulse = -60 --physics value to make helicopter go up

19. Declare Functions

Declare all functions as local at the start.

-- Functions

local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local createBlock = {}
local movePlayer = {}
local increaseSpeed = {}
local update = {}
local alert = {}

20. Constructor

Next, we create the function that will initialize the game logic.

function Main()
  -- code...
end

21. Add Title View

We start by placing the title view in the stage and call a function that will add tap listeners to the buttons.

function Main()
  titleBg = display.newImage('titleBg.png')
  playBtn = display.newImage('playBtn.png', 220, 178)
  creditsBtn = display.newImage('creditsBtn.png', 204, 240)
  titleView = display.newGroup(titleBg, playBtn, creditsBtn)

  startButtonListeners('add')
end

22. Start Button Listeners

The following function adds the necessary listeners to the TitleView's buttons.

function startButtonListeners(action)
  if(action == 'add') then
    playBtn:addEventListener('tap', showGameView)
    creditsBtn:addEventListener('tap', showCredits)
  else
    playBtn:removeEventListener('tap', showGameView)
    creditsBtn:removeEventListener('tap', showCredits)
  end
end

23. Show Credits

The credits screen is shown when the user taps the about button. A tap listener is added to the credits view to dismiss it when the user taps it.

function showCredits:tap(e)
  playBtn.isVisible = false
  creditsBtn.isVisible = false
  creditsView = display.newImage('credits.png', -110, display.contentHeight-80)
  transition.to(creditsView, {time = 300, x = 55, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end

24. Hide Credits

When the user taps the credits view, it is animated out of the stage and remove.

function hideCredits:tap(e)
  playBtn.isVisible = true
  creditsBtn.isVisible = true
  transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
end

25. Show Game View

When the play button is tapped, the title view is animated off the screen and the game is revealed. There are a number of moving parts so we'll take a closer look at each of them.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

26. Instructions Message

The following code snippet adds the instructions message.

ins = display.newImage('ins.png', 180, 270)
  transition.from(ins, {time = 200, alpha = 0.1, onComplete = function() timer.performWithDelay(2000, function() transition.to(ins, {time = 200, alpha = 0.1, onComplete = function() display.remove(ins) ins = nil end}) end) end})

27. Score TextField

The following code snippet creates a text field that shows the player's current score in the top-right of the stage.

-- TextFields

  scoreTF = display.newText('0', 450, 5, 'Marker Felt', 14)
  scoreTF:setTextColor(255, 255, 255)

28. Helicopter

Next, it is time to add the helicopter graphic to the stage as shown below.

-- Helicopter

  helicopter = display.newImage('helicopter.png', 23, 152)

29. Walls

In the following code snippet, we use the Corona graphics API to create several lines that we'll add to the physics simulation a bit later in this tutorial. They will be used to detect collisions with the helicopter.

-- Walls

  local top = display.newRect(0, 60, 480, 1)
  top:setFillColor(34, 34, 34)
  local bottom = display.newRect(0, 260, 480, 1)
  bottom:setFillColor(34, 34, 34)
end

30. Physics

Next, we need to add the necessary physics to each object.

-- Add physics

  physics.addBody(helicopter)
  physics.addBody(top, 'static')
  physics.addBody(bottom, 'static')

31. Start Game

We create a group for the blocks, invoke the gameListeners function, and start the background music.

  blocks = display.newGroup()
  gameListeners('add')
  audio.play(bgMusic, {loops = -1, channel = 1})
end

32. Game Listeners

The following code snippet may seem complex, but it simply adds a few listeners to start the game logic.

function gameListeners(action)
  if(action == 'add') then
    gameBg:addEventListener('touch', movePlayer)
    Runtime:addEventListener('enterFrame', update)
    timerSrc = timer.performWithDelay(1300, createBlock, 0)
    speedTimer = timer.performWithDelay(5000, increaseSpeed, 5)
    helicopter:addEventListener('collision', onCollision)
  else
    gameBg:addEventListener('touch', movePlayer)
    Runtime:removeEventListener('enterFrame', update)
    timer.cancel(timerSrc)
    timerSrc = nil
    timer.cancel(speedTimer)
    speedTimer = nil
    helicopter:removeEventListener('collision', onCollision)
  end
end

33. Create a Block

As its name indicates, the createBlock function creates a block and draws it to the screen. The resulting object is added to the physics engine to check for collisions.

function createBlock()
  local b
  local rnd = math.floor(math.random() * 4) + 1
  b = display.newImage('block.png', display.contentWidth, yPos[math.floor(math.random() * 3)+1])
  b.name = 'block'
  -- Block physics
  physics.addBody(b, 'kinematic')
  b.isSensor = true
  blocks:insert(b)
end

34. Move Function

In the movePlayer function, we update the up variable. As long as its value is equal to true, the update function moves the helicopter up.

function movePlayer(e)
  if(e.phase == 'began') then
    up = true
  end
  if(e.phase == 'ended') then
    up = false
    impulse = -60
  end
end

35. Increase Speed

To make the game more interesting, a timer increases the speed every five seconds. An icon is displayed to alert the player of change in speed.

function increaseSpeed()
  speed = speed + 2
  -- Icon
  local icon = display.newImage('speed.png', 204, 124)
  transition.from(icon, {time = 200, alpha = 0.1, onComplete = function() timer.performWithDelay(500, function() transition.to(icon, {time = 200, alpha = 0.1, onComplete = function() display.remove(icon) icon = nil end}) end) end})
end

36. Move Helicopter

The update function checks the value of up and moves the helicopter up if it is equal to true.

function update(e)
  -- Move helicopter up
  if(up) then
    impulse = impulse - 3
    helicopter:setLinearVelocity(0, impulse)
  end

37. Move Blocks

Next, it is time to move the blocks. We use the speed variable to determine how many pixels the blocks should be moved every frame.

-- Move Blocks
  if(blocks ~= nil)then
    for i = 1, blocks.numChildren do
      blocks[i].x = blocks[i].x - speed
    end
  end

38. Update Score

The algorithm to update the score is simple. The score is incremented by one every frame for as long as the helicopter hasn't crashed. Simple. Right?

  -- Score
  scoreTF.text = tostring(tonumber(scoreTF.text) + 1)
end

39. Collisions

The onCollision function verifies if the helicopter has collided with a block. If a collision was detected, we play a sound effect, an explosion, remove the graphics, and show an alert to the player.

function onCollision(e)
  audio.play(explo)
  display.remove(helicopter)
  alert()
end

40. Alert

The alert function creates an alert view and shows it to the user. The game then ends.

function alert()
  gameListeners('rmv')
  alert = display.newImage('alert.png', (display.contentWidth * 0.5) - 105, (display.contentHeight * 0.5) - 55)
  transition.from(alert, {time = 300, xScale = 0.5, yScale = 0.5})
end

41. Invoke Main Function

In order to start the game, the Main function needs to be invoked. With the rest of the code in place, we do that here.

Main()

42. Loading Screen

Loading Screen

On the iOS platform, the file named Default.png is displayed while the application is launching. Add this image to your project's source folder, it will be automatically added by the Corona compiler.


43. Icon

Icon

Using the graphics you created earlier, you can now create a nice icon. The dimensions of the icon size for a non-retina iPhone are 57px x 57px, while the retina version needs to be 114px x 114px. The artwork for iTunes is required to be 1024px x 1024px. I suggest creating the iTunes artwork first and then creating the smaller sized images by scaling the iTunes artwork down to the correct dimensions. There is no need to make the application icon glossy or add rounded corners as this is taken care of by the operating system for you.


44. Testing in Simulator

Testing

It's time to test our application in the simulator. Open the Corona Simulator, browse to your project folder, and click Open. If everything works as expected, you're ready for the final step.


45. Build Project

Build

In the Corona Simulator, go to File > Build and select the target device. Fill out the required fields and click Build. Wait a few seconds and your application is ready to test on a device and/or to be submitted for distribution.


Conclusion

In this tutorial, we've learned about touch listeners, collision detection, physics as well as a few other skills that can be useful in a wide number of games. Experiment with the final result and try to modify the game to create your own version of the game. I hope you liked this tutorial and found it helpful. Thank you for reading.

2014-01-15T15:23:00.000Z2014-01-15T15:23:00.000ZCarlos Yanezhttp://code.tutsplus.com/tutorials/corona-sdk-create-a-helicopter-obstacles-game--mobile-21216

Getting Started with Lua and the Corona SDK

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21457

In this tutorial, you will learn the basics of the Lua programming language and I will help you getting started with writing applications for iOS and Android. Excited? Let's dive right in.


Introduction

In this tutorial, we will take a look at the Corona SDK and the Lua programming language. Even though Lua isn't difficult to pick up, it is recommended to have some experience with other languages, such as JavaScript, PHP, Java, or Ruby. We will cover the very basics of the Corona SDK and Lua to get you familiar with developing for the Corona platform. You will see that it takes very little effort and code to get up and running. I'm ready when you are.


1. Introducing Lua and the Corona SDK

In the past, mobile developers faced a difficult predicament. Should they develop applications for iOS or Android? Most iOS developers use Objective-C, while Android developers use Java. Fortunately, we've got the Corona SDK and the Lua programming language, which enables cross platform mobile development. To put it simply, it means you can develop an application once and build it for iOS, Android, Kindle, and Nook.

The programming language we use when developing with the Corona SDK is Lua, which is moon in Portuguese. One of the main beneits of Lua, especially in combination with the Corona SDK, is that Lua is cross-platform as the language is written in C. Lua is not difficult to learn as you'll find out in this tutorial. Lua was created in 1993 by a small group of people at the Pontifical Catholic University of Rio de Janeiro, Brazil. Lua is open source software so you can freely use it in your projects. It is distributed under the MIT license.

The Corona SDK is developed and maintained by Corona Labs and is a commercial platform. There are several pricing plans to choose from. There's a free starter plan and paid plans starting from $19 per month.

For this tutorial, however, we'll be using the starter plan. Even though you can develop Corona applications on several platforms, in this tutorial I'll show you how to build applications using Windows and we'll build for the Android platform.


2. Setting Up Lua and the Corona SDK

Are you ready to get started with cross platform mobile development? Visit the developer portal of the Corona SDK, create an account, and download the Corona SDK. As I mentioned, in this tutorial I'll be using Windows, but you can follow along on OS X just as well. After the installation of the Corona SDK, open the Start menu and navigate to Corona SDK > Corona Simulator. You should see two windows as shown below.




3. Text Editors

Now that we have the Corona Simulator up and running, we need to get a text editor to write and edit Lua. I recommend Sublime Text 2, which is a free download. It's a great and popular text editor that support syntax highlighting and a boatload of other useful features. This is especially useful if you're writing large and complex applications. It supports Lua along with 43 other programming languages. Did I tell you Sublime Text if available on Windows, OS X as well as Linux? You can't go wrong with Sublime Text 2.



4. Writing a Hello World Application

Head back to the Corona Simulator, hit New Project, and choose a directory to store your project's files in. Select Blank as the project's template, Phone Preset for Upright Screen Size, and Upright as the Default Orientation. Click OK to finalize the project setup and navigate to the directory where you created your new project. You should find three files, build.settings, config.lua, and main.lua. The only file we'll need to edit is Main.lua. Open this file with your editor of choice and replace the file's contents with the code snippet below.

print("Hello World!");

Save the changes by pressing Ctrl+S and open the Corona Simulator window. Wait a minute. Nothing happened. That's perfectly normal. We need to check the other window that looks like command prompt. It should display Hello World! as shown below.


The reason why the text was only displayed in the Corona Terminal and not in the Corona Simulator is, because the print command is only used for the Lua programming language. It cannot be used to display the words on the screen of the Corona Simulator or a physical device. However, this basic print command will still be useful when we develop application, especially for debugging purposes.


5. Hello World - Take 2

We're going to create another Hello World! application. However, this time, we'll make it display the words in the Corona Simulator itself. Delete the contents of main.lua and replace it with the code snippet shown below.

display.newText("Hello World!", 0, 0, native.systemFont, 16);

You may have noticed that this snippet was a bit longer than the previous one. Let's see what this piece of code does for us.

  • display is the object we're talking to.
  • newText is the function that we use to display the text on the screen.
  • "Hello@ World!" is the text we want to display.
  • 0, 0 are the x and y coordinates respectively.
  • native.systemFont is the font we use for the text and 16 is the font's size.

If you save the changes and relaunch the Corona Simulator, you should see the following.



6. Variables and Math

What if you wanted to store a number as a variable for later use? The following code snippet shows how variables are declared in Lua.

local num1 = 3 + 3;
  • local is the keyword for declaring a variable.
  • num1 is the name of the variable.

If we combine this with the previous code snippet, we get the following.

local num1 = 3 + 3;
display.newText(num1, 0, 0, native.systemFont, 32);

The Corona Simulator should now display the number 6, which is the result of adding 3 and 3. Let's try another example using math. Using the following code snippet, the Corona Simulator should display the number 18.

local num1 = 3 + 3;
display.newText(num1 * 3, 0, 0, native.systemFont, 32);

As you can see, it is perfectly possible to perform mathematical operations on a variable. In the above code snippet, we multiplied num1 by 3 using * 3. I'm sure you've already figured out that the asterisk is the multiplication operator in Lua.

  • + for addition
  • - for subtraction and negative numbers
  • * for multiplication
  • / for division

7. Images

Displaying images isn't difficult either. To display an image, you need to add the image to the directory where main.lua sits. It is fine to create a subdirectory to keep the project's resources separated and organized. Let's do that now. Create a new directory in your project folder and name it images. Use the images directory to store your project's images. The image I'd like to use for this tutorial is logo1.png and I've placed it in the images directory we created a moment ago.

As you can see in the following code snippet, displaying an image is almost as easy as displaying text. I dare to say it's even easier as you don't need to specify a font.

local photo1 = display.newImage("images/logo1.png", 0, 0);


8. Status Bar

If you look closely at the previous screenshot, you'll notice that there's a status bar at the top of the screen displaying the carrier, battery life, etc. Have you ever noticed that sometimes, when you open an application, games in particular, the status bar automatically disappears? Hiding the status bar is as simple as adding one line of code to main.lua. It's a simple as that. Update your project and take a look at the result in the Corona Simulator.

display.setStatusBar(display.HiddenStatusBar);

It is useful to know that the status bar can have different styles. The names of the styles speak for themselves. For many applications, especially games, using HiddenStatusBar is most suitable.

display.setStatusBar(display.DefaultStatusBar);
display.setStatusBar(display.DarkStatusBar);
display.setStatusBar(display.TranslucentStatusBar);


9. Rectangles, Borders, and Colors

Let's move on with shapes. The first shape we'll display is a rectangle. Let's see what it takes to display a rectangle on the screen.

local rect1 = display.newRect(10, 20, 150, 50);
  • local rect1 declares a variable for the rectangle.
  • display.newRectcreates the rectangle shape.
  • (10, 20, 150, 50) define the x and y coordinates and the width and height, respectively.

Let's add some color to the rectangle.

rect1:setFillColor(51, 255, 0);

Hmmm. What does this mean?

  • rect1 is the variable we declared earlier.
  • setFillColor is the method we use to fill the rectangle with a color.
  • (51, 255, 0) specify the red (51), green (255), and blue (0) value of the color we use.

Let's expand this example with a border or stroke as shown in the following code snippet.

rect1.strokeWidth = 8;
rect1:setStrokeColor(80, 200, 130);
  • rect1.strokeWidth = 8 sets the strokeWidth property of the rectangle to 8.
  • rect1.setStrokeColor(80, 200, 130) sets the strokeColor property of the rectangle to the color specified by the values 80, 200, and 130 as we saw earlier.


10. Comments

Comments may seem trivial and even obsolete, but they are important and even more so when you work in a team. Comments are very useful for documenting code and this applies both for you as your colleagues. It improves the readability of code for other people in your team. In Lua, comments are easy to use. Take a look at the following example.

--this is a comment

Comments have no effect on your application in terms of how it works. They are only there for the developer. The following code snippet will not print Hello World! to the terminal.

--print("Hello World!")

You can also write comments that span several lines, which is useful if you need to explain how a particularly complex piece of code works or if you want to write an introduction to an application or project.

--[[
This is comment
spans
several lines.
]]

Conclusion

In this tutorial, you've learned the basics of Lua and the Corona SDK. We installed and set up the Corona SDK, downloaded and used a text editor for editing Lua, wrote several applications, and ran them in the Corona Simulator. We also learned how to use variables to store pieces of data, display images on the screen, configure the status bar, and draw shapes to the screen. And, last but not least, we saw how to use comments and why you should use comments in your code. I hope that this tutorial was helpful to you. Stay tuned for more.

If you'd like to learn more about the Corona SDK, I recommend visiting the Corona Labs developer website. It is filled with resources and guides to get you started. You can also explore the example applications that are included in the Corona SDK that you downloaded and installed earlier.

The Lua programming language also has its own website. It contains everything you need to know about the language, including a getting started guide and a very, very detailed manual. Make sure to visit the Lua website if you decide to continue with Corona development.

2014-01-17T12:30:44.000Z2014-01-17T12:30:44.000ZRyan Changhttp://code.tutsplus.com/tutorials/getting-started-with-lua-and-the-corona-sdk--mobile-21457

Working with NSURLSession: Part 3

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21879

In the previous tutorials, we explored the fundamentals of the NSURLSession API. There is one other feature of the NSURLSession API that we haven't look into yet, that is, out-of-process uploads and downloads. In the next two tutorials, I will show you how to create a very simple podcast client that enables background downloads.


Introduction

The podcast client that we're about to create isn't really going to be that functional. It will allow the user to query the iTunes Search API for a list of podcasts, select a podcast, and download episodes. Since we are focusing on the NSURLSession API, we won't go into playing the episodes the application downloads.

The project, however, will teach you how to use data tasks and download tasks in a real world application. The podcast client will also enable background downloads for which we'll leverage NSURLSession's out-of-process API. We have quite a few things to do so let's not waste time and get started.


1. Project Setup

Fire up Xcode 5, select New > Project... from the File menu, and choose the Single View Application template from the list of iOS application templates. Name the application Singlecast, set the Device Family to iPhone, and tell Xcode where you'd like to save the project. Hit Create to create the project.

Create the project in Xcode.

Configure the project in Xcode.

2. Update Storyboard

The first thing we need to do is edit the project's main storyboard. Open Main.storyboard, select the storyboard's only view controller, and choose Embed In > Navigation Controller from the Editor menu. The reason for embedding the view controller in a navigation controller will become clear later in this tutorial.

Embed the view controller in a navigation controller.

3. Search View Controller

Step 1: Create Class Files

As I mentioned in the introduction, to keep things simple, the user will only be able to subscribe to one podcast. Let's start by creating the search view controller. Select New > File... from the File menu and choose Objective-C class from the options on the right. Name the class MTSearchViewController and make it a subclass of UIViewController. Leave the check box labeled With XIB for user interface unchecked. Tell Xcode where you want to save the class files and hit Create.

Create the files for the MTSearchViewController class.

Step 2: Update Class Interface

Before we create the user interface, open the view controller's header file and update the class's interface as shown below. We specify that the MTSearchViewController class conforms to the UITableViewDataSource, UITableViewDelegate, and UISearchBarDelegate protocols, we declare two outlets, searchBar and tableView as well as an action, cancel, to dismiss the search view controller.

#import <UIKit/UIKit.h>

@interface MTSearchViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate>

@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
@property (weak, nonatomic) IBOutlet UITableView *tableView;

- (IBAction)cancel:(id)sender;

@end

Step 3: Create User Interface

Revisit the project's main storyboard and drag a new view controller from the Object Library on the right. Select the new view controller, open the Identity Inspector on the right, and set the view controller's class to MTSearchViewController. With the new view controller still selected, open the Editor menu and choose Embed In > Navigation Controller. Drag a table view to the view controller's view and connect the table view's dataSource and delegate outlets with the search view controller.

Add a table view to the search view controller's view and connect its dataSource and delegate outlets.

With the table view still selected, open the Attributes Inspector, and set the number of prototype cells to 1. Select the prototype cell and set its style property to Subtitle and its identifier to SearchCell.

Create and configure a prototype cell.

Drag a search bar from the Object Library and add it to the table view's header view. Select the search bar and connect its delegate outlet with the view controller.

Add a search bar to the table view's header view and connect the delegate outlet.

Select the view controller and connect its searchBar and tableView outlets with the search bar and table view respectively. There are a few other things that we need to do before we're done with the storyboard.

Open the Object Library and drag a bar button item to the navigation bar. Select the bar button item, connect it with the cancel: action we declared in the search view controller's interface, and change its Identifier in the Attributes Inspector to Cancel.

Add a bar button item to the search view controller.

Drag a bar button item to the navigation bar of the view controller (not the search view controller) and change its Identifier in the Attributes Inspector to Add. Control drag from the bar button item to the search view controller's navigation controller and select modal from the menu that pops up. This creates a segue from the view controller to the search view controller's navigation controller.

If you were to control drag from the view controller's bar button item directly to the search view controller instead of its navigation controller, the navigation controller would never be instantiated and you wouldn't see a navigation bar at the top of the search view controller.

Create a segue from the view controller to the search view controller's navigation controller.

Step 4: Table View Implementation

Before we implement the UITableViewDataSource and UITableViewDelegate protocols in the MTSearchViewController class, we need to declare a property that stores the search results we'll get back from the iTunes Search API. Name the property podcasts as shown below. We also declare a static string that will serve as a cell reuse identifier. It corresponds to the identifier we set on the prototype cell a few moments ago.

#import "MTSearchViewController.h"

@interface MTSearchViewController ()

@property (strong, nonatomic) NSMutableArray *podcasts;

@end
static NSString *SearchCell = @"SearchCell";

The implementation of numberOfSectionsInTableView: is as easy as it gets. We return 1 if self.podcasts is not nil and 0 if it is. The implementation of tableView:numberOfRowsInSection: is pretty similar as you can see below. In tableView:cellForRowAtIndexPath:, we ask the table view for a cell by passing the cell reuse identifier, which we declared earlier, and indexPath. We fetch the corresponding item from the podcasts data source and update the table view cell. Both tableView:canEditRowAtIndexPath: and tableView:canMoveRowAtIndexPath: return NO.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.podcasts ? 1 : 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.podcasts ? self.podcasts.count : 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SearchCell forIndexPath:indexPath];

    // Fetch Podcast
    NSDictionary *podcast = [self.podcasts objectAtIndex:indexPath.row];

    // Configure Table View Cell
    [cell.textLabel setText:[podcast objectForKey:@"collectionName"]];
    [cell.detailTextLabel setText:[podcast objectForKey:@"artistName"]];

    return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}

Before running the application, implement the cancel: action in which we dismiss the search view controller.

- (IBAction)cancel:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

Build the project and run the application to make sure that the foundation is working as expected. It's time to start using the NSURLSession API to query the iTunes Search API.

Step 5: Creating a Session

Let's begin by declaring two additional private properties in the MTSearchViewController class, session and dataTask. The session variable is used to store a reference to the NSURLSession instance we'll be using for querying Apple's API. We also keep a reference to the data task that we will use for the request. This will enable us to cancel the data task if the user updates the search query before we've received a response from the API. If you have an eye for detail, you may have noticed that the MTSearchViewController class also conforms to the UIScrollViewDelegate protocol. The reason for this will become clear in a few minutes.

#import "MTSearchViewController.h"

@interface MTSearchViewController () <UIScrollViewDelegate>

@property (strong, nonatomic) NSURLSession *session;
@property (strong, nonatomic) NSURLSessionDataTask *dataTask;

@property (strong, nonatomic) NSMutableArray *podcasts;

@end

The session is created in its getter method as you can see below. Its implementation shouldn't hold any surprises if you've read the previous tutorials. We override the getter method of the session property to lazily load the session and confine the session's instantiation and configuration in its getter method. This makes for clean and elegant code.

- (NSURLSession *)session {
    if (!_session) {
        // Initialize Session Configuration
        NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

        // Configure Session Configuration
        [sessionConfiguration setHTTPAdditionalHeaders:@{ @"Accept" : @"application/json" }];

        // Initialize Session
        _session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
    }

    return _session;
}

Step 6: Searching

To respond to the user's input in the search bar, we implement searchBar:textDidChange: of the UISearchBarDelegate protocol. The implementation is simple. If searchText is nil, the method returns early. If the length of searchText is less than four characters long, we reset the search by invoking resetSearch. If the query is four characters or longer, we perform a search by calling performSearch on the search view controller.

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
    if (!searchText) return;

    if (searchText.length <= 3) {
        [self resetSearch];

    } else {
        [self performSearch];
    }
}

Before we inspect performSearch, let's take a quick look at resetSearch. All that we do in resetSearch is clearing the contents of podcasts and reloading the table view.

- (void)resetSearch {
    // Update Data Source
    [self.podcasts removeAllObjects];

    // Update Table View
    [self.tableView reloadData];
}

The heavy lifting is done in performSearch. After storing the user's input in a variable named query, we check if dataTask is set. If it is set, we call cancel on it. This is important as we don't want to receive a response from an old request that may no longer be relevant to the user. This is also the reason why we have only one active data task at any one time. There is no advantage in sending multiple requests to the API.

Next, we ask the session for a new data task instance by passing it an NSURL instance and a completion handler. Remember that the session is the factory that creates tasks. You should never create tasks yourself. If we get a valid data task from the session, we call resume on it as we saw in the previous tutorials.

The logic inside the completion handler is interesting to say the least. The error object is important to us for several reasons. Not only will it tell us if something went wrong with the request, but it's also useful for determining if the data task was canceled. If we do get an error object, we check whether its error code is equal to -999. This error code indicates the data task was canceled. If we get another error code, we log the error to the console. In a real application, you'd need to improve the error handling and notify the user when an error is thrown.

If no error was passed to the completion handler, we create a dictionary from the NSData instance that was passed to the completion handler and we extract the results from it. If we have an array of results to work with, we pass it to processResults:. Did you notice we invoked processResults: in a GCD (Grand Central Dispatch) block? Why did we do that? I hope you remember, because it's a very important detail. We have no guarantee that the completion handler is invoked on the main thread. Since we need to update the table view on the main thread, we need to make sure that processResults: is called on the main thread.

- (void)performSearch {
    NSString *query = self.searchBar.text;

    if (self.dataTask) {
        [self.dataTask cancel];
    }

    self.dataTask = [self.session dataTaskWithURL:[self urlForQuery:query] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            if (error.code != -999) {
                NSLog(@"%@", error);
            }

        } else {
            NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
            NSArray *results = [result objectForKey:@"results"];

            dispatch_async(dispatch_get_main_queue(), ^{
                if (results) {
                    [self processResults:results];
                }
            });
        }
    }];

    if (self.dataTask) {
        [self.dataTask resume];
    }
}

Before we look at the implementation of processResults:, I want to quickly show you what happens in urlForQuery:, the helper method we use in performSearch. In urlForQuery:, we replace any spaces with a + sign to ensure that the iTunes Search API is happy with what we send it. We then create an NSURL instance with it and return it.

- (NSURL *)urlForQuery:(NSString *)query {
    query = [query stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    return [NSURL URLWithString:[NSString stringWithFormat:@"https://itunes.apple.com/search?media=podcast&entity=podcast&term=%@", query]];
}

In processResults:, the podcasts variable is cleared, populated with the contents of results, and the results are displayed in the table view.

- (void)processResults:(NSArray *)results {
    if (!self.podcasts) {
        self.podcasts = [NSMutableArray array];
    }

    // Update Data Source
    [self.podcasts removeAllObjects];
    [self.podcasts addObjectsFromArray:results];

    // Update Table View
    [self.tableView reloadData];
}

Step 6: Selecting a Podcast

When the user taps a row in the table view to select a podcast, tableView:didSelectRowAtIndexPath: of the UITableViewDelegate protocol is invoked. Its implementation may seem odd at first so let me explain what's going on. We select the podcast that corresponds with the user's selection, store it in the application's user defaults database, and dismiss the search view controller. We don't notify anyone about this? Why we do this will become clear once we continue implementing the MTViewController class.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    // Fetch Podcast
    NSDictionary *podcast = [self.podcasts objectAtIndex:indexPath.row];

    // Update User Defatuls
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    [ud setObject:podcast forKey:@"MTPodcast"];
    [ud synchronize];

    // Dismiss View Controller
    [self dismissViewControllerAnimated:YES completion:nil];
}

Step 7: Finishing Touches

There are two details I want to talk about before returning to the MTViewController class. When the search view controller is presented to the user, it is clear that she wants to search for podcasts. It is therefore a good idea to immediately present the keyboard. We do this in viewDidAppear: as shown below.

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // Show Keyboard
    [self.searchBar becomeFirstResponder];
}

The keyboard needs to hide the moment the user starts scrolling through the search results. To accomplish this, we implement scrollViewDidScroll: of the UIScrollViewDelegate protocol. This explains why MTSearchViewController conforms to the UIScrollViewDelegate protocol. Have a look at the implementation of scrollViewDidScroll: shown below.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if ([self.searchBar isFirstResponder]) {
        [self.searchBar resignFirstResponder];
    }
}
The UITableView class is a subclass of UIScrollView, which is the reason the above approach works.

4. Looping Back

As we saw earlier, we store the user's selection in the application's user defaults database. We need to update the MTViewController class to make use of the user's selection in the search view controller. In the view controller's viewDidLoad method, we load the podcast from the user defaults database and we add the view controller as an observer of the user defaults database for the key path MTPodcast so that the view controller is notified when the value for MTPodcast changes.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Load Podcast
    [self loadPodcast];

    // Add Observer
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"MTPodcast" options:NSKeyValueObservingOptionNew context:NULL];
}

All we do in loadPodcast is storing the value for MTPodcast from the user defaults database in the view controller's podcast property. This value will be nil if the user defaults database doesn't contain an entry for MTPodcast. The view controller will gracefully handle this for us. Remember that, in Objective-C, you can send messages to nil without all hell breaking loose. This has its disadvantages, but it certainly has its advantages to.

- (void)loadPodcast {
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    self.podcast = [ud objectForKey:@"MTPodcast"];
}

This also means that we need to declare a property named podcast in the view controller's implementation file.

#import "MTViewController.h"

@interface MTViewController ()

@property (strong, nonatomic) NSDictionary *podcast;

@end

Let's also take a quick look at setPodcast: and updateView.

- (void)setPodcast:(NSDictionary *)podcast {
    if (_podcast != podcast) {
        _podcast = podcast;

        // Update View
        [self updateView];
    }
}
- (void)updateView {
    // Update View
    self.title = [self.podcast objectForKey:@"collectionName"];
}

When the value in the user defaults database changes for the key MTPodcast, the view controller can respond to this change in observeValueForKeyPath:ofObject:change:context:. That's how key value observing works. All we do in this method is updating the value of the view controller's podcast property.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"MTPodcast"]) {
        self.podcast = [object objectForKey:@"MTPodcast"];
    }
}

When working with key value observing, it is instrumental to be aware of memory management and retain cycles. In this case, it means that we need to remove the view controller as an observer when the view controller is deallocated.

- (void)dealloc {
    [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"MTPodcast"];
}

5. Fetching and Parsing the Feed

Step 1: Adding Dependencies

The response we get back from the iTunes Search API includes a feedUrl attribute for each podcast. We could manually fetch the feed and parse it. However, to save some time, we'll make use of MWFeedParser, a popular library that can do this for us. You can manually download and include the library in your project, but I am going to opt for Cocoapods. I prefer Cocoapods for managing dependencies in iOS and OS X projects. You can read more about Cocoapods on its website or on Mobiletuts+.

I am going to assume the Cocoapods gem is installed on your system. You can find detailed instructions in this tutorial.

Quit Xcode, navigate to the root of your Xcode project, and create a file named Podfile. Open this file in your text editor of choice and add the following three lines of code. In the first line, we specify the platform and the deployment target, which is iOS 7 in this example. The next two lines each specify a dependency of our Xcode project. The first one is the MWFeedParser library and I've also included the popular SVProgressHUD library, which will come in handy a bit later.

platform :ios, '7'

pod 'MWFeedParser'
pod 'SVProgressHUD'

Open a Terminal window, navigate to the root of your Xcode project, and execute the command pod install. This should install the dependencies and create an Xcode workspace. When Cocoapods is finished installing the project's dependencies, it tells you to use the workspace it created for you. This is important so don't ignore this advice. In the root of your Xcode project, you will see that Cocoapods has indeed created an Xcode workspace for you. Double-click this file and you should be ready to go.

Install the project's dependencies using Cocoapods.

Step 2: Fetching and Parsing the Feed

Open the implementation file of the MTViewController class, add an import statement for MWFeedParser and SVProgressHUD, and declare two properties, episodes and feedParser. We also need to make MTViewController conform to the MWFeedParserDelegate protocol.

#import "MTViewController.h"

#import "MWFeedParser.h"
#import "SVProgressHUD.h"

@interface MTViewController () <MWFeedParserDelegate>

@property (strong, nonatomic) NSDictionary *podcast;
@property (strong, nonatomic) NSMutableArray *episodes;
@property (strong, nonatomic) MWFeedParser *feedParser;

@end

Next, we update setPodcast: by invoking fetchAndParseFeed, a helper method in which we use the MWFeedParser class to fetch and parse the podcast's feed.

- (void)setPodcast:(NSDictionary *)podcast {
    if (_podcast != podcast) {
        _podcast = podcast;

        // Update View
        [self updateView];

        // Fetch and Parse Feed
        [self fetchAndParseFeed];
    }
}

In fetchAndParseFeed, we get rid of our current MWFeedParser instance if we have one and initialize a new instance with the podcast's feed URL. We set the feedParseType property to ParseTypeFull and set the view controller as the feed parser's delegate. Before we fetch the feed, we use SVProgressHUD to show a progress HUD to the user.

- (void)fetchAndParseFeed {
    if (!self.podcast) return;

    NSURL *url = [NSURL URLWithString:[self.podcast objectForKey:@"feedUrl"]];
    if (!url) return;

    if (self.feedParser) {
        [self.feedParser stopParsing];
        [self.feedParser setDelegate:nil];
        [self setFeedParser:nil];
    }

    // Clear Episodes
    if (self.episodes) {
        [self setEpisodes:nil];
    }

    // Initialize Feed Parser
    self.feedParser = [[MWFeedParser alloc] initWithFeedURL:url];

    // Configure Feed Parser
    [self.feedParser setFeedParseType:ParseTypeFull];
    [self.feedParser setDelegate:self];

    // Show Progress HUD
    [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];

    // Start Parsing
    [self.feedParser parse];
}

We also need to implement two methods of the MWFeedParserDelegate protocol, feedParser:didParseFeedItem: and feedParserDidFinish:. In feedParser:didParseFeedItem:, we initialize the episodes property if necessary and pass it the feed item that the feed parser hands to us.

- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item {
    if (!self.episodes) {
        self.episodes = [NSMutableArray array];
    }

    [self.episodes addObject:item];
}

In feedParserDidFinish:, we dismiss the progress HUD and update the table view. Did you say table view? That's right. We need to add a table view and implement the necessary UITableViewDataSource protocol methods.

- (void)feedParserDidFinish:(MWFeedParser *)parser {
    // Dismiss Progress HUD
    [SVProgressHUD dismiss];

    // Update View
    [self.tableView reloadData];
}

Step 3: Displaying the Feed

Before we update the user interface, open MTViewController.h, declare an outlet for the table view, and tell the compiler the MTViewController class conforms to the UITableViewDataSource and UITableViewDelegate protocols.

#import <UIKit/UIKit.h>

@interface MTViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

Open the main storyboard one more time and add a table view to the view controller's view. Connect the table view's dataSource and delegate outlets with the view controller and connect the view controller's tableView outlet with the table view. Select the table view, open the Attributes Inspector, and set the number of prototype cells to 1. Select the prototype cell, set its style to Subtitle, and give it an identifier of EpisodeCell.

Adding a table view to the view controller.

Before we implement the UITableViewDataSource protocol, declare a static string named EpisodeCell in MTViewController.m. This corresponds with the identifier we set for the prototype cell in the storyboard.

static NSString *EpisodeCell = @"EpisodeCell";

Implementing the UITableViewDataSource protocol is simple as pie and very similar to how we implemented the protocol in the search view controller. The only difference is that the episodes variable contains instances of the MWFeedItem class instead of NSDictionary instances.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.episodes ? 1 : 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.episodes ? self.episodes.count : 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:EpisodeCell forIndexPath:indexPath];

    // Fetch Feed Item
    MWFeedItem *feedItem = [self.episodes objectAtIndex:indexPath.row];

    // Configure Table View Cell
    [cell.textLabel setText:feedItem.title];
    [cell.detailTextLabel setText:[NSString stringWithFormat:@"%@", feedItem.date]];

    return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return NO;
}

Run the application in the iOS Simulator or on a physical device and run it through its paces. You should now be able to search for podcasts, select a podcast from the list, and see its episodes.


Conclusion

We've done a lot in this tutorial, but we still have quite a bit of work in front of us. In the next tutorial, we zoom in on downloading episodes from the feed and we'll discuss background or out-of-process downloads. Stay tuned.

2014-01-20T12:30:11.000Z2014-01-20T12:30:11.000ZBart Jacobshttp://code.tutsplus.com/tutorials/working-with-nsurlsession-part-3--mobile-21879

Consuming Web Services with kSOAP

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21242

In this tutorial, you'll learn how to consume web services using the popular kSOAP library in an Android application. kSOAP is a reputable library loved by developers who have the need to parse WSDL (Web Service Definition Language) and SOAP (Simple Object Access Protocol) messages.


Introduction

The kSOAP library is an elegant, lightweight, and efficient SOAP client library. The library is open source, easy to use, and it can save you from the hassle of coding a custom parser. It is one of the more trusted SOAP libraries currently available and it's frequently updated, which is a good sign for any open source project.

It is often risky to integrate open source software (OSS) in a project as it can cause unforeseen problems down the road. Always look for comments and reviews of other developers who have used the library. If the project isn't under active development, it may be better to look for an alternative solution.

The objective of this tutorial is to make you familiar with the kSOAP library. For demonstration purposes, we'll be using a simple web service from W3Schools. The web service is a Fahrenheit to Celsius converter. The web service accepts a value in degrees Fahrenheit and responds with the equivalent value in degrees Celsius. We'll go through the example step by step. At the end of this tutorial, we'll display the result on the user's device.


1. Getting Started

Step 1: Project Setup

Start a new Android project and configure it as you please. Feel free to use your favorite IDE, but for this tutorial I'll be using IntelliJ IDEA.

In the project's manifest file, you need to specify that the application is allowed to connect to the internet. We're also going to specify the target SDK version as well as the minimum SDK version. Take a look at the manifest file shown below.

<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17"/><uses-permission android:name="android.permission.INTERNET"/>

Step 2: Downloading kSOAP

Always try and use the latest stable release of a library or upgrade the library you're using in an application after a major update has been released. It's good practice to keep a project's dependencies up to date for various reasons, such as security and bug fixes. In this tutorial, we'll be using version 3.1.1 of the kSOAP library, which you can find on the project's download page. After downloading the kSOAP library, add it to your project's libs folder.

Step 3: Adding kSOAP to Your Project

In order to use the kSOAP library in your project, you'll need to add it to your project. I'll show you how to add it using IntelliJ IDEA. The steps might be slightly different if you're using a different IDE, but the idea is the same. In IntelliJ IDEA, choose Project Structure... from the File menu, open the Modules pane, click the plus button at the bottom of the right pane, and select library. Navigate to the libs folder and select the kSOAP library. Take a look at the two images below for clarification.



The kSOAP library should now be visible as a dependency of your project. Click the check box next to the kSOAP library to add it to your project. Now that we've added the library to our project, it is time to put it to use. If you're using IntelliJ IDEA, your project should look similar to the one shown below.



2.W3Schools Web Service

Using the kSOAP library to consume a web service involves a number of steps. However, before we dive head first into using the kSOAP library, it is useful to tell you a bit more about the web service we'll be using.

Visit the website of the W3Schools web service that we'll use in this tutorial. You'll see that there are two operations, CelsiusToFahrenheit and FahrenheitToCelsius. The name of each operation is self-explanatory. The web service's URL is the base URL that we'll use to connect to the web service.

If you select an operation on the W3Schools website, you're shown an example of the request that the web service expects as well as the response of the web service. Take a look at the code snippet below, which is an example request that the web service expects. Pay close attention to the SOAPAction in the code snippet. We will use it a bit later in this tutorial.

POST /webservices/tempconvert.asmx HTTP/1.1
Host: www.w3schools.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://www.w3schools.com/webservices/FahrenheitToCelsius"<!--?xml version="1.0" encoding="utf-8"?-->
string

The next code snippet shows an example response of the web service.

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length<!--?xml version="1.0" encoding="utf-8"?-->
string

3. Using kSOAP

Step 1: Creating an Envelope

The first thing we need to do is creating a SOAP envelope using the SoapSerializationEnvelope class (org.ksoap2.serialization.SoapSerializationEnvelope), which you need to import from the kSOAP library. Take a look at the code snippet below, in which I have initialized an instance of the SoapSerializationEnvelope class.

SoapSerializationEnvelope envelope = getSoapSerializationEnvelope(request);

The getSoapSerializationEnvelope method isn't defined in the kSOAP library. It's a helper method that I've created that to make working with the kSOAP library a bit easier. The method returns the SOAP envelope that we need for the rest of the example. Take a look at the implementation of getSoapSerializationEnvelope below.

private final SoapSerializationEnvelope getSoapSerializationEnvelope(SoapObject request) {
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.dotNet = true;
    envelope.implicitTypes = true;
    envelope.setAddAdornments(false);
    envelope.setOutputSoapObject(request);

    return envelope;
}

The getSoapSerializationEnvelope method accepts a SoapObject instance, which is the request. We'll see how to create the request in just a few minutes. When creating an instance of the SoapSerializationEnvelope class, the SoapEnvelope version is set by passing in SoapEnvelope.VER11, which tells the kSOAP library that we'll be using SOAP 1.1. We set the envelope's dotNet property to true as the web service we'll be consuming runs on Microsoft's .NET framework.

envelope.dotNet = true;

Step 2: Configuring the Envelope

It is now time to configure the SOAP envelope by setting the request information. Start by importing the SoapObject class (org.ksoap2.serialization.SoapObject) and take a look at the code snippet below to see how to configure the envelope. We start by creating an instance of the SoapObject class, which requires two parameters, a namespace and a method name. You can add additional properties to the request using the addProperty method as shown below. In our example, I use addProperty to add the value in degrees Fahrenheit to the request.

String methodname = "FahrenheitToCelsius";
SoapObject request = new SoapObject(NAMESPACE, methodname);
request.addProperty("Fahrenheit", fValue);

You may be wondering where NAMESPACE is coming from. It's a private static string that is defined elsewhere in the application as you can see below.

private static final String NAMESPACE = "http://www.w3schools.com/webservices/";

Step 3: Creating the Request

To send the request to the web service, we need to create an HTTP transport request. We'll use the HttpTransportSE class (org.ksoap2.transport.HttpTransportSE) for this. Take a look at the example below.

HttpTransportSE ht = getHttpTransportSE();

As you may have guessed, getHttpTransportSE is another helper method and allows you to quickly create an HTTP transport object. It makes it less tedious to create an HTTP transport object every time you make a web service call. Take a look at its implementation below. To create an HttpTransportSE instance, we only need the base URL of the web service, which is another private static string as you can see below.

private final HttpTransportSE getHttpTransportSE() {
    HttpTransportSE ht = new HttpTransportSE(Proxy.NO_PROXY,MAIN_REQUEST_URL,60000);
    ht.debug = true;
    ht.setXmlVersionTag("<!--?xml version=\"1.0\" encoding= \"UTF-8\" ?-->");
    return ht;
}
private static final String MAIN_REQUEST_URL = "http://www.w3schools.com/webservices/tempconvert.asmx";

In getHttpTransportSE, we also configure the HttpTransportSE object. By passing Proxy.NO_PROXY as the first argument of the constructor, we specify that no proxy is used for the request. The third argument of the constructor sets the session timeout in milliseconds. To make debugging easier, we also set the object's debug property to true. Any issues that pop up are logged to LogCat.

Step 4: Sending the Request

It is time to send the SOAP request to the web service. We do this over HTTP using the transport and envelope objects we created earlier. The HTTP transport object has a call method, which is used to add the SOAP action and envelope that we created earlier.

ht.call(SOAP_ACTION, envelope);

SOAP_ACTION is another private static string as you can see below.

private static final String SOAP_ACTION = "http://www.w3schools.com/webservices/FahrenheitToCelsius";

Step 5: Processing the Response

When the web service sends back a response, we'll need to process it and handle any errors that may have been thrown. We can then display the data to the user. Take a look at the code snippet below in which we extract the response from the response envelope using the getResponse method.

SoapPrimitive resultsString = (SoapPrimitive)envelope.getResponse();

I'm using a SoapPrimitive type, but you can also use a SoapObject instance if the response from the web service is XML. You can then use the SoapObject instance to get the response values and store them in an array. Call toString on the SoapPrimitive object to convert the response to a simple string to use it in your application.

Take a look at the next code snippet in which I've implemented a method getCelsiusConversion. The method accepts a string variable as its only argument. The variable is added as a property to the SOAP request as we saw earlier in this tutorial. The variable the method consumes is the value in degrees Fahrenheit. This value is sent to and processed by the web service and we get back a response in degrees Celsius.

public String getCelsiusConversion(String fValue) {
        String data = null;
        String methodname = "FahrenheitToCelsius";

        SoapObject request = new SoapObject(NAMESPACE, methodname);
        request.addProperty("Fahrenheit", fValue);

        SoapSerializationEnvelope envelope = getSoapSerializationEnvelope(request);

        HttpTransportSE ht = getHttpTransportSE();
        try {
            ht.call(SOAP_ACTION, envelope);
            testHttpResponse(ht);
            SoapPrimitive resultsString = (SoapPrimitive)envelope.getResponse();

            List COOKIE_HEADER = (List)      ht.getServiceConnection().getResponseProperties();

            for (int i = 0; i < COOKIE_HEADER.size(); i++) {
                String key = COOKIE_HEADER.get(i).getKey();
                String value = COOKIE_HEADER.get(i).getValue();

                if (key != null && key.equalsIgnoreCase("set-cookie")) {
                    SoapRequests.SESSION_ID = value.trim();
                    Log.v("SOAP RETURN", "Cookie :" + SoapRequests.SESSION_ID);
                    break;
                }
            }
            data = resultsString.toString();

        } catch (SocketTimeoutException t) {
            t.printStackTrace();
        } catch (IOException i) {
            i.printStackTrace();
        } catch (Exception q) {
            q.printStackTrace();
        }
   return data;
}

I use two strings in getCelsiusConversion, data and methodname. The data variable will be returned by the method after the web service sent back a response, while methodname stores the name of the operation of the the web service that we'll target and is used in the SoapObject instance.

You may have noticed the for loop in getCelsiusConversion, which isn't part of the steps we've discussed earlier. When working with more complex web services, it is important to keep track of the current session. In the snippet below, I store the session and keep track of it each time I make a call to the web service.

for (int i = 0; i < COOKIE_HEADER.size(); i++) {
    String key = COOKIE_HEADER.get(i).getKey();
    String value = COOKIE_HEADER.get(i).getValue();
    if (key != null && key.equalsIgnoreCase("set-cookie")) {
        SoapRequests.SESSION_ID = value.trim();
        Log.v("SOAP RETURN", "Cookie :" + SoapRequests.SESSION_ID);
        break;
    }
}

4. Creating the user interface

Now that the hard work is behind us, it is time to make use of what we just created. To conclude this tutorial, I'll show you how to create a simple user interface for converting a value in degrees Fahrenheit to a value in degrees Celsius and display the result on the user's device.

Step 1: Create the layout

First, we need to create an XML file in the project's layout folder. Take a look at the code snippet below. It is a simple illustration of a user interface created in XML.

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent" /><EditText
 android:layout_width="200dp"
 android:layout_height="wrap_content"
 android:layout_gravity="center_horizontal"
 android:hint="Fahrenheit"
 android:inputType="number"
 android:id="@+id/value_to_convert" /><Button
 android:layout_width="200dp"
 android:layout_height="wrap_content"
 android:layout_gravity="center_horizontal"
 android:text="Convert to Celsius"
 android:id="@+id/convert" /><TextView
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:gravity="center_horizontal"
 android:textSize="30dp"
 android:id="@+id/answer" /></LinearLayout>

We create three components, an EditText instance, a Button instance, and a TextView instance. The EditText instance is used to enter and capture the value that we intend to send to the web service. The button is used to run the thread that invokes getCelsiusConversion and the text view displays the response we get back from the web service.

Step 2: Create the Activity

The next step is creating an Activity instance to display the layout we just created. Take a look at the following code snippet. This shouldn't be too surprising if you've developed Android applications before.

package com.example.KsoapExample;

import android.app.Activity;
import android.os.Bundle;

public class MyActivity extends Activity {
    /**
    * Called when the activity is first created.
    */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Now that we've taken care of the user interface, we can tie everything together. Take a look at the next code snippet to see how this is done.

package com.example.KsoapExample;

import android.app.Activity;
import android.os.Bundle;

public class MyActivity extends Activity {
    private TextView txt;
    private String celsius;
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        final EditText edt = (EditText)findViewById(R.id.value_to_convert);
        Button btn = (Button)findViewById(R.id.convert);
        txt = (TextView)findViewById(R.id.answer);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (edt.length() > 0) {
                    getCelsius(edt.getText().toString());
                } else {
                    txt.setText("Fahrenheit value can not be empty.");
                }
            }
        });
    }
}

In onCreate, we set a listener on the button, btn. We also double-check that a value is entered in the input field before sending it to the web service. In the button click listener, the value passed to getCelsius is cast to a string as the web service expects a string value. The implementation of getCelsius isn't difficult as you can see below.

private final void getCelsius(final String toConvert) {
    new Thread(new Runnable() {

        @Override
        public void run() {
            SoapRequests ex = new SoapRequests();
            celsius = ex.getCelsiusConversion(toConvert);
            handler.sendEmptyMessage(0);
            }
    }).start();
}

In getCelsius, a new thread is created, which runs and creates an instance of the class that implements getCelsiusConversion. When we receive a response from the web service, we send a message to a handler to update the user interface by displaying the value in degrees Celsius to the user.

public Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {

            case 0:
                txt.setText(celsius);
                break;
        }
        return false;
    }
});

In the handler, we update the TextView instance with the value we received from the web service. Take a look at the final result below.



Conclusion

You should now be able to add the kSOAP library to a project and leverage it to make requests to a web service that uses the SOAP protocol. Working with the kSOAP library will become easier with a little practice and I therefore encourage you to try the Celsius to Fahrenheit conversion web service. Try out the example Android application that's part of the tutorial for a bit of extra help.

2014-01-22T12:30:22.143Z2014-01-22T12:30:22.143ZSashen Govenderhttp://code.tutsplus.com/tutorials/consuming-web-services-with-ksoap--mobile-21242

Android First… Even When It's Second

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21360

Designing for the Android platform isn't easy, especially if you're used to designing for iOS. Is porting your iOS designs to Android a viable option? It's not, and I will tell you why.


The Backstory

Chances are, you got into mobile design so you could make iOS applications. Sooner or later though, you'll find yourself being asked to port an iOS application to Android. Your boss tells you Android has something like 98% of the mobile market—okay, it's about 60%—and it must be supported. "Think of all the people that cannot use our application!" he says. You ponder leaping off the balcony, but instead you decide to give it shot. After all, how hard can it be?

You talk to your designer friends, asking them what you should do. They complain about Android and about how hard it is to design for Android—not much of a help. You visit Dribbble, Behance, and the Google Play store looking for examples of good Android designs. Unfortunately, there's not much to find. Even the big guys seem to have given up and jury-rigged iOS designs onto Android. You start wondering what kind of a mess you've gotten yourself into.

And evidently, after all that, your natural reaction is to contemplate the easy path. You'll pump out the fastest possible iOS-to-Android design port you can and get this project off your plate and out of your life. My advice? Don't do it! You can break this cycle—here's how.


Start from Scratch

This step is crucial in the process as it will define your future workflow, your needs, and get you a realistic time estimate for the project. But you'll have to approach this pretending that there is no iOS application. Start from scratch. Revisit your goals. Do some problem solving. (Re)engage the challenge of making a fantastic application. It's a similar process to what you would have gone through to get that lovely iOS application out, back in the day.



Explore New Patterns

Immerse yourself in Android and its design patterns. Sure, it's got some warts, but ask yourself—what does it get right? What do all the applications do, and what are users expecting? One of Android's weaknesses is also one its greatest strengths: flexibility. Think about all the possibilities it has to offer. You can control the lock screen of a device or put a widget on its home screen. You'll never find proper Facebook chat heads on iOS, Cover for iPhone, or a custom keyboard that you can use across applications. What can you do better on Android than on iOS?



New Rules for Typography

Use Roboto. Get over yourself and your love for Proxima Nova and Avenir. Roboto is a good typeface that adapts well to a lot of use cases. With six weights for each roman, italic, and condensed styles and an imposing library of glyphs, it can satisfy even the most demanding typographic tasks. It's free and it's pretty. Its overly geometrical shapes and Grotesque touches set a perfect rhythm for your application. It'll give your application that spark that will make it feel like an Android application. Google knew what they were doing when they made it the default font for Android and chances are that they thought about their devices, operating system, and typographic needs way more than you ever will.



Responsive Design

Embrace responsiveness. It's amazing how often mobile designers cry about screen sizes on Android and how well everything fits on their iPhone in one breath, and in the next, they talk about how awesome responsive websites are. Web designers have been dealing with this for more than twenty years now. This is exactly what you're creating, a responsive application. There's nothing new there. Start with the smallest low resolution screen and work your way up to the tablets. Remember that your phone application is also your tablet application. Resolutions are your breakpoints and it's all one big liquid mass of grids, copy, and images.

It's worth pointing out that, even though not mandatory, tablet optimization is an easy way to get noticed in the Google Play store or, if you're lucky, get featured by Google. At this point, you're competing with very few tablet application so go for it. Besides that, it will make you a better designer and broaden your skill set.



Second Device in Your Pocket

You must use Android as your main phone, at least until the project's done. You'll learn how the platform works and develop an eye for little Android details. Get to know the operating system and ask Android users what they like (and dislike) about Android. Take notes. Get to know the platform. Take another look at Explore New Patterns if you have to. That said, you can have an iPhone as well—you are a designer after all.



Make New Friends

Befriend your local Android engineer. Chances are that they've been using Android since you got your first iPhone. They know a lot about the platform, what it can do, what others are doing with it, and the tools you can use to craft an awesome experience. Together, you'll come up with great approaches that neither of you would have thought of on your own. Your design gurus are right under your nose.



Get Noticed

The Android platform didn't have the fortune to have a Steve Jobs to give it direction. This has resulted in mediocre user interface design and made most designers feel unconvinced. Every designer wants to be known for significant contributions to their field—that Android application is your opportunity to shine. The Android platform is maturing at a fast pace. It's more robust than ever, but it's still a toddler in terms of UI and UX. The little innovations that you come up with today may be the standards of tomorrow. You have the ability to help define the future of the operating system and get credit for your contributions.



Conclusion

To create a fantastic application on this foreign platform, you have to change how you feel about it. Learn to appreciate Android and love it as it is. Don't port your application to Android, design it for Android. I'll be the first to admit that designing for Android isn't easy, but it's the right thing to do. Good luck.

Resources

Here are few links for further reading and resources to get you started:

2014-01-24T12:30:26.000Z2014-01-24T12:30:26.000ZIlie Ciorbahttp://code.tutsplus.com/articles/android-first-even-when-its-second--mobile-21360

Working with NSURLSession: Part 4

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-22545

In the previous tutorial, we started creating a simple podcast client to put what we've learned about NSURLSession into practice. So far, our podcast client can query the iTunes Search API, download a podcast feed, and display a list of episodes. In this tutorial, we zoom in on another interesting aspect of NSURLSession, out-of-process downloads. Let me show you how this works.


Introduction

In this fourth and final tutorial about NSURLSession, we'll take a closer look at out-of-process tasks, download tasks in particular. Our podcast client is already able to show a list of episodes, but it currently lacks the ability to download individual episodes. That'll be the focus of this tutorial.

Background Uploads and Downloads

Adding support for background uploads and downloads is surprisingly easy with NSURLSession. Apple refers to them as out-of-process uploads and downloads as the tasks are managed by a background daemon, not your application. Even if your application crashes during an upload or download task, the task continues in the background.

Overview

I'd like to take a few moments to take a closer look at how out-of-process tasks work. It's pretty simple once you have a complete picture of the process. Enabling background uploads and downloads is nothing more than flipping a switch in your session's configuration. With a properly configured session object, you are ready to schedule upload and download tasks in the background.

When an upload or download is initiated, a background daemon comes into existence. The daemon takes care of the task and sends updates to the application through the delegate protocols declared in the NSURLSession API. If your application stops running for some reason, the task continues in the background as it's the daemon managing the task. The moment the task finishes, the application that created the task is notified. It reconnects with the background session that created the task and the daemon managing the task informs the session that the task finished and, in the case of a download task, hands the file over to the session. The session then invokes the appropriate delegate methods to make sure your application can take the appropriate actions, such as moving the file to a more permanent location. That's enough theory for now. Let's see what we need to do to implement out-of-process downloads in Singlecast.


1. Subclass UITableViewCell

Step 1: Update Main Storyboard

At the moment, we are using prototype cells to populate the table view. To give us a bit more flexibility, we need to create a UITableViewCell subclass. Open the main storyboard, select the table view of the MTViewController instance and set the number of prototype cells to 0.

Update the project's main storyboard.

Step 2: Create Subclass

Open Xcode's File menu and choose New > File.... Create a new Objective-C class, name it MTEpisodeCell, and make sure it inherits from UITableViewCell. Tell Xcode where you'd like to store the class files and hit Create.

Create a subclass of UITableViewCell.

Step 3: Update Class Interface

The interface of MTEpisodeCell is simple as you can see in the code snippet below. All we do is declare a property progress of type float. We'll use this to update and display the progress of the download task that we'll use for downloading an episode.

#import <UIKit/UIKit.h>

@interface MTEpisodeCell : UITableViewCell

@property (assign, nonatomic) float progress;

@end

Step 4: Implement Class

The implementation of MTEpisodeCell is a bit more involved, but it isn't complicated. Instead of using an instance of UIProgressView, we'll fill the cell's content view with a solid color to show the progress of the download task. We do this by adding a subview to the cell's content view and updating its width whenever the cell's progress property changes. Start by declaring a private property progressView of type UIView.

#import "MTEpisodeCell.h"

@interface MTEpisodeCell ()

@property (strong, nonatomic) UIView *progressView;

@end

We override the class's designated initializer as shown below. Note how we ignore the style argument and pass UITableViewCellStyleSubtitle to the superclass's designated initializer. This is important, because the table view will pass UITableViewCellStyleDefault as the cell's style when we ask it for a new cell.

In the initializer, we set the background color of the text and detail text labels to [UIColor clearColor] and create the progress view. Two details are especially important. First, we insert the progress view as a subview of the cell's content view at index 0 to make sure that it's inserted below the text labels. Second, we invoke updateView to make sure that the frame of the progress view is updated to reflect the value of progress, which is set to 0 during the cell's initialization.

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];

    if (self) {
        // Helpers
        CGSize size = self.contentView.bounds.size;

        // Configure Labels
        [self.textLabel setBackgroundColor:[UIColor clearColor]];
        [self.detailTextLabel setBackgroundColor:[UIColor clearColor]];

        // Initialize Progress View
        self.progressView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, size.width, size.height)];

        // Configure Progress View
        [self.progressView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth)];
        [self.progressView setBackgroundColor:[UIColor colorWithRed:0.678 green:0.886 blue:0.557 alpha:1.0]];
        [self.contentView insertSubview:self.progressView atIndex:0];

        // Update View
        [self updateView];
    }

    return self;
}

Before we take a look at the implementation of updateView, we need to override the setter method of the progress property. The only change we make to the default implementation of setProgress: is invoke updateView when the _progress instance variable is updated. This ensures that the progress view is updated whenever we update the cell's progress property.

- (void)setProgress:(CGFloat)progress {
    if (_progress != progress) {
        _progress = progress;

        // Update View
        [self updateView];
    }
}

In updateView, we calculate the new width of the progress view based on the value of the cell's progress property.

- (void)updateView {
    // Helpers
    CGSize size = self.contentView.bounds.size;

    // Update Frame Progress View
    CGRect frame = self.progressView.frame;
    frame.size.width = size.width * self.progress;
    self.progressView.frame = frame;
}

Step 5: Use MTEpisodeCell

To make use of the MTEpisodeCell, we need to make a few changes in the MTViewController class. Start by adding an import statement for MTEpisodeCell.

#import "MTViewController.h"

#import "MWFeedParser.h"
#import "SVProgressHUD.h"
#import "MTEpisodeCell.h"

@interface MTViewController () <MWFeedParserDelegate>

@property (strong, nonatomic) NSDictionary *podcast;
@property (strong, nonatomic) NSMutableArray *episodes;
@property (strong, nonatomic) MWFeedParser *feedParser;

@end

In the view controller's viewDidLoad method, invoke setupView, a helper method we'll implement next.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup View
    [self setupView];

    // Load Podcast
    [self loadPodcast];

    // Add Observer
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"MTPodcast" options:NSKeyValueObservingOptionNew context:NULL];
}

In setupView, we invoke setupTableView, another helper method in which we tell the table view to use the MTEpisodeCell class whenever it needs a cell with a reuse identifier of EpisodeCell.

- (void)setupView {
    // Setup Table View
    [self setupTableView];
}
- (void)setupTableView {
    // Register Class for Cell Reuse
    [self.tableView registerClass:[MTEpisodeCell class] forCellReuseIdentifier:EpisodeCell];
}

Before we build the project and run the application, we need to update our implementation of tableView:cellForRowAtIndexPath: as shown below.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MTEpisodeCell *cell = (MTEpisodeCell *)[tableView dequeueReusableCellWithIdentifier:EpisodeCell forIndexPath:indexPath];

    // Fetch Feed Item
    MWFeedItem *feedItem = [self.episodes objectAtIndex:indexPath.row];

    // Configure Table View Cell
    [cell.textLabel setText:feedItem.title];
    [cell.detailTextLabel setText:[NSString stringWithFormat:@"%@", feedItem.date]];

    return cell;
}

Step 6: Build and Run

Run your application in the iOS Simulator or on a test device to see the result. If nothing has changed, then you've followed the steps correctly. All that we've done so far is replacing the prototype cells with instances of MTEpisodeCell.


2. Create Background Session

To enable out-of-process downloads, we need a session that is configured to support out-of-process downloads. This is surprisingly easy to do with the NSURLSession API. There a few gotchas though.

Step 1: Create session Property

Start by declaring a new property session of type NSURLSession in the MTViewController class and make the class conform to the NSURLSessionDelegate and NSURLSessionDownloadDelegate protocols.

#import "MTViewController.h"

#import "MWFeedParser.h"
#import "SVProgressHUD.h"
#import "MTEpisodeCell.h"

@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate, MWFeedParserDelegate>

@property (strong, nonatomic) NSDictionary *podcast;
@property (strong, nonatomic) NSMutableArray *episodes;
@property (strong, nonatomic) MWFeedParser *feedParser;

@property (strong, nonatomic) NSURLSession *session;

@end

In viewDidLoad, we set the session property by invoking backgroundSession on the view controller instance. This is one of the gotchas I was talking about.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup View
    [self setupView];

    // Initialize Session
    [self setSession:[self backgroundSession]];

    // Load Podcast
    [self loadPodcast];

    // Add Observer
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"MTPodcast" options:NSKeyValueObservingOptionNew context:NULL];
}

Let's take a look at the implementation of backgroundSession. In backgroundSession, we statically declare a session variable and use dispatch_once (Grand Central Dispatch) to instantiate the background session. Even though this isn't strictly necessary, it emphasizes the fact that we only need one background session at any time. This is a best practice that's also mentioned in the WWDC session on the NSURLSession API.

In the dispatch_once block, we start by creating a NSURLSessionConfiguration object by invoking backgroundSessionConfiguration: and passing a string as an identifier. The identifier we pass uniquely identifies the background session, which is key as we'll see a bit later. We then create a session instance by invoking sessionWithConfiguration:delegate:delegateQueue: and passing the session configuration object, setting the session's delegate property, and passing nil as the third argument.

- (NSURLSession *)backgroundSession {
    static NSURLSession *session = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Session Configuration
        NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.mobiletuts.Singlecast.BackgroundSession"];

        // Initialize Session
        session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
    });

    return session;
}
By passing nil as the third argument of sessionWithConfiguration:delegate:delegateQueue:, the session creates a serial operation queue for us. This operation queue is used for performing the delegate method calls and completion handler calls.

3. Download Episode

Step 1: Create Download Task

It's time to make use of the background session we created and put the MTEpisodeCell to use. Let's start by implementing tableView:didSelectRowAtIndexPath:, a method of the UITableViewDelegate protocol. Its implementation is straightforward as you can see below. We fetch the correct MWFeedItem instance from the episodes array and pass it to downloadEpisodeWithFeedItem:.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    // Fetch Feed Item
    MWFeedItem *feedItem = [self.episodes objectAtIndex:indexPath.row];

    // Download Episode with Feed Item
    [self downloadEpisodeWithFeedItem:feedItem];
}

In downloadEpisodeWithFeedItem:, we extract the remote URL from the feed item by invoking urlForFeedItem:, create a download task by calling downloadTaskWithURL: on the background session, and send it a message of resume to start the download task.

- (void)downloadEpisodeWithFeedItem:(MWFeedItem *)feedItem {
    // Extract URL for Feed Item
    NSURL *URL = [self urlForFeedItem:feedItem];

    if (URL) {
        // Schedule Download Task
        [[self.session downloadTaskWithURL:URL] resume];
    }
}

As you may have guessed, urlForFeedItem: is a convenience method that we use. We'll use it a few more times in this project. We obtain a reference to the feed item's enclosures array, extract the first enclosure, and pull out the object for the url key. We create and return an NSURL instance.

- (NSURL *)urlForFeedItem:(MWFeedItem *)feedItem {
    NSURL *result = nil;

    // Extract Enclosures
    NSArray *enclosures = [feedItem enclosures];
    if (!enclosures || !enclosures.count) return result;

    NSDictionary *enclosure = [enclosures objectAtIndex:0];
    NSString *urlString = [enclosure objectForKey:@"url"];
    result = [NSURL URLWithString:urlString];

    return result;
}

We're not done yet. Is the compiler giving you three warnings? That's not surprising as we haven't implemented the required methods of the NSURLSessionDelegate and NSURLSessionDownloadDelegate protocols yet. We also need to implement these methods if we want to show the progress of the download tasks.

Step 2: Implementing Protocol(s)

The first method we need to implement is URLSession:downloadTask:didResumeAtOffset:. This method is invoked if a download task is resumed. Because this is something we won't cover in this tutorial, we simply log a message to Xcode's console.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

More interesting is the implementation of URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:. This method is invoked every time a few bytes have been downloaded by the session. In this delegate method, we calculate the progress, fetch the correct cell, and update the cell's progress property, which in turn updates the cell's progress view. Have you spotted the dispatch_async call? There's no guarantee that the delegate method is invoked on the main thread. Since we update the user interface by setting the cell's progress, we need to update the cell's progress property on the main thread.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    // Calculate Progress
    double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;

    // Update Table View Cell
    MTEpisodeCell *cell = [self cellForForDownloadTask:downloadTask];

    dispatch_async(dispatch_get_main_queue(), ^{
        [cell setProgress:progress];
    });
}

The implementation of cellForForDownloadTask: is straightforward. We pull the remote URL from the download task using its originalRequest property and loop over the feed items in the episodes array until we have a match. When we've found a match, we ask the table view for the corresponding cell and return it.

- (MTEpisodeCell *)cellForForDownloadTask:(NSURLSessionDownloadTask *)downloadTask {
    // Helpers
    MTEpisodeCell *cell = nil;
    NSURL *URL = [[downloadTask originalRequest] URL];

    for (MWFeedItem *feedItem in self.episodes) {
        NSURL *feedItemURL = [self urlForFeedItem:feedItem];

        if ([URL isEqual:feedItemURL]) {
            NSUInteger index = [self.episodes indexOfObject:feedItem];
            cell = (MTEpisodeCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
            break;
        }
    }

    return cell;
}

The third delegate method of the NSURLSessionDownloadDelegate protocol that we need to implement is URLSession:downloadTask:didFinishDownloadingToURL:. As I mentioned in the previous tutorials, one of the advantages of the NSURLSession API is that downloads are immediately written to disk. The result is that we are passed a local URL in URLSession:downloadTask:didFinishDownloadingToURL:. However, the local URL that we receive, points to a temporary file. It is our responsibility to move the file to a more permanent location and that's exactly what we do in URLSession:downloadTask:didFinishDownloadingToURL:.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    // Write File to Disk
    [self moveFileWithURL:location downloadTask:downloadTask];
}

In moveFileWithURL:downloadTask:, we extract the episode's file name from the download task and create a URL in the application's Documents directory by invoking URLForEpisodeWithName:. If the temporary file that we received from the background session points to a valid file, we move that file to its new home in the application's Documents directory.

- (void)moveFileWithURL:(NSURL *)URL downloadTask:(NSURLSessionDownloadTask *)downloadTask {
    // Filename
    NSString *fileName = [[[downloadTask originalRequest] URL] lastPathComponent];

    // Local URL
    NSURL *localURL = [self URLForEpisodeWithName:fileName];

    NSFileManager *fm = [NSFileManager defaultManager];

    if ([fm fileExistsAtPath:[URL path]]) {
        NSError *error = nil;
        [fm moveItemAtURL:URL toURL:localURL error:&error];

        if (error) {
            NSLog(@"Unable to move temporary file to destination. %@, %@", error, error.userInfo);
        }
    }
}
I use a lot of helper methods in my iOS projects, because it makes for DRY code. It's also good practice to create methods that only do one thing. Testing becomes much easier that way.

URLForEpisodeWithName: is another helper method, which invokes episodesDirectory. In URLForEpisodeWithName:, we append the name argument to the Episodes directory, which is located in the application's Documents directory.

- (NSURL *)URLForEpisodeWithName:(NSString *)name {
    if (!name) return nil;
    return [self.episodesDirectory URLByAppendingPathComponent:name];
}

In episodesDirectory, we create the URL for the Episodes directory and create the directory if it doesn't exist yet.

- (NSURL *)episodesDirectory {
    NSURL *documents = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *episodes = [documents URLByAppendingPathComponent:@"Episodes"];

    NSFileManager *fm = [NSFileManager defaultManager];

    if (![fm fileExistsAtPath:[episodes path]]) {
        NSError *error = nil;
        [fm createDirectoryAtURL:episodes withIntermediateDirectories:YES attributes:nil error:&error];

        if (error) {
            NSLog(@"Unable to create episodes directory. %@, %@", error, error.userInfo);
        }
    }

    return episodes;
}

Step 3: Build and Run

Run the application and test the result by downloading an episode from the list of episodes. You should see the table view cell's progress view progress from left to right reflecting the progress of the download task. There are a few issues though. Have you tried scrolling through the table view? That doesn't look right. Let's fix that.


4. Create a Progress Buffer

Because the table view reuses cells as much as possible, we need to make sure that each cell properly reflects the download state of the episode that it represents. We can fix this in several ways. One approach is to use an object that keeps track of the progress of each download task, including the download tasks that have already completed.

Step 1: Declare a Property

Let's start by declaring a new private property progressBuffer of type NSMutableDictionary in the MTViewController class.

#import "MTViewController.h"

#import "MWFeedParser.h"
#import "SVProgressHUD.h"
#import "MTEpisodeCell.h"

@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate, MWFeedParserDelegate>

@property (strong, nonatomic) NSDictionary *podcast;
@property (strong, nonatomic) NSMutableArray *episodes;
@property (strong, nonatomic) MWFeedParser *feedParser;

@property (strong, nonatomic) NSURLSession *session;
@property (strong, nonatomic) NSMutableDictionary *progressBuffer;

@end

Step 2: Initialize Buffer

In viewDidLoad, we initialize the progress buffer as shown below.

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup View
    [self setupView];

    // Initialize Session
    [self setSession:[self backgroundSession]];

    // Initialize Progress Buffer
    [self setProgressBuffer:[NSMutableDictionary dictionary]];

    // Load Podcast
    [self loadPodcast];

    // Add Observer
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"MTPodcast" options:NSKeyValueObservingOptionNew context:NULL];
}

Step 3: Update Table View Cells

The key that we'll use in the dictionary is the remote URL of the corresponding feed item. With this in mind, we can update the tableView:cellForRowAtIndexPath: method as shown below. We pull the remote URL from the feed item and ask progressBuffer for the value for the key that corresponds to the remote URL. If the value isn't nil, we set the cell's progress property to that value, otherwise we set the progress property of the cell to 0.0, which hides the progress view by setting its width to 0.0.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MTEpisodeCell *cell = (MTEpisodeCell *)[tableView dequeueReusableCellWithIdentifier:EpisodeCell forIndexPath:indexPath];

    // Fetch Feed Item
    MWFeedItem *feedItem = [self.episodes objectAtIndex:indexPath.row];
    NSURL *URL = [self urlForFeedItem:feedItem];

    // Configure Table View Cell
    [cell.textLabel setText:feedItem.title];
    [cell.detailTextLabel setText:[NSString stringWithFormat:@"%@", feedItem.date]];

    NSNumber *progress = [self.progressBuffer objectForKey:[URL absoluteString]];
    if (!progress) progress = @(0.0);

    [cell setProgress:[progress floatValue]];

    return cell;
}

Step 4: Avoid Duplicates

We can also use the progress buffer to prevent users from downloading the same episode twice. Take a look at the updated implementation of tableView:didSelectRowAtIndexPath:. We take the same steps we took in tableView:cellForRowAtIndexPath: to extract the progress value from the progress buffer. Only when the progress value is nil, we download the episode.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    // Fetch Feed Item
    MWFeedItem *feedItem = [self.episodes objectAtIndex:indexPath.row];

    // URL for Feed Item
    NSURL *URL = [self urlForFeedItem:feedItem];

    if (![self.progressBuffer objectForKey:[URL absoluteString]]) {
        // Download Episode with Feed Item
        [self downloadEpisodeWithFeedItem:feedItem];
    }
}

Step 5: Update Buffer

The progress buffer only works in its current implementation if we keep it up to date. This means that we need to update the URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: method as well. All we do is store the new progress value in the progress buffer.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
    // Calculate Progress
    double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;

    // Update Progress Buffer
    NSURL *URL = [[downloadTask originalRequest] URL];
    [self.progressBuffer setObject:@(progress) forKey:[URL absoluteString]];

    // Update Table View Cell
    MTEpisodeCell *cell = [self cellForForDownloadTask:downloadTask];

    dispatch_async(dispatch_get_main_queue(), ^{
        [cell setProgress:progress];
    });
}

In downloadEpisodeWithFeedItem:, we set the progress value to 0.0 when the download task starts.

- (void)downloadEpisodeWithFeedItem:(MWFeedItem *)feedItem {
    // Extract URL for Feed Item
    NSURL *URL = [self urlForFeedItem:feedItem];

    if (URL) {
        // Schedule Download Task
        [[self.session downloadTaskWithURL:URL] resume];

        // Update Progress Buffer
        [self.progressBuffer setObject:@(0.0) forKey:[URL absoluteString]];
    }
}

The session delegate is notified when a download task finishes. In URLSession:downloadTask:didFinishDownloadingToURL:, we set the progress value to 1.0.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    // Write File to Disk
    [self moveFileWithURL:location downloadTask:downloadTask];

    // Update Progress Buffer
    NSURL *URL = [[downloadTask originalRequest] URL];
    [self.progressBuffer setObject:@(1.0) forKey:[URL absoluteString]];
}

Step 6: Restore Buffer

At the moment, the progress buffer is only stored in memory, which means that it's cleared between application launches. We could write its contents to disk, but to keep this application simple we are going to restore or recreate the buffer by checking which episodes have already been downloaded. The feedParser:didParseFeedItem: method, part of the MWFeedParserDelegate protocol, is invoked for every item in the feed. In this method, we pull the remote URL from the feed item, create the corresponding local URL, and check if the file exists. If it does, then we set the corresponding progress value for that feed item to 1.0 to indicate that it's already been downloaded.

- (void)feedParser:(MWFeedParser *)parser didParseFeedItem:(MWFeedItem *)item {
    if (!self.episodes) {
        self.episodes = [NSMutableArray array];
    }

    [self.episodes addObject:item];

    // Update Progress Buffer
    NSURL *URL = [self urlForFeedItem:item];
    NSURL *localURL = [self URLForEpisodeWithName:[URL lastPathComponent]];

    if ([[NSFileManager defaultManager] fileExistsAtPath:[localURL path]]) {
        [self.progressBuffer setObject:@(1.0) forKey:[URL absoluteString]];
    }
}

Step 7: Rinse and Repeat

Run the application one more time to see if the issues with the table view are resolved. The application should now also remember which episodes have already been downloaded.


5. Being a Good Citizen

It's important that our application is a good citizen by not wasting more CPU cycles or consume more battery power than needed. What does this mean for our podcast client. When a download task is started by our application and the application goes to the background, the background daemon that manages our application's download task notifies our application through the background session that the download task has finished. If necessary, the background daemon will launch our application so that it can respond to these notifications and process the downloaded file.

In our example, we don't need to do anything special to make sure that our application reconnects to the original background session. This is taken care of by the MTViewController instance. However, we do have to notify the operating system when our application has finished processing the download(s) by invoking a background completion handler.

When our application is woken up by the operating system to respond to the notifications of the background session, the application delegate is sent a message of application:handleEventsForBackgroundURLSession:completionHandler:. In this method, we can reconnect to the background session, if necessary, and invoke the completion handler that is passed to us. By invoking the completion handler, the operating system knows that our application no longer needs to run in the background. This is important for optimizing battery life. How do we do this in practice?

Step 1: Declare a Property

We first need to declare a property on the MTAppDelegate class to keep a reference to the completion handler that we get from application:handleEventsForBackgroundURLSession:completionHandler:. The property needs to be public. The reason for this will become clear in a moment.

#import <UIKit/UIKit.h>

@interface MTAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (copy, nonatomic) void (^backgroundSessionCompletionHandler)();

@end

Step 2: Implement Callback

In application:handleEventsForBackgroundURLSession:completionHandler:, we store the completion handler in backgroundSessionCompletionHandler, which we declared a moment ago.

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
    [self setBackgroundSessionCompletionHandler:completionHandler];
}

Step 3: Invoke Background Completion Handler

In the MTViewController class, we start by adding an import statement for the MTAppDelegate class.

#import "MTViewController.h"

#import "MWFeedParser.h"
#import "MTAppDelegate.h"
#import "SVProgressHUD.h"
#import "MTEpisodeCell.h"

@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate, MWFeedParserDelegate>

@property (strong, nonatomic) NSDictionary *podcast;
@property (strong, nonatomic) NSMutableArray *episodes;
@property (strong, nonatomic) MWFeedParser *feedParser;

@property (strong, nonatomic) NSURLSession *session;
@property (strong, nonatomic) NSMutableDictionary *progressBuffer;

@end

We then implement another helper method, invokeBackgroundSessionCompletionHandler, which invokes the background completion handler stored in the application delegate's backgroundSessionCompletionHandler property. In this method, we ask the background session for all its running tasks. If there are no tasks running, we get a reference to the application delegate's background completion handler and, if it isn't nil, we invoke it and set it to nil.

- (void)invokeBackgroundSessionCompletionHandler {
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        NSUInteger count = [dataTasks count] + [uploadTasks count] + [downloadTasks count];

        if (!count) {
            MTAppDelegate *applicationDelegate = (MTAppDelegate *)[[UIApplication sharedApplication] delegate];
            void (^backgroundSessionCompletionHandler)() = [applicationDelegate backgroundSessionCompletionHandler];

            if (backgroundSessionCompletionHandler) {
                [applicationDelegate setBackgroundSessionCompletionHandler:nil];
                backgroundSessionCompletionHandler();
            }
        }
    }];
}

Wait a minute. When do we invoke invokeBackgroundSessionCompletionHandler? We do this every time a download task finishes. In other words, we invoke this method in URLSession:downloadTask:didFinishDownloadingToURL: as shown below.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
    // Write File to Disk
    [self moveFileWithURL:location downloadTask:downloadTask];

    // Update Progress Buffer
    NSURL *URL = [[downloadTask originalRequest] URL];
    [self.progressBuffer setObject:@(1.0) forKey:[URL absoluteString]];

    // Invoke Background Completion Handler
    [self invokeBackgroundSessionCompletionHandler];
}

6. Wrapping Up

I hope you agree that our podcast client isn't ready for the App Store just yet since one of the key features, playing podcasts, is still missing. As I mentioned in the previous tutorial, the focus of this project wasn't creating a full-featured podcast client. The goal of this project was illustrating how to leverage the NSURLSession API to search the iTunes Search API and download podcast episodes using data and out-of-process download tasks respectively. You should now have a basic understanding of the NSURLSession API as well as out-of-process tasks.


Conclusion

By creating a simple podcast client, we have taken a close look at data and download tasks. We've also learned how easy it is to schedule download tasks in the background. The NSURLSession API is an important step forward for both iOS and OS X, and I encourage you to take advantage of this easy to use and flexible suite of classes. In the final installment of this series, I will take a look at AFNetworking 2.0. Why is it a milestone release? When should you use it? And how does it compare to the NSURLSession API?

2014-01-27T12:30:14.000Z2014-01-27T12:30:14.000ZBart Jacobshttp://code.tutsplus.com/tutorials/working-with-nsurlsession-part-4--mobile-22545

Android 2013: A Year in Review and 2014 Predictions

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-21766

It was another strong and, may I say, dominant year for the Android platform. It continued to dominate in market share and climb in numbers. Countless devices were released featuring the popular, mobile operating system and not all of those devices were smartphones or tablets. Android is making headway in new territories. 2013 was a busy year for the green robot. Now that the year has come to a close, it's time to recap the action.


Market Share

Android's market share in 2013 was impressive. Google reported that there'd been over 900,000 activations and IDC (International Data Corporation) data showed that four out of five phones shipped with the Android operating system on it. There was good news on the Google Play front as well. Google Play is accounting for 75% of all app downloads.

Unfortunately, market share doesn't equal profit. In 2013, Apple's App Store generated over $10 billion with December accounting for $1 billion.

From a developer's perspective, these numbers are encouraging and the same is true for Google Play. However, it remains a fact that Android users spend less money on applications than iOS users. Apple's mobile platform remains more profitable than Google's.


API distribution

In 2013, Android made good progress addressing the operating system's problematic fragmentation. Android versions below 2.2 have virtually disappeared with 2.2 (Froyo) and 3.2 (Honeycomb) following suit.

As of January 2014, more than 50% of Android devices are running a flavor of Jelly Bean, while 2.3 (Gingerbread) and 4.0 (Ice cream sandwich) declined considerable in 2013. This is a good sign, though, as it indicates that attempts to reduce Android's fragmentation are heading the right direction.

December 2012
VersionCodenameAPIDistribution
1.5Cupcake30.1%
1.6Donut40.3%
2.1Eclaire72.7%
2.2Froyo810.3%
2.2-2.3.2Ginger Bread90.2%
2.3.3-2.3.7Ginger Bread1050.6%
3.1Honeycomb120.4%
3.2Honeycomb131.2%
4.0.3-4.0.4Ice Cream Sandwich1527.5%
4.1Jelly Bean165.9%
4.2Jelly Bean170.8%
December 2013
VersionCodenameAPIDistribution
2.2Froyo81.6%
2.3.3-2.3.7Ginger Bread1024.1%
3.2Honeycomb130.1%
4.0.3-4.0.4Ice Cream Sandwich1518.6%
4.1Jelly Bean1637.4%
4.2Jelly Bean1712.9%
4.3Jelly Bean184.2%
4.4KitKat191.1%

Hardware

Nexus Line

The Nexus line was updated with two new entries in 2013. LG's Nexus 5 was the successor to the Nexus 4 and, once again, it was LG manufacturing the device. The device itself didn't ship with revolutionary features for its time of launch, it was the KitKat experience and the mid-market price tag of $349 that made for a desirable device. Asus' Nexus 7 also received a refresh, which included a 1200 x 1920 display and 2GB of RAM.


Gaming

Android made great strides as a gaming platform with a few new and notable gaming systems using Android as their operating system. One of the biggest hits was the Ouya. The tiny console's goal is to bring cost-effective hardware into the living room and to provide a platform that is open to small developers. The concept proved a major hit on Kickstarter. It reached its goal in just eight hours.

And then there was the NVidea Shield, ultimate gaming and portable entertainment as NVidia puts it. The Shield is a powerful handheld gaming console with access to Google play and the ability to stream games from compatible computers. It makes for an impressive and unique device, but the key question is whether it will make a dent in the incredibly competitive gaming landscape.

Glass

Google Glass continued to grow with many developers and technology enthusiasts joining the Glass Explorers Program. Early adopters and developers were excited enough by the product to spend the $1,500 to join the program. Most of the program's participants continue to give overwhelmingly positive responses so it seems Google is doing something right with Google Glass. People wearing Google Glass still experience social awkwardness and tension and it will therefore be interesting to see if this holds back the adoption and growth of Google Glass.

Galaxy Gear

In 2013, wearables started to become mainstream. Really? No. Not really. However, the Pebble smartwatch was finally released after its successful Kickstarter campaign. In addition, Sony released its second generation of smartwatch and Samsung launched the Galaxy Gear in the fall.

None managed to win over the average consumer and they failed to prove the usefulness of a smartwatch to the mass market. This leaves the market wide open for 2014 and with big names like Apple, Google, and Microsoft rumored to be introducing products in this category, it could become an interesting year for the smartwatch. Let's hope the competition in this space will lead to spectacular innovation so that we will start to see more useful and practical implementation of smartwatches in this market segment.

Google TV

It was a slow year for Google TV. There were few additions to the market and none that were noteworthy. It was announced that Android version 4.2.2 will eventually make its way to compatible set-top boxes and that it will also get support for Google's Chromecast at some point.

Chromecast

Speaking of Google's Chromecast, on July 24, Google announced the Chromecast. The Chromecast is a $35 dongle that allows consumers to stream content from a computer or mobile device to their TV over Wi-Fi. The cheap dongle was very well received and it even made it in TIME's Top 10 Gadgets of 2013.


KitKat

On October 31, Google released Android 4.4, also known as KitKat. The release came as a surprise to many as Key Lime Pie and 5.0 were expected instead of KitKat. KitKat was also the first version to be named after a branded product.

The update itself is substantial and includes many new features. The update's theme was reducing the operating system's footprint and making it more efficient in terms of power consumption and performance on older and low-end devices.

From a developer's perspective, it is interesting to note the update also introduced new memory management tools, new storage access and printing frameworks, and host card emulation. You can read a more comprehensive review of Android KitKat on Mobiletuts+.


CyanogenMod

One of last year's most significant success stories came from Android's community operating system, CyanogenMod. CyanogenMod has been helping Android users for years getting the best from their devices, long after official updates and patches had stopped. In 2013, the people behind the popular Android distribution was able to raise funding and create a company. The not so surprising result is that rumors have started circulating about the possibility of an official CyanogenMod smartphone in the not so distant future.



Android Studio

During last year's Google I/O developer conference, Google announced a new IDE (Integrated Development Environment) for the Android platform, Android Studio. It was made available to developers in June as an early access preview. The preview is available for download on Windows, OS X, and Linux from the Android developer website.

Android Studio is based on IntelliJ IDEA, a popular Java IDE developed by JetBrains. Based on early reviews and feedback, it seems to be well received by the Android developer community. Despite a plenty of updates, the IDE is still an early preview and prone to bugs. Eclipse is still preferred by most developers.


Google Play Game Services

In 2013, Google Play and a number of other core applications and services received significant updates. These updates took the spotlight at Google I/O 2013. With more than 900,000 daily activations, it was time to refine the user experience and improve the operating system's services.

The biggest announcement was Google Play game services SDK enabling developers to create games with achievements, leaderboards, real-time multiplayer. Game data can be stored in the cloud and users can use their Google+ account to sign in.


Predictions

Convergence

My prediction is that 2014 will be the year of convergence. There's currently an Android device for nearly every situation imaginable and the focus for this year will be on making sure that the user experience is less fragmented and more alike across devices.

Devices

We can expect another update of the operating system this year, which will most likely coincide with a refresh of the Nexus line. The Nexus 10 will receive an update or a sibling and the release of a Nexus 6 is a safe bet.

The key players in the smartphone and tablet market will no doubt release new products, but I think it's the console market that will surprise us this year. There's a new breed of consoles, microconsoles, that are trying to enter the console market, such as the Nvidia's Shield, Valve's Steam Machine, and, of course, the crowdfunded Ouya. Valve's Steam Machine in particular is an interesting concept as it lets manufacturers create consoles based on a detailed specification. Alienware, for example, recently announced that it will be releasing a Steam Machine in September. It will be an interesting year for gamers. That's for sure.

In 2013, several companies entered the wearables category and this trend will not stop in 2014. The rumors about an Apple smartwatch have subsided a little, but where there's smoke, there's fire. Right?

Android

I hope we get to see Android 5.0 in 2014 as this milestone release is long overdue. The update may introduce improvements related to multitasking, better Chromecast integration, and deeper integration of Google Now. As for Google Now, new voice commands will be added as well as improvements to voice search that build upon the OK Google features added to Google Now in 2013.

Many consumers hope to see KitKat on their devices at some point. With the market as competitive as it is, failing to quickly and smoothly update handsets could put several manufactures into trouble. Fast and smooth updates of the operating system remain one of the key advantages iOS has over Android. Long gone are the days when a company could issue a single update to a device in its first year and then leave it at that. Failing to keep handsets updated will definitely cause customers to switch.

Google has invested heavily into making KitKat compatible with a wide range of devices including less powerful low-end devices. It goes without saying that developers support this wholeheartedly. Android fragmentation continues to be one of Android's primary gripes that also affect consumers. KitKat will hopefully be roled out to most of the new devices that we'll see in 2014, allowing developers to leverage KitKat's new features in their applications.


Conclusion

2014 is set to be a frantic year for Android. In many aspects, it will be similar to 2010. This time, however, it won't be smartphones being released in every shape, size, and form factor. Instead, it'll be wearables, game consoles, and television sets. We'll be spoiled with plenty of press releases, lots of excitement, and more than enough early adopters regretting their purchase.

Developers too have a lot to look forward to. The first version of Android Studio will very likely see the light of day in 2014. New devices and possibly new markets will continue to please and tempt developers, but I will be spending 2014 tweaking images for high-resolution displays.

What do you think 2014 will bring? Share your thought in the comment's below or let me know on Twitter or Google+.

2014-01-29T12:31:06.828Z2014-01-29T12:31:06.828ZLeif Johnsonhttp://code.tutsplus.com/articles/android-2013-a-year-in-review-and-2014-predictions--mobile-21766

Create a Collect-the-Pieces Game with Corona

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-22400

In this tutorial, I will show you how to create a collect-the-pieces game using the Corona SDK and the Lua programming language. We will explore how to use touch controls and events, create shapes using the graphics API, and we will also make use of physics collisions and timers. Let's get started.


1. Application Overview

In this game, the player will be able to control a puck on the screen to collect other pieces with the same color. The player has only a limited time to collect as many pieces as possible. If the player touches a piece from another color, the game is over.

App Overview
This application uses the Graphics 2.0 Library. If you're using an older version of the Corona SDK, you may run into problems. Consult the migration guide for more information.

In this project you will learn the following skills:

  • physics collisions
  • create text fields
  • user timers and images
  • how to use touch controls and events
  • create shapes using the new Graphics API

2. Target Device

Target Device

The first thing we have to do is select the platform we want to run our application on. This enables us to choose the correct sizes for the artwork that we'll use.

The iOS platform has the following requirements:

  • iPad 1/2/Mini: 1024px x 768px, 132 PPI
  • iPad Retina: 2048px x 1536px, 264 PPI
  • iPhone/iPod Touch: 320px x 480px, 163 PPI
  • iPhone/iPod Retina: 960px x 640px, 326 PPI
  • iPhone 5/iPod Touch: 1136px x 640px, 326 PPI

Since Android is a more open platform, there are many devices and possible resolutions. A few of the more common ones are listed below:

  • Asus Nexus 7 Tablet: 800px x 1280px, 216 PPI
  • Motorola Droid X: 854px x 480px, 228 PPI
  • Samsung Galaxy SIII: 720px x 1280px, 306 PPI

In this tutorial, we'll be focusing on the iOS platform and the iPhone and iPod Touch in particular. However, the code used in this tutorial can also be used for the Android platform.


3. Interface

Interface

We'll use a simple and friendly interface with multiple shapes, buttons, bitmaps and more. The artwork for the interface can be found in the source files of this tutorial.


4. Export Graphics

Export Graphics

Depending on the device you've selected, you may need to convert the graphics to the recommended resolution (PPI), which you can do in your favorite image editor. I used the Adjust Size… option in the Tools menu of the Preview application on OS X. Remember to give the images a descriptive name and save them in your project folder.


5. Application Configuration

We'll use a configuration file, config.lua, to make the application go full screen across devices. The configuration file shows the original screen size and the method used to scale the content in case the application is run on a different resolution.

application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = 'letterbox'
    },
}

6. main.lua

Let's write the actual application. Open your preferred Lua editor. Any plain text editor will work, but it is recommended to use a text editor that has syntax highlighting. Create a new file and save it as main.lua in your project folder.


7. Code Structure

We'll structure our code as if it were a class. If you're familiar with ActionScript or Java, you should find the project structure familiar.

Necessary Classes

Variables and Constants

Declare Functions

    constructor (Main function)

    class methods (other functions)

call Main function

8. Hide Status Bar

display.setStatusBar(display.HiddenStatusBar)

This code snippet hides the status bar. The status bar is the bar at the top of the device's screen that shows the time, signal, and other indicators.


9. Default Anchors

Setting the display's default anchors is useful if you're porting an application from the previous Graphics library, that is, the projects you've created with previous version of the Corona SDK. Since the release of the Graphics 2.0 library, the reference point of every image has changed from its top-left to its center. To change this in every image that you use in your project, we can invoke the setDefault method and pass a value from 0.0 to 1.0, with 0.0 being the left if you change the x anchor and the top if you change the y anchor.

display.setDefault('anchorX', 0)
display.setDefault('anchorY', 0)

10. Import Physics

We'll be using the physics library to handle collisions. Import and start the library using the code snippet shown below.

-- Physics

local physics = require('physics')
physics.start()

11. Background

Background

A simple background for the application's user interface. The code snippet below draws the background to the screen.

-- Graphics
-- [Background]
local gameBg = display.newImage('gameBg.png')

12. Title View

Title View

This is the title view. It's the first interactive screen to appear in our game. These variables store its components.

-- [Title View]

local title
local playBtn
local creditsBtn
local titleView

13. Credits View

Credits View

The credits view shows the credits and copyright of the application. The creditsView variable is used to store it.

-- [CreditsView]

local creditsView

14. Score Text Field

Score

We'll create a text field for showing the player's score a bit later in this tutorial. We store a reference to this text field in the scoreTF variable.

-- Score TextField

local scoreTF

15. Pucks

Pucks

The pucks in the game are created and distributed randomly on the stage. The pucks group will be used to group them so that we can manipulate them easily.

-- Pucks

local pucks

16. Alert

Alert

The alertView variable keeps a reference to the alert view that is displayed when a puck of the wrong color is touched by the player. The alert view will show the player the game is over.

-- Alert

local alertView

17. Sounds

Sounds

We'll use sound effects to give the game extra character. The sounds that I've used in this project were obtained from as3sfxr. You can find the background music on Play On Loop.

-- Sounds

local bgMusic = audio.loadStream('POL-sky-sanctuary-short.mp3')
local blip = audio.loadSound('blip.wav')
local wrong = audio.loadSound('lose.wav')

18. Variables

The next code snippet lists the variables that we'll use in the game. The totalPucks variable stores the number of pucks that are placed on the stage, timerSrc keeps a reference to the game's timer, and time references the rectangle that shows the remaining time.

-- Variables

local totalPucks = 20
local timerSrc
local time

19. Declare Functions

We declare the functions as local at the very beginning. In Lua, you can forward declare a function by declaring its name before implementing the function's body. This makes it easier to keep track of the various functions that we'll use in this project.

-- Functions

local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local createPuck = {}
local movePuck = {}
local reduceTime = {}
local alert = {}

20. Constructor

Let's start by creating a stub implementation for the function that will initialize the game logic, the Main function.

function Main()
end

21. Add Title View

Next, we draw the title view to the screen and add a tap listener to each button. The newImage method is used to load the images and display them on the screen using the positions passed to the function. We also create a group named titleView that serves as a container for the newly created elements.

function Main()
  titleBg = display.newImage('titleBg.png')
  playBtn = display.newImage('playBtn.png', 220, 168)
  creditsBtn = display.newImage('creditsBtn.png', 204, 230)
  titleView = display.newGroup(titleBg, playBtn, creditsBtn)

  startButtonListeners('add')
end

22. Start Button Listeners

In startButtonListeners, we add the event listeners to the title view's buttons. When the play button is tapped, we show and start the game. When the credits button is tapped, we show the game's credits.

function startButtonListeners(action)
  if(action == 'add') then
    playBtn:addEventListener('tap', showGameView)
    creditsBtn:addEventListener('tap', showCredits)
  else
    playBtn:removeEventListener('tap', showGameView)
    creditsBtn:removeEventListener('tap', showCredits)
  end
end

23. Show Credits

In showCredits, we hide the buttons, display the credits, and add a tap listener to hide the credits when the player taps the credits.

function showCredits:tap(e)
  playBtn.isVisible = false
  creditsBtn.isVisible = false
  creditsView = display.newImage('credits.png', -110, display.contentHeight-80)
  transition.to(creditsView, {time = 300, x = 0, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end

24. Hide Credits

When the player taps the credits, the view is tweened from the stage and removed.

function hideCredits:tap(e)
  playBtn.isVisible = true
  creditsBtn.isVisible = true
  transition.to(creditsView, {time = 300, y = display.contentHeight+creditsView.height, onComplete = function() creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
end

25. Show Game View

When the player taps the play button to start a new game, the title view is tweened from the stage and hidden. This shows the game view. The game view is the heart of the game. Let's break the rest of the showGameView step by step.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})
end

26. Score Text Field

We start by creating the score text field as shown below.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

  -- TextFields

  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)
end

The newText method accept a number of arguments.

  • The initial text of the text field.
  • The text field's x coordinate.
  • The text field's y coordinate.
  • The text field's font and font size.

On iOS, you have access to a wide range of fonts. On Android there are only three fonts available; Droid Sans, Droid Serif, and Droid Sans Mono.


27. Timer

In the next step, we create the timer's rectangle shape using Corona built-in vector graphics library. The newRect function creates a rectangle that is 20pt wide, 6pt tall, and it places it in the top-right of the stage. The default color of new shapes is white so we need to change the rectangle's color by invoking setFillColor and passing in an RGB value. The Graphics 2.0 library doesn't use values ranging from 0 to 255 for RGB values. Instead an RGB value is expected to range from 0.0 to 1.0.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

  -- TextFields

  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)

  -- Timer

  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)
end

A quick and easy approach for converting old RGB value to new values is by dividing the old value by 255. For example, 123 becomes 123/255, which translates to 0.48.


28. Pucks

The pucks group stores all the pucks so that we can manipulate them all at once.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

  -- TextFields

  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)

  -- Timer

  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)

  -- Pucks

  pucks = display.newGroup()
end

29. Start Game

To finish the showGameViewMethod, we install the game listeners and start the background music. By setting the loops parameter to -1, the background music will loop until we tell it to stop.

function showGameView:tap(e)
  transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

  -- TextFields

  scoreTF = display.newText('0', 25, 18, 'Courier', 15)
  scoreTF:setFillColor(15, 223, 245)

  -- Timer

  time = display.newRect(450, 20, 20, 6)
  time:setFillColor(0.05, 0.87, 0.96)

  -- Pucks

  pucks = display.newGroup()

  gameListeners('add')
  audio.play(bgMusic, {loops = -1, channel = 1})
end

30. Game Listeners

In showGameView, we call the gameListeners function in which the pucks are created by invoking the createPucks function. We also create a rectangle for displaying the game's time.

function gameListeners(action)
  if(action == 'add') then
    createPucks()
    timerSrc = timer.performWithDelay(1000, reduceTime, 20)
  else
    for i = 1, pucks.numChildren do
      pucks[i]:removeEventListener('touch', movePuck)
    end
    timer.cancel(timerSrc)
    timerSrc = nil
  end
end

31. Create Pucks

In createPucks, we use a for loop to instantiate the pucks. The number of pucks is stored in totalPucks, which we set to 20 a bit earlier in this tutorial.

A random position is calculated for each puck using math.random. We also use math.random to help us load a different color from the project's images. How does this work? We generate a random integer between 1 and 4, and add the result to the name of the image that we want to load. For example, if the random number is equal to 3, the game loads an image named puck3.png.

function createPucks()
  for i = 1, totalPucks do
    local p
    local rnd = math.floor(math.random() * 4) + 1
    p = display.newImage('puck' .. tostring(rnd) .. '.png', math.floor(math.random() * display.contentWidth), math.floor(math.random() * display.contentHeight))
    p.name = 'p' .. tostring(rnd)
    p:addEventListener('touch', movePuck)
    -- Physics
    physics.addBody(p, 'dynamic', {radius = 12})
    p.isSensor = true
    pucks:insert(p)
  end
end

32. Move Function

We use touch events to let the player move the pucks. When the player touches a puck, it is aligned with the position of the touch, the player's finger, and is then moved by updating its position. We also add a collision listener to the active puck to detect when the puck collides with another puck. This event listener is removed when the player releases the puck by lifting their finger from the screen.

function movePuck(e)
  if(e.phase == 'began') then
    -- Collision
    e.target.x = e.x - e.target.width/2
    e.target.y = e.y - e.target.height/2
    e.target:addEventListener('collision', onCollision)
  end
  if(e.phase == 'moved') then
    e.target.x = e.x - e.target.width/2
    e.target.y = e.y- e.target.height/2
  end
  if(e.phase == 'ended') then
    e.target:addEventListener('collision', onCollision)
  end
end

33. Timer

The reduceTimer function is in charge of the timer rectangle we created earlier. Every second, the width of the shape is reduced by setting its xScale property and it is removed from the stage when it's no longer visible. An alert view is shown to the player when the time's up.

function reduceTime(e)
  time.xScale = time.xScale - 0.05
  time.x = 450
  if(time.xScale <= 0.2) then
    display.remove(time)
    alert()
  end
end

34. Handling Collisions

The onCollision function is in charge of handling collisions between pucks.

function onCollision(e)
  if(e.other ~= nil) then
    if(e.other.name == e.target.name) then
      audio.play(blip)
      -- Local Score
      local score = display.newText('50', e.other.x, e.other.y, 'Courier New Bold', 14)
      transition.to(score, {time = 500, xScale = 1.5, yScale = 1.5, y = score.y - 20, onComplete = function() display.remove(score) score = nil end })
      -- Remove
      display.remove(e.other)
      e.other = nil
      -- Total Score
      scoreTF.text = tostring(tonumber(scoreTF.text) + 50)
      scoreTF.x = 15
    else
      audio.play(wrong)
      alert('lose')
    end
  end
end

This is what happens when a collision occurs:

  • We first check whether the names of the pucks are the same to see if their color matches.
  • If they have the same color, we play a sound effect.
  • The player's score is increased by 50 points and the score text field is updated.
  • If the color of the pucks don't match, a different sound effect is played and the game over alert view is shown to the player.

35. Alert

The alert function is a simple helper function to show an alert view and animate it. We stop the audio after 700 milliseconds to make sure that we can play a sound effect. The game listeners are removed to end the game and we show an appropriate image to the player.

function alert(action)
  timer.performWithDelay(700, function() audio.stop(1) audio.dispose(bgMusic) bgMusic = nil end, 1)
  gameListeners('rmv')
  if(action == 'lose') then
    alertView = display.newImage('gameOver.png', 155, 125)
  else
    alertView = display.newImage('timeUp.png', 155, 125)
    local score = display.newText(scoreTF.text, 225, 160, 'Courier New Bold', 20)
    score:setFillColor(255, 255, 255)
  end
  transition.from(alertView, {time = 300, xScale = 0.5, yScale = 0.5})

  -- Wait 100 ms to stop physics
  timer.performWithDelay(10, function() physics.stop() end, 1)
end

36. Call Main

To start the game, we invoke the Main function as shown below.

Main()

37. Loading Screen

Loading Screen

On the iOS platform, the file named Default.png is displayed while the application is launching. Add this image to your project's source folder, and it will be automatically added by the Corona compiler.


38. Icon

Icon

Using the graphics you created earlier, you can now create a nice icon. The dimensions of the icon size for a non-retina iPhone are 57px x 57px, while the retina version needs to be 114px x 114px. The artwork for iTunes is required to be 1024px x 1024px. I suggest creating the iTunes artwork first and then creating the smaller sized images by scaling the iTunes artwork down to the correct dimensions. There is no need to make the application icon glossy or add rounded corners as this is taken care of by the operating system.


39. Testing in the Simulator

Testing

It's time to test our application in the simulator. Open the Corona Simulator, browse to your project folder, and click Open. If everything works as expected, you're ready for the final step.


40. Build

Build

In the Corona Simulator, go to File > Build and select the target device. Fill out the required fields and click Build. Wait a few seconds and your application is ready to test on a device and/or to be submitted for distribution.


Conclusion

In this tutorial we've learned about touch listeners, collision detection, and physics, as well as a few other skills that can be useful in a wide number of games. Experiment with the final result and try to modify the game to create your own version. I hope you liked this tutorial and found it helpful. Thank you for reading.

2014-01-31T12:30:27.000Z2014-01-31T12:30:27.000ZCarlos Yanezhttp://code.tutsplus.com/tutorials/create-a-collect-the-pieces-game-with-corona--mobile-22400

Working with NSURLSession: AFNetworking 2.0

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-22651

In the previous installments of this series, we've taken a close look at the NSURLSession API introduced in iOS 7 and OS X Mavericks. Networking on iOS and OS X has become much simpler and more flexible thanks to the NSURLSession API. Does this mean that you should stop using AFNetworking for your networking needs? And what about AFNetworking 2.0, which was introduced a few months ago? In this final installment, I will tell you about AFNetworking 2.0 and how it compares to the NSURLSession API.


Introduction

As Mattt Thompson points out on NSHipster, AFNetworking has become the de facto networking library for Cocoa applications. At the time of writing, the library has surpassed 10,000 stars on GitHub and was forked close to 3,000 times.

If you're new to AFNetworking and would like to read more about its history and features, then I suggest to read Mattt's post about AFNetworking 2.0 on NSHipster. In this article, I'd like to focus on two aspects, what is new in AFNetworking 2.0 and how does it compare to the NSURLSession API . The question you should be asking yourself is "Should I still be using AFNetworking with NSURLSession in my toolbox?" That's the question I plan to answer in this article.


Requirements

Before we take a closer look at what AFNetworking 2.0 has to offer, it's important to know about the library's new requirements. It shouldn't be a surprise that the requirements of AFNetworking 2.0 are stricter than those of AFNetworking 1.x. As its version number indicates, AFNetworking includes breaking changes, which are the result of its new architecture. This also means that migrating a project from AFNetworking 1.x to 2.0 should be done with caution. The migration guide is the ideal place to start if you plan on migrating a project to AFNetworking 2.0.

AFNetworking no longer supports iOS 4.3 and 5. The minimum deployment targets for iOS and OS X are iOS 6 and OS X 10.8 respectively. AFNetworking 2.0 also requires Xcode 5. If you're still using Xcode 4, this may be a good time to make the transition to Xcode 5.


Solid Foundation

AFURLConnectionOperation

As many of you probably know, AFNetworking is built on top of NSURLConnection and NSOperation, which has proven to be a powerful and elegant combination. The result of this combination is AFURLConnectionOperation, an NSOperation subclass that manages an NSURLConnection instance and implements the NSURLConnectionDelegate protocol. Even though this robust foundation remains unchanged in AFNetworking 2.0, it is supplemented by the NSURLSession API, which I'll cover in more detail later.

Separation of Responsibilities

In AFNetworking 1.x, the AFHTTPRequestOperation class was in charge of request and response serialization and validation. This is no longer true in AFNetworking 2.0. The AFHTTPRequestOperation class and its subclasses are no longer directly responsible for serializing and validating requests and responses. This responsibility has been moved to the and protocols. Each protocol declares a single delegate method to handle request and response serialization and validation respectively.

To make your life easier, however, AFNetworking 2.0 ships with AFHTTPRequestSerializer and AFHTTPResponseSerializer as well as a number of subclasses for common content types, such as JSON and XML. To see how this works in practice, let's take a look at an example in which I query the Forecast API. This doesn't look all that different from how you made a request with AFNetworking 1.x. The main difference is that you'll be using the AFHTTPRequestOperation class more often. The AFJSONRequestOperation and AFXMLRequestOperation classes are no longer present in AFNetworking 2.0.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/37.8267,-122.423", key]];

// Initialize Request Operation
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:URL]];

// Configure Request Operation
[requestOperation setResponseSerializer:[AFJSONResponseSerializer serializer]];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    // Process Response Object

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    // Handle Error
}];

// Start Request Operation
[requestOperation start];

The AFJSONResponseSerializer class inherits from AFHTTPResponseSerializer and should be used for JSON responses. If you need to process XML responses, then AFXMLNResponseSerializer will help you out. For other content types, you need to subclass AFHTTPResponseSerializer.

AFHTTPRequestOperationManager

Another major change in AFNetworking 2.0 is the removal of the AFHTTPClient class, which was in charge of talking to web services. The responsibilities of AFHTTPClient have been split up into a number of classes and protocols. To talk to a web service, you now use AFHTTPRequestOperationManager and AFHTTPSessionManager. Just like AFHTTPRequestOperation, AFHTTPRequestOperationManager and AFHTTPSessionManager delegate serialization of requests and responses to separate objects. Both AFHTTPRequestOperationManager and AFHTTPSessionManager have a requestSerializer and responseSerializer property for this purpose. Let's revisit the above example to see how this works in practice.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/", key]];

// Initialize Request Operation Manager
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];

// Configure Request Operation Manager
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];

// Send Request
[manager GET:@"37.8267,-122.423" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    // Process Response Object

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    // Handle Error
}];

What About NSURLSession?

AFURLSessionManager

The key question when discussing AFNetworking 2.0 in the light of the NSURLSession API is whether we still need AFNetworking. AFNetworking adds a number of convenience classes and methods to the NSURLSession API and this is only possible thanks to the separation of responsibilities I discussed earlier. The most significant improvement that AFNetworking provides on top of the NSURLSession API are the AFURLSessionManager and AFHTTPSessionManager classes.

The AFURLSessionManager helps you manage an NSURLSession object. Even though I love the NSURLSession API, I have to agree with Mattt that its API is incomplete. The AFURLSessionManager class adds a number of convenience methods that make the NSURLSession API even better. Serialization and validation, for example, are much easier and intuitive with AFNetworking. Just like the AFHTTPRequestOperationManager, instances of AFURLSessionManager have a requestSerializer and responseSerializer property that makes serialization of requests and responses seamless and intuitive.

NSString *key = @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.forecast.io/forecast/%@/37.8267,-122.423", key]];

// Initialize Session Configuration
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

// Initialize Session Manager
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:sessionConfiguration];

// Configure Manager
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];

// Send Request
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
[[manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    // Process Response Object
}] resume];

I hope you can see the benefit of using AFNetworking in combination with the NSURLSession API. It makes working with the NSURLSession API much more intuitive especially if you're already used to the way AFNetworking 2.0 separates serialization and validation from the actual task or request operation.

AFHTTPSessionManager

AFNetworking 2.0 also incudes a subclass of AFURLSessionManager, AFHTTPSessionManager, which makes interacting with web services a breeze. The AFHTTPSessionManager class includes a number of conveniences methods, such as GET:parameters:success:failure: and POST:parameters:constructingBodyWithBlock:success:failure: that make the migration process from AFHTTPClient to AFHTTPSessionManager easy. Similar methods are also available in the AFHTTPRequestOperationManager class, which I discussed earlier.


Reachability

Reachability is managed by the AFURLRequestOperationManager and AFURLSessionManager classes. When instances of these classes have a valid baseURL, then a reachability manager is automatically instantiated and set up. Of course, it is also possible to explicitly create an instance of the AFNetworkReachabilityManager class.


Icing On The Cake

AFNetworking 2.0 has a number of other features, such as built-in support for SSL pinning and various categories on UIKit classes. Take a look at this example in which I leverage AFNetworking's category on UIProgressView to update a progress view while downloading a remote image.

// Initialize Request
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cdn.tutsplus.com/mobile/uploads/2014/01/5a3f1-sample.jpg"]];

// Initialize Session Manager
self.sessionManager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

// Initialize Download Task
NSURLSessionDownloadTask *downloadTask = [self.sessionManager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    // Handle Success

} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    // Handle Failure
}];

[self.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];

[downloadTask resume];

Verdict

If your project doesn't rely on networking, then you won't need all the power and convenience that AFNetworking has to offer. As a matter of fact, I think it's important that every iOS or OS X developer is familiar with NSURLSession and NSURLConnection, because they are key components of the platform.

Even though the NSURLSession API is great and easy to use, the networking APIs provided by the Foundation framework aren't perfect. As Mattt points out, even the NSURLSession API has a few notable gaps. AFNetworking tries to elegantly fill these gaps. The combination of the NSURLSession API and AFNetworking is a marriage made in heaven. It truly is a joy to use.

I really like the NSURLSession API and I hope I've convinced you of its power in this series. However, this doesn't mean that AFNetworking has become obsolete. In my opinion, AFNetworking is the perfect companion of the NSURLSession API. AFNetworking has matured and it is indeed the best and most robust networking library available. Its modular architecture means that you don't need to include every class or category it has to offer. You are free to pick and choose from the components that it includes. AFNetworking supports CocoaPods subspecs, which make this process trivial.


Conclusion

I couldn't be more happy with the second major release of AFNetworking. The library's modularity and separation of responsibilities is what makes it shine in my opinion. It complements the NSURLSession API so well that you won't even notice that you're using AFNetworking. Mattt Thompson and the more than 130 contributors of AFNetworking have done a formidable job. It's good to be a Cocoa developer.

2014-02-03T12:30:26.000Z2014-02-03T12:30:26.000ZBart Jacobshttp://code.tutsplus.com/tutorials/working-with-nsurlsession-afnetworking-20--mobile-22651

What Makes Mobile Design Unique?

$
0
0

Are you getting started with mobile design? Any designer who's had the opportunity to design for mobile devices as well as desktop computers knows that mobile design is a one of a kind discipline. You'll find challenges unique for mobile. In this introductory article, I'd like to take a look at some of the attributes that make mobile unique.

It's no secret that thinking about a product's design makes for a better product. This doesn't solely relate to aesthetics as design also influences how a product's functionality and how the user interacts with it. Design choices have a distinct influence on usability and user experience. When you start out as a mobile designer or developer, it's your responsibility to make the product not only work for you, but also for its target audience.


A Smaller Screen

The most obvious difference compared to other types of digital design is that we have a limited space to work with. Smartphones come in many forms and flavors and there's a lot of variety in screen sizes. Offering a similar experience on every mobile device can be a challenge. This isn't limited to the physical screens we hold in our hands and interact with. It also spans the pixel density of those screens. There are smartphones and tablets with a low pixel density all the way up to a very high pixel density.

Even though the screen is arguably one of the most important components of a mobile device, it is just one of hundreds of components that affect the user's experience. It has therefore become close to impossible to test an application on every device on the market.

This infographic shows the hardware fragmentation in the mobile market.

Touch & Gestures

Touch screens have the distinct capability to provide an immersive user experience. Swiping, tapping, pinching, squeezing, holding, and all kinds of other gestures can have a specific functionality depending on the application.

The gestures in Mailbox help substantially in creating an efficient and user-friendly email client.

The power and natural interaction that touch screens provide make them more efficient to use for specific tasks. Some tasks are better suited for mobile devices than for notebooks or desktop computers. I can only encourage you to make use of the possibilities that mobile devices offer over traditional computers, but be aware that they are as challenging as they are advantageous. Not every user is familiar with a gesture-based user interface and a gesture-based user interface has its challenges, such as discoverability. If you deviate a little too much from the standards set by the operating system, the usability of your application can run the risk of becoming a nightmare for the user.


Operating Systems

At the moment, Android and iOS are the main players in the mobile space. However, there are other players, such as Microsoft's Windows Phone, Mozilla's Firefox OS, BlackBerry's BlackBerry 10, and even Ubuntu is entering the mobile market. Creating an application and making it available on the major mobile platforms is a daunting undertaking.

Every platform has its own design standards and patterns. Managing a list of items on Android is different from managing a list on iOS. Sliding menus or side menus are typical for the iOS platform and are less common in Android applications. A tab bar at the bottom of the screen was a commonly used design pattern on iOS and introduced by Apple. Many applications, however, are moving away from this pattern. Each platform defines a set of standards of how buttons and symbols look like. Users who have been using a mobile operating system for a long time have come to expect to see these standards in every application. In other words, what you create for iOS might be perceived as strange or unconventional by Android users who have grown to like and appreciate the standards set by Android.

Another interesting challenge are the various flavors of a mobile platform. Even though this isn't much of a problem for the iOS platform, the situation is quite complex on Android and often referred to as Android fragmentation. Anything you design for the latest Android release may be incompatible with an older version, potentially affecting a significant group of devices.


Think Different

In the mobile space, the question that usually arises is "How?" How do you implement certain features on a device with a smaller screen? How do you ensure that your application works as good on an iPhone as it does on an iPad? How can you communicate to your users that double-tapping the screen brings up additional options?

Applications often have a very particular goal. They usually have one main feature. This is different from, for example, desktop or web applications. This difference is reflected in how we perceive and build mobile applications. They are usually very task-oriented and have a closed structure and flow.


Developers also have access to the device's hardware features, such as the built-in camera, location services, accelerometer, etc. This can truly enrich the experience and add capabilities to an application that are only found on mobile devices.


Different Ecosystem


The mobile space in its current form is built up of a number of closed ecosystems with some ecosystems being more closed than the others. Apple is the best known example of such an ecosystem. If you want to develop iOS applications, you'll have to do business with Apple. This includes going through Apple's notorious review process. This is Apple's approach to ensure a platform with high-quality applications. This also means splitting your profits with Apple. Google is a bit less strict compared to Apple. However, this doesn't mean that Google hasn't created a closed ecosystem. You can't run your iOS applications on an Android device and vice versa. This stands in stark contrast with the web. All you need on the web is a browser.


Conclusion

The importance of mobile has increased dramatically since the introduction of the iPhone in 2007. Consumers as well as corporations understand the potential of mobile devices.

The principles of mobile first have spread all over the digital landscape. Websites have become responsive. Enterprises have started creating and using mobile applications. Games are ported to mobile platforms. As such, mobile is hot right now and the whole industry is worth billions of dollars when you consider the various players in the space, hardware manufacturers, software companies, etc. The most exciting aspect of all this is that mobile is still in its infancy.

Mobile is a complex medium. Keep learning how to become a better designer or developer, even if you're just starting out. Mobiletuts+ is excited to be part of your journey.

2014-02-05T12:30:20.000Z2014-02-05T12:30:20.000ZSven Lenaertshttp://code.tutsplus.com/articles/what-makes-mobile-design-unique--mobile-22068

Wanted: Amazing Writers

$
0
0

Mobiletuts+ is looking for amazing writers who know their stuff. We are looking for people who are skilled in developing for the major mobile platforms, such as iOS, Android, and Windows Phone. However, we are also interested in mobile design and user experience, Xamarin and RubyMotion development, etc. If the topic you have in mind is related to mobile development, then we want to talk to you.


Who are we looking for?

  • Rock Solid Knowledge: Mobiletuts+ strives to be a highly respected source of information. Our authors must have excellent knowledge about their topic and be willing to perform thorough research if necessary.
  • Superb Writing Skills: You must be comfortable with the English language. We can proof your content, but we can't rewrite everything for you. To put it simply, if you struggle when deciding whether to write its or it's, then this gig isn't for you.

What's in it for you?

  • A full-length tutorial earns you between $200 and $250 USD, depending on length and quality.
  • For quick tips, we pay between $50 and $150 USD, depending on length and quality.
  • We are open to discuss rates for well-known authors. Don't hesitate to get in touch with us.

The exact rate is dependent on your industry experience and writing skills.


Where do I sign up?

If you're an expert in your field and you master the English language, then we'd like to know the following from you:

  • your name
  • a brief paragraph about your background and why you're a good fit for Mobiletuts+
  • a single link to an article that you've written, helped with, or like
  • at least two ideas for tutorials that you'd like to write for Mobiletuts+

Please email the above information to:

contact e-mail

We get quite a bit of single-line emails. Don't be that person. Emails without the required information will be discarded. We hope to hear from you soon.

2014-02-06T12:30:05.000Z2014-02-06T12:30:05.000ZBart Jacobshttp://code.tutsplus.com/articles/wanted-amazing-writers--mobile-22748

Create a Hangman Game: Project Setup

$
0
0

In this series, I will show you how to create a simple Hangman game for Android. In every tutorial, you'll learn a number of fundamental skills for creating Android applications. The application will consist of two screens, include user interaction involving adapters, contain dialogs and an action bar, and leverage XML for data storage. Let's get started.


Introduction

If you're a beginning Android developer with one or two basic applications under you belt, then you should be able to complete these tutorials without much difficulty.

The game will have a simple, basic structure so that you can add your own enhancements to it when you've completed the tutorials. Throughout this series, I will point out optional steps you can include to add additional functionality and to improve support for a wider range of Android devicces. The user interface, for example, will be simple, but I encourage you to give it more love to make the game more appealing. The following screenshot should give you an idea of what the final result will look like.

Android Hangman Game Final

This tutorial is split up into three distinct parts.

  • Project Setup
  • User Interface
  • Gameplay and Interaction

Our game will be a traditional Hangman game. It will choose a random word, presenting placeholders for each letter so that the user knows its length. The game will of course display a gallows area and buttons for each letter of the alphabet. Whenever the user taps a letter, the application will check whether the word includes the letter. If it does, the letter is revealed in the word. If the word doesn't contain the selected letter, one part of the body will be displayed in the gallows area, starting with the head. The user can make up to six wrong guesses and still win the game. However, if they make a seventh mistake, it's game over. If the player needs less than seven guesses, she wins the game. Are you still with me?


1. Create a New Android Project

Step 1

Create a new Android project in your IDE (Integrated Development Environment) of choice. In this tutorial, I will use Eclipse. In Eclipse, select New > Project from the File menu. In the Android section, select Android Application Project and click Next.

Android Hangman Game Project

Enter an application, project, and package name. To keep things simple, the application targets API 16, but you may wish to support older versions. If you decide to do so, you'll need to carry out some additional processing. Click Next to continue.

Android Hangman Game Project Properties

Step 2

In the Configure Project window, it is up to you to choose if you'd like to create a custom icon. However, make sure to check the Create activity and the Create Project in Workspace check boxes. Click Next.

Android Hangman Game Project Configure

Set a launcher icon and click Next. In the Create Activity screen, select Blank Activity and click Next.

Android Hangman Game Project Activity

You can modify the activity class and layout names if you prefer, but the default names work just fine as well. Click Finish to create your new project.

Android Hangman Game Project Finish

Now is a good time to configure the themes and styles associated with your application. To match the demo application, set the AppTheme in your res/values/ styles XML file to android:Theme.Holo. The default settings work fine, but your application's user interface will differ from the screenshots in this tutorial.


2. Create the User Interface Images

Step 1

Our application will present an intro screen with a play button to the user and a separate screen for the game itself. As you can see in the previous screenshot, the screen used for the game includes a number of images for the gallows and body parts. Beneath the gallows is the area for the word the user needs to guess and a grid with the letters of the alphabet. We will also use an action bar for navigating the game and it will also contain a help button.

We'll need quite a number of images for the gallows, the body parts (head, body, arms, and legs), the help button, and a background image for the intro screen. You can find these images in the source files of this tutorial, which you can find at the top of this tutorial. Keep in mind that you need to create additional images if you aim to target a broader set of devices with different screen sizes.

Download the images and add them to the drawable folders in your project directory. Make sure that you are using images suited for the various screen densities on Android.

Step 2

The application is also going to use some drawable resources that we can define as shapes in XML. These include the background for the letters in the word the user needs to guess and the background for the letters of the buttons (normal and highlighted state). These will also need to be tailored to the different screen densities that you want your application to support. The sample code of this tutorial gives you a good starting point.

Let's start with the background of the letters. Create a new file in your project drawables and name it letter_bg.xml.

Android Hangman Drawable

Enter the shape shown in the code snippet below.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="line" ><stroke
    android:width="2dp"
    android:color="#FF333333" /><solid android:color="#00000000" /><padding android:bottom="20dp" /><size
    android:height="20dp"
    android:width="20dp" /></shape>

If you aren't familiar with creating shapes in XML, this may seem a little confusing. The result is an underline, which will be displayed as the backgrond of each letter in the word the user needs to guess.

If you want to alter the dimensions of a shape to target different screen densities, consider using dimension resources. They allow you to use the same shape drawable XML code for all screen densities, but with each referring to dimension values tailored to the screen in use.

Next, create another file in your project's drawables for the letter buttons in their initial state and name it letter_up.xml. Add the following snippet to the file you've just created.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:padding="5dp"
  android:shape="rectangle" ><solid android:color="#FF333333" /><corners
    android:bottomLeftRadius="15dp"
    android:bottomRightRadius="15dp"
    android:topLeftRadius="15dp"
    android:topRightRadius="15dp" /><stroke
    android:width="2dp"
    android:color="#FFCCCCCC" /></shape>

Feel free to modify the above snippets to style the user interface elements to your liking. It is important to see the resources of this tutorial as a guideline. Create one more drawable and name it letter_down.xml. We'll use this drawable for the letter buttons in their highlighted state, that is, when the player taps the button.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:padding="5dp"
  android:shape="rectangle" ><solid android:color="#FF000000" /><corners
    android:bottomLeftRadius="15dp"
    android:bottomRightRadius="15dp"
    android:topLeftRadius="15dp"
    android:topRightRadius="15dp" /></shape>

The user can choose only one letter in each round of the game. This means that each letter or button is disabled from the moment the user has chosen the letter. This is reflected by updating the button's background.

We will change each button's background using Java, depending on the button's state, enabled or disabled. For more advanced button options, check out using button states.

3. Create the Main Layout

Step 1

Let's create the main layout file for the intro screen. Open the main layout file of the project and update its contents to reflect the snippet below.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#FF000000"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context=".MainActivity" ><ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:src="@drawable/android_hangman_bg" /><Button
    android:id="@+id/playBtn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="Play"
    android:textColor="#FFFF0000" /></RelativeLayout>

As you can see, the code refers to a drawable resource to display in the background. You can omit the ImageView element if you like. If you are including the image, make sure the filename matches this code. The layout includes a single button to play the game, but you can add other buttons later if you like. For example, you could add levels or categories to the application rather than simply choosing a random word from a list as we will do in this tutorial.

Step 2

Let's create a few more files that we'll use in the next tutorials. First, add a second activity class to the project for the game itself. Select the package in your project's src directory and choose New > Class from the File menu. Name the class GameActivity and set android.app.Activity as its superclass.

Android Hangman Gameplay Class

We will complete the GameActivity class later in this series. Let's now create a layout file for the class. Add a new file to the project's layout folder and name it activity_game.xml. Replace its contents with the following snippet below.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="fill_parent"
  android:background="#FF000000"
  android:orientation="vertical"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin" ></LinearLayout>

We will add items to the layout next time. Add the activity to your project manifest file, after the element for the main activity, inside the application element:

<activity
      android:name="com.example.hangman.GameActivity"
      android:parentActivityName="com.example.hangman.MainActivity"
      android:screenOrientation="portrait" />

We set the screen's orientation to portrait. If you decide to also support a landscape orientation in your game, then you need to remove the orientation attribute. We also specify the main activity as parent, which means that the action bar can be used for navigating the game. We will talk more about this a bit later in this tutorial.


4. Start the Game Activity

Step 1

Let's finish this tutorial by starting the gameplay activity when the user taps the play button. Open the file of the MainActivity class and begin by adding the following import statements.

import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

Eclipse should have prepopulated the MainActivity class with an Activity class outline, including an onCreate method in which the content view is set. After the setContentView line, retrieve a reference to the play button you added to the main layout and set the class to listen for clicks.

Button playBtn = (Button)findViewById(R.id.playBtn);
playBtn.setOnClickListener(this);

We can find the button by using the identifier we set in the layout XML. Extend the opening line of the class declaration to implement the click listening interface as follows.

public class MainActivity extends Activity implements OnClickListener

Step 2

Add the onClick method after the onCreate method as shown below.

@Override
public void onClick(View view) {
  //handle clicks
}

Inside this method, we check if the play button was clicked and, if it was, start the gameplay activity.

if (view.getId() == R.id.playBtn) {
  Intent playIntent = new Intent(this, GameActivity.class);
  this.startActivity(playIntent);
}

This won't have much of an effect at the moment since we haven't implemented the gameplay activity yet. That is something we'll do in the next tutorial.


Conclusion

In this tutorial, we've created the foundation for the Hangman application. In the next tutorial, we will build the game's interface elements and, in the final part, we will focus on the interactive aspects of the application. Along the way, we will explore adapters and the action bar. We will also store the word answers in XML, retrieving them when the app launches, and choosing one at random every time the user plays a game.

2014-02-07T12:31:08.000Z2014-02-07T12:31:08.000ZSue Smithhttp://code.tutsplus.com/tutorials/create-a-hangman-game-project-setup--mobile-21797

Create a Hangman Game: User Interface

$
0
0

In this series, we are creating a Hangman game for the Android platform. In the first tutorial, we set the application up to present two screens to the user and we also made a start with the user interface elements, the images and shape drawables to be precise. In the second tutorial, we will zoom in on the game's layout.


Introduction

Creating the game's layout will involve using an adapter to create letter buttons and positioning the body parts we will display when users select incorrect letters. We will also store the player's answers in XML, retrieve them, and choose a random word using Java.

To refresh your memory, this is what the final game will look like.

Android Hangman Game Final

1. Position the Body Parts

Step 1

In the previous tutorial, we created images for the gallows and for the six body parts. We will place these inside the game's layout in this tutorial. The positions you set for these elements need to be determined by the image elements that you are using. One approach is to import the images into a image editor, such as Adobe's Photoshop, position them manually, and then use their x and y positions relative to the gallows image to work out the correct positions to apply to each image in the XML layout. If you are starting with the demo images we included in the previous tutorial, you can use the values listed in this tutorial.

Start by opening activity_game.xml. Inside the linear layout that we added in the previous tutorial, enter a relative layout to hold the seven images that will make up the gallows area.

<RelativeLayout
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:background="#FFFFFFFF"
  android:gravity="center"
  android:paddingTop="15dp" ></RelativeLayout>

Step 2

Inside the relative layout you just created, start by adding the gallows image as shown below.

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/gallows"
  android:paddingLeft="0dp"
  android:paddingTop="0dp"
  android:src="@drawable/android_hangman_gallows" />

Remember to modify the drawable name if the image you're using is named differently. We set the image's left and top padding to 0 so that we can position the other images relative to its position. We'll add string resources for the content descriptions a bit later in this tutorial. Next up is the head.

<ImageView
  android:id="@+id/head"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/head"
  android:paddingLeft="108dp"
  android:paddingTop="23dp"
  android:src="@drawable/android_hangman_head" />

If you're using your own images, you'll need to alter the left and top padding accordingly. We use an id attribute so that we can refer to the image in code. This is necessary to make it appear and disappear depending on the user's input. The next image we add is the body.

<ImageView
  android:id="@+id/body"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/body"
  android:paddingLeft="120dp"
  android:paddingTop="53dp"
  android:src="@drawable/android_hangman_body" />

This looks very similar to what we did for the head and, as you can see below, the arms and legs are pretty similar.

<ImageView
  android:id="@+id/arm1"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/arm"
  android:paddingLeft="100dp"
  android:paddingTop="60dp"
  android:src="@drawable/android_hangman_arm1" /><ImageView
  android:id="@+id/arm2"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/arm"
  android:paddingLeft="120dp"
  android:paddingTop="60dp"
  android:src="@drawable/android_hangman_arm2" />
<ImageView
  android:id="@+id/leg1"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/leg"
  android:paddingLeft="101dp"
  android:paddingTop="90dp"
  android:src="@drawable/android_hangman_leg1" /><ImageView
  android:id="@+id/leg2"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/leg"
  android:paddingLeft="121dp"
  android:paddingTop="90dp"
  android:src="@drawable/android_hangman_leg2" />

Open the project's res/values strings XML file and add the content description strings that we've used in the above code snippets.

<string name="gallows">The Gallows</string><string name="head">The Head</string><string name="body">The Body</string><string name="arm">An Arm</string><string name="leg">A Leg</string>

Go back to the layout file and switch to the Graphical Layout tab to see the result of our work. Adjust the top and left padding of each image to adjust their position if necessary.

Android Hangman Game Layout

Whenever a new game is started, the body parts need to be hidden. Each body part is revealed if the player chooses a letter that is not present in the target word.


2. Store the Answer Words

The game will use a collection of predefined words, which we will store in XML. In your project resources values folder, add a new file and name it arrays.xml.

Android Hangman Game Arrays File

Switch to the XML tab, create an array, and add a few words to it.

<resources><string-array name="words"><item>CHARGER</item><item>COMPUTER</item><item>TABLET</item><item>SYSTEM</item><item>APPLICATION</item><item>INTERNET</item><item>STYLUS</item><item>ANDROID</item><item>KEYBOARD</item><item>SMARTPHONE</item></string-array></resources>
For this tutorial, we use one array of words related to computing. However, to make the game more interesting, you could work with various categories with each category containing words related to a specific theme. When the user clicks the play button, you could prompt them to choose a category and read in the relevant array of words.

3. Choose and Present a Word

Step 1

Back in your game's activity layout file, add a linear layout immediately after the relative layout we added for the gallows area. The linear layout is reserved for the answer area.

<LinearLayout
  android:id="@+id/word"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_marginBottom="5dp"
  android:background="#FFFFFFFF"
  android:gravity="center"
  android:orientation="horizontal"
  android:padding="10dp" ></LinearLayout>

We store each character of the target word within its own text view so that we can reveal each letter separately. We will use the id of the linear layout in code to add the text views each time a new word is chosen.

Step 2

Open the GameActivity class and start by adding the following import statements at the top.

import android.content.res.Resources;
import android.graphics.Color;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.TextView;

Inside the class declaration, add an onCreate method as shown below.

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_game);
}

We set the content view to the layout file we created a moment ago.

Step 3

Before we move on, we need to declare a few instance variables. Add the declaration immediately before the onCreate method.

private String[] words;
private Random rand;
private String currWord;
private LinearLayout wordLayout;
private TextView[] charViews;

The collection of words are stored in an array and the application uses the rand object to select a word from the array each time the user starts a new game. We keep a reference to the current word (currWord), the layout we created to hold the answer area (wordLayout), and an array of text views for the letters (charViews). Back in onCreate, after setting the content view, request the application's resources, read the collection of words, and store them into the words instance variable.

Resources res = getResources();
words = res.getStringArray(R.array.words);

Note that we use the name we gave the word array in XML. Initialize the rand object and currWord string.

rand = new Random();
currWord = "";

Get a reference to the layout area we created for the answer letters.

wordLayout = (LinearLayout)findViewById(R.id.word);

Step 4

A number of tasks need to be performed every time a new game is started by the player. Let's create a helper method to keep everything organized. After the onCreate method, add the following method outline.

private void playGame() {
  //play a new game
}

Inside the playGame method, start by choosing a random word from the array.

String newWord = words[rand.nextInt(words.length)];

Because the playGame method is invoked when the user chooses to play again after winning or losing a game, it is important that we make sure we don't pick the same word two times in a row.

while(newWord.equals(currWord)) newWord = words[rand.nextInt(words.length)];

Update the currWord instance variable with the new target word.

currWord = newWord;

Step 5

The next step is to create one text view for each letter of the target word. Still inside our helper method, create a new array to store the text views for the letters of the target word.

charViews = new TextView[currWord.length()];

Next, remove any text views from the wordLayout layout.

wordLayout.removeAllViews();

Use a simple for loop to iterate over each letter of the answer, create a text view for each letter, and set the text view's text to the current letter.

for (int c = 0; c < currWord.length(); c++) {
  charViews[c] = new TextView(this);
  charViews[c].setText(""+currWord.charAt(c));
}

The string is stored as an array of characters. The charAt method allows us to access the characters at a specific index. Still inside the for loop, set the display properties on the text view and add it to the layout.

for (int c = 0; c < currWord.length(); c++) {
  charViews[c] = new TextView(this);
  charViews[c].setText(""+currWord.charAt(c));

  charViews[c].setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  charViews[c].setGravity(Gravity.CENTER);
  charViews[c].setTextColor(Color.WHITE);
  charViews[c].setBackgroundResource(R.drawable.letter_bg);
  //add to layout
  wordLayout.addView(charViews[c]);
}

We set the text color to white so that the user will not be able to see it against the white background. If they guess the letter correctly, the text color property is se to black to reveal it to the player. Back in the onCreate method, call the helper method we just created.

playGame();

We'll expand both the onCreate and the helper method a bit later.


4. Create the Letter Buttons

Step 1

The next step is to create an area for the player to choose letters to guess. Revisit the game's activity layout and add the following grid view to hold the letter buttons. Insert the grid view immediately after the linear layout we added for the answer word.

<GridView
  android:id="@+id/letters"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:layout_marginBottom="5dp"
  android:background="#FF000000"
  android:horizontalSpacing="5dp"
  android:numColumns="7"
  android:padding="5dp"
  android:stretchMode="columnWidth"
  android:verticalSpacing="5dp" />

We're going to use an adapter to map the letters of the alphabet to buttons in the grid view. We lay out seven buttons per row as you can see from the numColumns attribute.

Step 2

Each letter is going to be a button added to the layout using an adapter. Add a new file in your project layout folder, name it letter.xml, and populate it with the following code snippet.

<Button xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="35dp"
  android:background="@drawable/letter_up"
  android:onClick="letterPressed" />

We use one of the drawable shapes we created last time as background and set an onClick method we will implement next time. If you are enhancing the application to target different screen densities, you can consider tailoring the height attribute accordingly.

Step 3

Add a new class to your project's src package, name it LetterAdapter, and choose android.widget.BaseAdapter as its superclass.

Android Hangman Game Adapter Class

As you'll see, an adapter class includes a series of standard methods we will implement. Add the following import statements to the new class.

import android.content.Context;
import android.view.LayoutInflater;
import android.widget.Button;

Inside the class declaration, add two instance variables as shown below.

private String[] letters;
private LayoutInflater letterInf;

The letters array will store the letters of the alphabet and the layout inflater will apply the button layout we defined to each view handled by the adapter. After the instance variables, add a constructor method for the adapter.

public LetterAdapter(Context c) {
  //setup adapter
}

Inside the constructor, instantiate the alphabet array and assign the letters A-Z to each position.

letters=new String[26];
for (int a = 0; a < letters.length; a++) {
  letters[a] = "" + (char)(a+'A');
}

Each character is represented as a number so that we can set the letters A to Z in a loop starting at zero by adding the value of the character A to each array index. Still inside the constructor method, specify the context in which we want to inflate the layout, which will be passed from the main activity later.

letterInf = LayoutInflater.from(c);

Eclipse should have created a getCount method outline. Update its implementation as follows.

@Override
public int getCount() {
  return letters.length;
}

This represents the number of views, one for each letter. We don't call the methods in the adapter class explicitly within the application. It's the operating system that uses them to build the user interface element we apply the adapter to, which in this case will be the grid view.

Update the implementation of getView as shown in the code snippet below.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
  //create a button for the letter at this position in the alphabet
  Button letterBtn;
  if (convertView == null) {
    //inflate the button layout
    letterBtn = (Button)letterInf.inflate(R.layout.letter, parent, false);
  } else {
    letterBtn = (Button) convertView;
  }
  //set the text to this letter
  letterBtn.setText(letters[position]);
  return letterBtn;
}

Take a moment to let everything sink in. Essentially, this method builds each view mapped to the user interface element through the adapter. We inflate the button layout we created and set the letter according to the position in the alphabet that the view represents. We have stated that there will be 26 views being mapped, with the position of each reflecting its position in the alphabet array. You can leave the other methods in the adapter class as they are.

@Override
public Object getItem(int arg0) {
  return null;
}

@Override
public long getItemId(int arg0) {
  return 0;
}

Step 4

Revisit the game's activity class and add an instance variable for the grid view and the adapter.

private GridView letters;
private LetterAdapter ltrAdapt;

You also need to add another import statement.

import android.widget.GridView;

In the onCreate method, before the line in which you call the playGame helper method, get a reference to the grid view.

letters = (GridView)findViewById(R.id.letters);

Append the playGame's current implementation with the following snippet. In this snippet, we instantiate the adapter and set it on the grid view.

ltrAdapt=new LetterAdapter(this);
letters.setAdapter(ltrAdapt);

Run the application in the emulator and you should see the user interface. However, you won't be able to interact with the buttons yet. That's what we will focus on in the third and final installment of this series.

Android Hangman Game

Conclusion

If you run your application at this point, it will present you with the game's interface, but it won't respond to user interaction yet. We will implement the remaining functionality in the final part of this series.

When a player clicks the letter buttons, the application needs to verify if the selected letter is included in the target word and update the answer and gallows accordingly. We will also present a dialog to the player if they win or lose, and we'll also add a help button. Finally, we'll add basic navigation through an action bar.

2014-02-10T12:30:15.595Z2014-02-10T12:30:15.595ZSue Smithhttp://code.tutsplus.com/tutorials/create-a-hangman-game-user-interface--mobile-21853

In the Spotlight: Jeremy Olson

$
0
0
tag:code.tutsplus.com,2005:PostPresenter/mobile-22695

Jeremy Olson is the founder of Tapity and the company's lead designer. Tapity focuses on designing, developing, and marketing mobile applications. Jeremy and his team don't create just any mobile application, they craft software that stands out in a crowded App Store and closely follow Apple's philosophy with respect to design and attention to detail.

Grades, an application for helping students keep track of how they're doing at school, won an Apple Design Award in 2011 and Languages, one of their most popular applications, was an Editor's Choice in 2012 and reached the top charts in the App Store. Tapity currently focuses solely on iOS, and as you'll learn in the interview, this is a deliberate choice.


Q.What did you do before you started Tapity and got into iOS development?

When I was ten, I wanted to build games and my dad gave me some programming books. I started learning programming and began building some games, mainly for my own pleasure. When I was a teenager, I got into web development and built websites for people.

My first contact with mobile was when I was twelve or thirteen. I really liked the Palm devices of that time and I thought PDAs were the future. At that time, I didn't think about phones as I was twelve and didn't really need a phone. Back then, twelve-year-olds didn't have phones.

Jeremy is the founder of Tapity.

I had some PalmPilots and there was this guy that got in touch with me, who was a developer for Palm software. I built a website for him and, because he liked my work, he wanted me to help design one of his apps. That was my first experience with mobile and I really enjoyed it.

For the next six or seven years, I was doing web development and I didn't even think about mobile until it became huge in 2007, when the iPhone came out.

Q.What made you choose for the iOS platform? Did you start developing iOS applications from the moment the iPhone was introduced?

I actually tried. When the iPhone was first introduced, you could only build web apps, so I tried to build a few of those. I even built a website to help people find web apps for the iPhone, an App Store for web apps so to speak.

When the SDK (Software Development Kit) came out in 2008, I tried to jump into that, but the learning curve was quite high coming from web development. At that time, I was also a full-time student and working at a local web development firm. I didn't have a whole lot of time to commit to learning the SDK, but I did try to learn iOS development.

I tried and failed two or three times, got frustrated with it, and eventually gave up. Hopefully that's encouraging to some people.

Q.Even though the iPhone was introduced only a few years ago, a lot has changed during that time. As someone running a business in the App Store, does it scare you that everything changes so fast?

It's a little bit scary, because things do change quickly and it's a tough market. It's certainly not a get-rich-quick market and there's a ton of competition. The market is changing very quickly and you really have to adapt and evolve depending on how the market changes.

In recent years, there's been a trend for people to download a lot more free apps. I wrote about this on my blog and gave it a somewhat controversial title, Yep, paid apps are dead. It was my response to something Marco Arment wrote about his observations on the App Store.

I've been talking to a lot of people who have top apps in the App Store and the consensus is that the whole paid app market is having trouble, because a lot of people are downloading free instead of paid apps. It's not that people aren't downloading apps, they're just downloading less paid apps.

About a year ago, Languages got to #5 overall in the US App Store. This resulted in a lot of downloads. But talking to people who have recently hit #5 in the App Store, they are getting a fraction of the downloads that Languages received. People still download paid apps, just not in the volume they used to.

Languages hit the #5 in the US App Store.

That doesn't mean that the App Store is dead. It just means that a lot more people are downloading free apps, they're looking for free alternatives. That's just one example of something where developers have to evolve.

I think we cheated a little bit with the mentality that you can just put an app on the App Store for 99 cents and strike it rich if you're lucky. That's not going to happen anymore. Even if you put an app out on the App Store for 99 cents and it gets to the very top, you still aren't going to get that rich as the volume just isn't there anymore for paid apps.

Q.Do you think a subscription-based model is a viable business model in the App Store?

It is definitely worth experimenting with. Though certain apps don’t lend themselves to a subscription model, especially novelty apps that people are going to use once or twice. There are a few examples of subscription-based models that work—iTranslate comes to mind—but they are definitely difficult to pull off in consumer apps.

That being said, a subscription model is a lot more valuable, because you get recurring revenue. Getting a dollar for an app is just not viable for a business. You need to sell at least 100,000 copies to make it worth your time and that's really hard to do. It's not impossible, but it's difficult to do. A subscription-based model is a good model to experiment with in the App Store.

Q.At the moment, Tapity focuses solely on iOS. Is that a deliberate choice and will that change in the future?

It was a natural choice, because I've been using Macs since I was a kid, before they were cool. It was therefore natural to do stuff the Apple way and Apple's culture really vibes with our culture as a company. Like Apple, Tapity is all about design. We really care about the details. We really care about polish and making apps that are enjoyable to use, which is in line with Apple's philosophy.

In fact, I'm sure we got a lot of our principles from Apple's philosophy. This also means that it just makes sense for us to build apps for Apple's platform, because they appreciate and feature that in the App Store. And they give Apple Design Awards to companies like that.

We've appreciated that over the years and their culture matches with us, which is why we've stuck with them. And I do have to say that there's something to exclusivity as well. It certainly doesn't hurt our relation with Apple that our apps have been only available on iOS. We have a really good relationship with Apple and that's something I consider a huge asset for us. I'm careful about that.

That's not to say that we won't build apps for Android. However, I wouldn't want to build an Android app unless I was using Android myself, because I realize that the experience is different. As a designer, I need to empathize with the users of my app and, if I'm using a completely different platform, then how can I design for users of that platform? It's not a small thing for us to do. We feel that it's a real commitment.

Q.This ties back to the App Design Handbook you recently released with Nathan Barry. Building for mobile isn't just opening Photoshop and filling the canvas with buttons and text fields, is it?

One thing that's tempting on mobile is to jump straight into the code. You've got an idea for an app and you start building it. That can work in some cases, but a designed approach is going to generally work a lot better.

One of the challenges is not realizing that it requires a design process. It needs to be designed and that includes a number of things. Let me walk you through our design process at Tapity.

We start by breaking the app into three categories, strategic design, interaction design, and visual design. These all happen at different moments during the design process, but you need a start on one to start the rest.

The App Design Handbook has been updated for iOS 7.

During the strategic design process, we really try to get to know our users by creating personas. You don't have to go overkill, but empathize with your users as much as possible by learning as much about them as you can. Any people you can talk to about your ideas for an app can help with this.

We create personas or fictional characters that represent characteristics of users that we feel we should capture. For example, Sarah is a lawyer who tracks her hours by six minute increments and we basically fill out what her life is like to put ourselves in her mind. When we're talking about a feature, it's not "Users would like this." It's "What would Sarah like?" and "What would Sarah think about this?"

During the strategic phase, we go through their experience with and without the app. If this app were magic, how could it help them in their day-to-day experience? We then discuss what we can really do for them by going over what's realistic. At the end of this phase, we end up with a definition of 1.0.

We then enter the interaction design process. At Tapity, we don't make it very formal. A lot of people create formal wireframes and we did this with clients, because we believed that they needed a more formal approach. However, we felt that we got stuck in that phase, because people would nitpick the wireframes when you really shouldn't. Wireframes are only used to get the interactions down.

These days, we use whiteboards to map the whole app out. It's a rough sketch of the app, which we iterate on. Whiteboards make this very easy and quick. We use an app, Pop, that lets us take pictures from our sketches and link them together to get an idea of how the app will look and feel.

Of course, interaction design continues as you're getting into visual design, because you iterate a ton once you actually see how it's going to look.

The intersection between interaction and visual design is crucial. Visual design informs interaction design and vice versa so you can’t completely disconnect the two. Visual design is really where we iterate the most. It's probably not as efficient as iterating interaction design, but it gives you a more complete picture of what's going on.

Q.Let's talk about the elephant in the room. How do you feel about iOS 7 and the direction Apple has taken with iOS?

The right direction is hard to define. I appreciate the fact that they did something radical and I think that's cool. It's always fun when things gets shaken up a bit, whether you like it or not. It provides new challenges and it makes it so that new players can come onto the scene that are doing innovative things that the established players can't catch up to or are too slow to do.

In general, I like iOS 7. Especially in the beginning, there were certain things I didn't like. I know a few people at Apple and they're definitely open to feedback. But the general direction is set. It's going to mature over time, but I think it's a good new direction that mixes things up and gives us new challenges.

As with any new platform or any new design direction, people tend to do exactly what Apple does. When iOS was first released and the SDK became available, everything looked exactly like Apple's apps. But then, over time, things matured and people started experimenting, creating novel experiences by iterating on Apple's designs. That's what needs to happen with iOS 7.

I've seen some apps that start to go in that direction, but I think the vast majority, as they're scrambling to fit into iOS 7, take a very conservative approach by doing what Apple is doing. But that's not even what Apple wants.

You'd think Apple wants everything to look like their apps, but they know that if everything looks the same it's going to be boring. I've talked to people at Apple about this. They want diversity and they know that it will come eventually, it just takes some time.

Q.If you were starting out with mobile development today, what advice would you give yourself?

Based on my own experience, a big factor is going to be the drive to do it. If you don't have the drive to do it, then you're not going to do it. Like I said, I failed a couple of times learning iOS development, because the learning curve is fairly steep.

For me, that happened when I had the idea for my first app. It wasn't just a vague "I want to learn iOS development." It was "I want to build this thing and and I'm going to do whatever it takes to build it."

Instead of telling yourself "I'm going to learn iOS development.”, tell yourself "I'm going to build this product and I'm going to learn iOS development to do it." And that, I think, gives you the drive to keep going.

Grades is a perfect fit for any student.

It also makes learning iOS development a lot more concrete. It's not just "That's how you create a table. But I don't know if I need a table.” Instead, it's "I'm trying to build this app and I need a table. Okay. This is how you build a table and I put it in my app."

The first prototype of Grades was nothing more than a bunch of tutorials hacked together, bent and twisted to do my will. After I learned the principles and learned how to do things properly, I trashed the prototype and rewrote the app. Having the app as a way to apply the tutorials and apply the things I was learning really made it a lot
easier and it made me learn quicker.

Q.Thank you for taking the time to share your experiences with the readers of Mobiletuts+, Jeremy. Where can people find you online?

It was a pleasure. You can follow me on Twitter and I regularly write on Tapity's blog about iOS development, marketing, and design.

2014-02-12T12:30:20.673Z2014-02-12T12:30:20.673ZBart Jacobshttp://code.tutsplus.com/articles/in-the-spotlight-jeremy-olson--mobile-22695

Create a Hangman Game: User Interaction

$
0
0

In this series, we are creating a Hangman game for the Android platform. So far, we've built the application's user interface, including images, drawables, and layouts. In this third and final installment, we are going to focus on user interaction.


Introduction

Adding user interaction to the game involves several aspects, including detecting whether the user wins or loses a game as well as responding to each event. We'll also add a help button to the action bar and add the ability to navigate the game.

To refresh your memory, this is what the final game will look like.

Android Hangman Game Final

1. Prepare the User Interface

Step 1

As we saw in the previous tutorial, the game activity presents the gallows area with the six body parts drawn to the screen. When a new game starts, the body parts need to be hidden, only showing them when the user makes a wrong guess. Open the game's activity class and start by adding the following import statements to it.

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.v4.app.NavUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;

Next, declare five instance variables in the class's interface.

//body part images
private ImageView[] bodyParts;
//number of body parts
private int numParts=6;
//current part - will increment when wrong answers are chosen
private int currPart;
//number of characters in current word
private int numChars;
//number correctly guessed
private int numCorr;

You could change the number of body parts if, for example, you'd like to add several levels of difficulty to the game. By storing the current body part (currPart), we can add one body part at a time in case the player makes a wrong guess. We use the letter count of the target word and the number of right guesses to keep track of the player's progress in the current game. We periodically check if the player has won or lost the game.

In the onCreate method of the game's activity class, right before we invoke playGame, we instantiate the image view array and fetch the body part images we placed in the layout. This code snippet also determines the order in which the body parts are shown when the player makes a wrong guess. We start with the head and end with the legs.

bodyParts = new ImageView[numParts];
bodyParts[0] = (ImageView)findViewById(R.id.head);
bodyParts[1] = (ImageView)findViewById(R.id.body);
bodyParts[2] = (ImageView)findViewById(R.id.arm1);
bodyParts[3] = (ImageView)findViewById(R.id.arm2);
bodyParts[4] = (ImageView)findViewById(R.id.leg1);
bodyParts[5] = (ImageView)findViewById(R.id.leg2);

Step 2

In the playGame method, append the following code snippet. We set currPart to 0, set numChars to the length of the target word, and set numCorr to 0.

currPart=0;
numChars=currWord.length();
numCorr=0;

Before we start the game, the body parts need to be hidden.

for(int p = 0; p < numParts; p++) {
  bodyParts[p].setVisibility(View.INVISIBLE);
}

The next screenshot shows how the game should look when a new game is about to begin.

Android Hangman Game Start

2. Respond to User Clicks

Step 1

When we created the layout for the letter buttons, we declared an onClick method. Let's add this to the game's activity.

public void letterPressed(View view) {
  //user has pressed a letter to guess
}

When the player taps a letter button to make a guess, letterPressed receives a reference to the view. This allows us to figure out which letter the player has chosen. To find out which letter the player has tapped, we use the following code snippet.

String ltr=((TextView)view).getText().toString();

Next, we get the character from the string.

char letterChar = ltr.charAt(0);

We also disable the letter button and update the background drawable to show the player that the letter has already been played.

view.setEnabled(false);
view.setBackgroundResource(R.drawable.letter_down);

In the next step, we loop through the characters of the target word to verify whether the player's guess is in it. Each letter of the target word is compared with the player's guess. If the player's guess matches a letter in the target word, we increment numCorr, set correct to true to indicate that the player made a good guess, and update the letter's text color from white to black to make it visible. The for loop continues until every letter of the target word has been checked. This is important as a letter can occur more than once in the target word.

boolean correct = false;
for(int k = 0; k < currWord.length(); k++) {
  if(currWord.charAt(k)==letterChar){
    correct = true;
    numCorr++;
    charViews[k].setTextColor(Color.BLACK);
  }
}
Android Hangman Game Last Guess

Step 2

Next, we need to verify whether the player has won or lost the game after their guess, or made a wrong guess but can still continue. Still inside letterPressed, start by checking if the player has made a good guess.

if (correct) {
  //correct guess
}

If she did make a good guess, check if she's guessed all the letters of the target word.

if (numCorr == numChars) {
  //user has won
}

If this is true, we notify the player that she's won the game. The first thing we do is disabling the letter buttons. We do this by implementing another helper method, disableBtns. Implement this method after letterPressed.

public void disableBtns() {
  int numLetters = letters.getChildCount();
  for (int l = 0; l < numLetters; l++) {
    letters.getChildAt(l).setEnabled(false);
  }
}

In disableBtns, we loop through the views via the adapter and disable each button. If the user has won the game, we invoke disableBtns and display an alert dialog to the user. In the alert dialog, we also ask the player if they want to play another game.

Take a moment to look over this if you're not familiar with dialogs on Android. We set the properties including title and a message including confirmation of the correct answer. We add a button to play again to the alert dialog, which calls the playGame method. We also add a button to exit, which takes the player back to the main activity.

if (numCorr == numChars) {
  // Disable Buttons
  disableBtns();

  // Display Alert Dialog
  AlertDialog.Builder winBuild = new AlertDialog.Builder(this);
  winBuild.setTitle("YAY");
  winBuild.setMessage("You win!\n\nThe answer was:\n\n"+currWord);
  winBuild.setPositiveButton("Play Again",
    new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        GameActivity.this.playGame();
    }});

  winBuild.setNegativeButton("Exit",
    new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        GameActivity.this.finish();
    }});

  winBuild.show();
}
Android Hangman Game Win

Step 3

If the user hasn't won the game, we need to verify if she has answered incorrectly, but still has some guesses left. Inside the else if block, we show the next body part and increment the number of incorrect guesses with 1.

if (correct) {
  //correct guess

} else if (currPart < numParts) {
  //some guesses left
  bodyParts[currPart].setVisibility(View.VISIBLE);
  currPart++;
}

Step 4

After the else if, we can safely assume that the player has lost the game. We start by disabling the buttons as we did earlier and we display an alert dialog stating that the player has lost the game. We also include the correct answer and offer the option to play another game.

else{
  //user has lost
  disableBtns();

  // Display Alert Dialog
  AlertDialog.Builder loseBuild = new AlertDialog.Builder(this);
  loseBuild.setTitle("OOPS");
  loseBuild.setMessage("You lose!\n\nThe answer was:\n\n"+currWord);
  loseBuild.setPositiveButton("Play Again",
    new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        GameActivity.this.playGame();
    }});

  loseBuild.setNegativeButton("Exit",
    new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        GameActivity.this.finish();
    }});

  loseBuild.show();
}

Believe it or not, we're done implementing the user interaction aspect of the game. All that's left for us to do is add a few enhancements to the interface.

Android Hangman Game Lose

3. Complete the Action Bar

Step 1

Let's finish up this tutorial by adding a help button to the action bar. I won't go into too much detail, but feel free to experiment with this in your own applications. Depending on the API levels you target, support for navigating by means of the action bar is provided with little or no coding. To ensure the action bar allows navigating back to the main activity, add the following inside the onCreate method in the game's activity.

getActionBar().setDisplayHomeAsUpEnabled(true);

In the manifest, remember that we specified the main activity as the parent of the game's activity. This tells the operating system that navigating back/up from the game's activity should bring the user back to the main activity. Your project should have a main menu resource. Open it and take a look at its contents. By default, it will have a settings action, which we don't need for our game. Insert the following code snippet, to add a help button.

<item
  android:id="@+id/action_help"
  android:icon="@drawable/android_hangman_help"
  android:showAsAction="ifRoom"
  android:title="help"/>

Remember that we listed the help icon in the first tutorial of this series. You can add more buttons to your action bar later if you like. If you do, you'll need to extend the activity code we cover below.

We don't need the action bar functions in the main activity so if Eclipse added the onCreateOptionsMenu method to your main activity class, feel free to remove it.

Step 2

Back in the game's activity, set the screen to use the main menu by adding the following method.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
}

Add another method to specify what should happen when the user tries to navigate back/up or press the help button in the action bar.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
  case android.R.id.home:
    NavUtils.navigateUpFromSameTask(this);
    return true;
  case R.id.action_help:
    showHelp();
    return true;
  }

  return super.onOptionsItemSelected(item);
}

Navigating up will take the user back to the main activity. The android.R.id.home action corresponds to the call to setDisplayHomeAsUpEnabled we added in onCreate.

Step 3

Add another instance variable at the top of the class for the help information.

private AlertDialog helpAlert;

We'll use another helper method to show the help information.

public void showHelp() {
  AlertDialog.Builder helpBuild = new AlertDialog.Builder(this);

  helpBuild.setTitle("Help");
  helpBuild.setMessage("Guess the word by selecting the letters.\n\n"
      + "You only have 6 wrong selections then it's game over!");
  helpBuild.setPositiveButton("OK",
    new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
        helpAlert.dismiss();
    }});
  helpAlert = helpBuild.create();

  helpBuild.show();
}
Android Hangman Game Help
The action bar support we've included will only work for API levels of 11 and up, and navigation support will only work for API levels of 16 and up. To support older versions, you'll need to use the support libraries for the action bar and navigation.

Conclusion

Our Hangman game should now be complete. Run it and give it a spin. The application in its current form will only work on recent versions of Android and the user interface is only suited for a limited range of devices. As you can see, there is room for improvement. In addition to supporting a wider ranger of devices, you could enhance the game by adding difficulty levels or word categories. You could even create a leaderboard to keep track of scores.

If you've completed this series successfully, you should have learned about several aspects of the Android platform, including user interaction, adapters, XML resources, and leveraging the action bar.

2014-02-14T12:30:12.160Z2014-02-14T12:30:12.160ZSue Smithhttp://code.tutsplus.com/tutorials/create-a-hangman-game-user-interaction--mobile-21893

Blocks and Table View Cells on iOS

$
0
0


A table view cell doesn't know about the table view it belongs to and that's fine. In fact, that's how it should be. However, people who are new to this concept are often confused by it. For example, if the user taps a button in a table view cell, how do you obtain the index path of the cell so you can fetch the corresponding model? In this tutorial, I'll show you how not to do this, how it's usually done, and how to do this with style and elegance.

1. Introduction

When the user taps a table view cell, the table view invokes tableView:didSelectRowAtIndexPath: of the UITableViewDelegate protocol on the table view delegate. This method accepts two arguments, the table view and the index path of the cell that was selected.

The problem that we're going to tackle in this tutorial, however, is a bit more complex. Assume we have a table view with cells, with each cell containing a button. When the button is tapped, an action is triggered. In the action, we need to fetch the model that corresponds with the cell's position in the table view. In other words, we need to know the index path of the cell. How do we infer the cell's index path if we only get a reference to the button that was tapped? That's the problem we'll solve in this tutorial.

2. Project Setup

Step 1: Create Project

Create a new project in Xcode by selecting the Single View Application template from the list of iOS Application templates. Name the project Blocks and Cells, set Devices to iPhone, and click Next. Tell Xcode where you'd like to store the project and hit Create.

Select Project Template
Configure Project

Step 2: Update Deployment Target

Open the Project Navigator on the left, select the project in the Project section, and set the Deployment Target to iOS 6. We do this to make sure that we can run the application on both iOS 6 and iOS 7. The reason for this will become clear later in this tutorial.

Configure Project Settings

Step 3: Create UITableViewCell Subclass

Select New > File... from the File menu and choose Objective-C class from the list of Cocoa Touch templates. Name the class TPSButtonCell and make sure it inherits from UITableViewCell.

Create UITableViewCell Subclass
Create UITableViewCell Subclass

Open the class's header file and declare two outlets, a UILabel instance named titleLabel and a UIButton instance named actionButton.

#import <UIKit/UIKit.h>

@interface TPSButtonCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *actionButton;

@end

Step 4: Update View Controller

Open the header file of the TPSViewController class and create an outlet named tableView of type UITableView. The TPSViewController also needs to adopt the UITableViewDataSource and UITableViewDelegate protocols.

#import <UIKit/UIKit.h>

@interface TPSViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

We also need to take a brief look at the view controller's implementation file. Open TPSViewController.m and declare a static variable of type NSString that we'll use as the reuse identifier for the cells in the table view.

#import "TPSViewController.h"

@implementation TPSViewController

static NSString *CellIdentifier = @"CellIdentifier";

// ... //

@end

Step 5: User Interface

Open the project's main storyboard, Main.Storyboard, and drag a table view to the view controller's view. Select the table view and connect its dataSource and delegate outlets with the view controller instance. With the table view still selected, open the Attributes Inspector and set the number of Prototype Cells to 1. The Content attribute should be set to Dynamic Prototypes. You should now see one prototype cell in the table view.

Select the prototype cell and set its Class to TPSButtonCell in the Identity Inspector. With the cell still selected, open the Attributes Inspector and set the Style attribute to Custom and the Identifier to CellIdentifier.

Drag a UILabel instance from the Object Library to the cell's content view and repeat this step for a UIButton instance. Select the cell, open the Connections Inspector, and connect the titleLabel and actionButton outlets with their counterparts in the prototype cell.

Setting Up the User Interface

Before we dive back into the code, we need to make one more connection. Select the view controller, open the Connections Inspector one more time, and connect the view controller's tableView outlet with the table view in the storyboard. That's it for the user interface.

3. Populating the Table View

Step 1: Create a Data Source

Let's populate the table view with some notable movies that were released in 2013. In the TPSViewController class, declare a property of type NSArray and name it dataSource. The corresponding instance variable will hold the movies that we'll show in the table view. Populate dataSource with a dozen or so movies in the view controller's viewDidLoad method.

#import "TPSViewController.h"

@interface TPSViewController ()

@property (strong, nonatomic) NSArray *dataSource;

@end
- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup Data Source
    self.dataSource = @[
                        @{ @"title" : @"Gravity", @"year" : @(2013) },
                        @{ @"title" : @"12 Years a Slave", @"year" : @(2013) },
                        @{ @"title" : @"Before Midnight", @"year" : @(2013) },
                        @{ @"title" : @"American Hustle", @"year" : @(2013) },
                        @{ @"title" : @"Blackfish", @"year" : @(2013) },
                        @{ @"title" : @"Captain Phillips", @"year" : @(2013) },
                        @{ @"title" : @"Nebraska", @"year" : @(2013) },
                        @{ @"title" : @"Rush", @"year" : @(2013) },
                        @{ @"title" : @"Frozen", @"year" : @(2013) },
                        @{ @"title" : @"Star Trek Into Darkness", @"year" : @(2013) },
                        @{ @"title" : @"The Conjuring", @"year" : @(2013) },
                        @{ @"title" : @"Side Effects", @"year" : @(2013) },
                        @{ @"title" : @"The Attack", @"year" : @(2013) },
                        @{ @"title" : @"The Hobbit", @"year" : @(2013) },
                        @{ @"title" : @"We Are What We Are", @"year" : @(2013) },
                        @{ @"title" : @"Something in the Air", @"year" : @(2013) }
                        ];
}

Step 2: Implement the UITableViewDataSource Protocol

The implementation of the UITableViewDataSource protocol is very easy. We only need to implement numberOfSectionsInTableView:, tableView:numberOfRowsInSection:, and tableView:cellForRowAtIndexPath:.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.dataSource ? 1 : 0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource ? self.dataSource.count : 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TPSButtonCell *cell = (TPSButtonCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Fetch Item
    NSDictionary *item = [self.dataSource objectAtIndex:indexPath.row];

    // Configure Table View Cell
    [cell.titleLabel setText:[NSString stringWithFormat:@"%@ (%@)", item[@"title"], item[@"year"]]];
    [cell.actionButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];

    return cell;
}

In tableView:cellForRowAtIndexPath:, we use the same identifier that we set in the main storyboard, CellIdentifier, which we declared earlier in the tutorial. We cast the cell to an instance of TPSButtonCell, fetch the corresponding item from the data source, and update the cell's title label. We also add a target and action for the UIControlEventTouchUpInside event of the button.

Don't forget to add an import statement for the TPSButtonCell class at the top of TPSViewController.m.

#import "TPSButtonCell.h"

To prevent the application from crashing when a button is tapped, implement didTapButton: as shown below.

- (void)didTapButton:(id)sender {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

Build the project and run it in the iOS Simulator to see what we've got so far. You should see a list of movies and tapping the button on the right logs a message to the Xcode console. Great. It's time for the meat of the tutorial.

4. How Not To Do It

When the user taps the button on the right, it will send a message of didTapButton: to the view controller. You almost always need to know the index path of the table view cell that the button is in. But how do you get that index path? As I mentioned, there are three approaches you can take. Let's first look at how not to do it.

Take a look at the implementation of didTapButton: and try to find out what's wrong with it. Do you spot the danger? Let me help you. Run the application first on iOS 7 and then on iOS 6. Take a look at what Xcode outputs to the console.

- (void)didTapButton:(id)sender {
    // Find Table View Cell
    UITableViewCell *cell = (UITableViewCell *)[[[sender superview] superview] superview];

    // Infer Index Path
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    // Fetch Item
    NSDictionary *item = [self.dataSource objectAtIndex:indexPath.row];

    // Log to Console
    NSLog(@"%@", item[@"title"]);
}

The problem with this approach is that it's error prone. On iOS 7, this approach works just fine. On iOS 6, however, it doesn't work. To make it work on iOS 6, you'd have to implement the method as shown below. The view hierarchy of a number of commons UIView subclasses, such as UITableView, has changed in iOS 7 and the result is that the above approach doesn't produce a consistent result.

- (void)didTapButton:(id)sender {
    // Find Table View Cell
    UITableViewCell *cell = (UITableViewCell *)[[sender superview] superview];

    // Infer Index Path
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    // Fetch Item
    NSDictionary *item = [self.dataSource objectAtIndex:indexPath.row];

    // Log to Console
    NSLog(@"%@", item[@"title"]);
}

Can't we just check if the device is running iOS 7? That's a very good idea. However, what will you do when iOS 8 changes the internal view hierarchy of UITableView yet again? Are you going to patch your application every time a major release of iOS is introduced? And what about all those users that don't upgrade to the latest (patched) version of your application? I hope it's clear that we need a better solution.

5. A Better Solution

A better approach is to infer the cell's index path in the table view based on the position of the sender, the UIButton instance, in the table view. We use convertPoint:toView: to accomplish this. This method converts the button's center from the button's coordinate system to the table view's coordinate system. It then becomes very easy. We call indexPathForRowAtPoint: on the table view and pass pointInSuperview to it. This gives us an index path that we can use to fetch the correct item from the data source.

- (void)didTapButton:(id)sender {
    // Cast Sender to UIButton
    UIButton *button = (UIButton *)sender;

    // Find Point in Superview
    CGPoint pointInSuperview = [button.superview convertPoint:button.center toView:self.tableView];

    // Infer Index Path
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInSuperview];

    // Fetch Item
    NSDictionary *item = [self.dataSource objectAtIndex:indexPath.row];

    // Log to Console
    NSLog(@"%@", item[@"title"]);
}

This approach may seem cumbersome, but it actually isn't. It's an approach that isn't affected by changes in the view hierarchy of UITableView and it can be used in many scenarios, including in collection views.

6. The Elegant Solution

There is one more solution to solve the problem and it requires a bit more work. The result, however, is a display of modern Objective-C. Start by revisiting the header file of the TPSButtonCell and declare a public method named setDidTapButtonBlock: that accepts a block.

#import <UIKit/UIKit.h>

@interface TPSButtonCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UIButton *actionButton;

- (void)setDidTapButtonBlock:(void (^)(id sender))didTapButtonBlock;

@end

In the implementation file of TPSButtonCell create a private property named didTapButtonBlock as shown below. Note that the property attributed is set to copy, because blocks need to be copied to keep track of their captured state outside of the original scope.

#import "TPSButtonCell.h"

@interface TPSButtonCell ()

@property (copy, nonatomic) void (^didTapButtonBlock)(id sender);

@end

Instead of adding a target and action for the UIControlEventTouchUpInside event in the view controller's tableView:cellForRowAtIndexPath:, we add a target and action in awakeFromNib in the TPSButtonCell class itself.

- (void)awakeFromNib {
    [super awakeFromNib];

    [self.actionButton addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
}

The implementation of didTapButton: is trivial.

- (void)didTapButton:(id)sender {
    if (self.didTapButtonBlock) {
        self.didTapButtonBlock(sender);
    }
}

This may seem like a lot of work for a simple button, but hold you horses until we've refactored tableView:cellForRowAtIndexPath: in the TPSViewController class. Instead of adding a target and action to the cell's button, we set the cell's didTapButtonBlock. Getting a reference to the corresponding item of the data source becomes very, very easy. This solution is by far the most elegant solution to this problem.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TPSButtonCell *cell = (TPSButtonCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Fetch Item
    NSDictionary *item = [self.dataSource objectAtIndex:indexPath.row];

    // Configure Table View Cell
    [cell.titleLabel setText:[NSString stringWithFormat:@"%@ (%@)", item[@"title"], item[@"year"]]];

    [cell setDidTapButtonBlock:^(id sender) {
        NSLog(@"%@", item[@"title"]);
    }];

    return cell;
}

Conclusion

Even though the concept of blocks has been around for decades, Cocoa developers have had to wait until 2011. Blocks can make complex problems easier to solve and they make complex code simpler. Since the introduction of blocks, Apple has started making extensive use of them in their own APIs so I encourage you to follow Apple's lead by taking advantage of blocks in your own projects.

2014-02-17T12:30:25.956Z2014-02-17T12:30:25.956ZBart Jacobshttp://code.tutsplus.com/tutorials/blocks-and-table-view-cells-on-ios--mobile-22982
Viewing all 1836 articles
Browse latest View live