Showing posts with label Software Development. Show all posts
Showing posts with label Software Development. Show all posts

Wednesday, March 5, 2014

Punch up your iOS 7 app's wow factor by using UIKit Dynamics

Your can improve the user experience of your iOS 7 apps by incorporating UIKit Dynamics features into your development work. Marcio Valenzuela warns: just don't overdo it

 
ios7-logo.jpg
UIKit Dynamics adds a new level of features to the overall user experience (UX) of iOS 7 apps. The API is, however, something to be used sparingly and only when needed; otherwise, you might get user complaints about the bounciness of a table view or the Lock Screen camera button, for example.
It's simple to incorporate UIKit Dynamics into your iOS 7 app. Plus, the end result has a really big wow factor that is certain to impress your users. Let's take a quick conceptual drive around UIKit Dynamics.
First, we adopt the protocol into the ViewController, which will implement UIKit Dynamics. Why? Because the objects that will be animated in the end will have to send back a lot of signals like "Hey, I collided with a boundary" or "Hey, I just hit somebody else and I was going this fast, in this direction." In order to receive these messages, we use a delegate and its callbacks.

@interface … <UICollisionBehaviorDelegate>
 
We need to create the view to animate and the property to reference a UIDynamicAnimator, which is the object in charge of handling animations in UIKit Dynamics.

@property (nonatomic, weak) IBOutlet UIView *square1;
@property (nonatomic) UIDynamicAnimator* animator;
 
Basically, we would prep all we need in viewDidLoad, such as instantiating an animator to call the shots inside a particular reference view. Then, we create a behavior or set of behaviors we wish to assign to our animatable view. We define boundaries so we can keep our objects inside a view. Finally, we add the behaviors to the animator and set the viewcontroller as the delegate as well as set that animator object to our property in order to hold a reference to it.
Now we sit back and get messages from the animator and the animated view via the callbacks.

- (void)viewDidLoad
{
[super viewDidLoad];
IDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
U
UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[self.square1]];
UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.square1]];
collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
or:collisionBehavior]; collisionBehavio
[animator addBehavior:gravityBeahvior]; [animator addBehav ir.collisionDelegate = self; self.animator = animator; }
tForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p { // Lighten the background color when the view is in contact w
-(void)collisionBehavior:(UICollisionBehavior *)behavior beganConta cith a boundary. [(UIView*)item setBackgroundColor:[UIColor lightGrayColor]]; } -(void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier
{ // Restore the default color when ending a contcact. [(UIView*)item setBackgroundColor:[UIColor grayColor]];
}
 

A simple example

Let's say we are building a restaurant rating app. It's a single view app with a plain vanilla UIViewController that has these properties connected to those outlets:

@property (nonatomic, strong) IBOutlet UILabel *restaurantName;
@property (nonatomic, strong) IBOutlet UILabel *restaurantAddress;
//STAR RATING
tomic, strong) IBOutlet UIImageView *stars1; @property (non
@property (non aatomic, strong) IBOutlet UIImageView *stars2;
iew *stars3; @property (nonatomic, strong) IBOutlet UIImage
@property (nonatomic, strong) IBOutlet UIImage VView *stars4;
tomic, strong) IBOutlet UIImageView *stars5;
@property (non a
 
Create the animator that will handle the animation inside our viewDidLoad:

// Create animator
UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
 
Add the views we want to animate to the behaviors we want to implement:

//Create behaviors
UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[self.stars1,self.stars2, self.stars3, self.stars4,self.stars5]];
UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.stars1,self.stars2, self.stars3, self.stars4,self.stars5]];
IDynamicItemBehavior* propertiesBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.stars1,self.stars2, self.stars3, self.stars4,self.stars5]]; prop
UertiesBehavior.elasticity = 5;
 
The last behavior is actually created to modify certain physical properties of an object, in this case elasticity. There are other properties we can modify in this manner.
We can also add specific boundaries, but in many cases we will want to simply use the view's edges as the natural boundaries, so we use this line:

collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
 
Add the behaviors to the animator, set the delegate to self, and reference our animator through its property:

[animator addBehavior:gravityBeahvior];
[animator addBehavior:collisionBehavior];
ollisionBehavior.collisionDelegate = self;
c
self.animator = animator;
 
Finally, add the following delegate callbacks to decide what gets done when a collision occurs:

-(void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p
{ // Lighten the background color when the view is in contact with a boundary. [(UIView*)item setBackgroundColor:[UIColor lightGrayColor]]; }
// Restore the default color when ending a contcact. [(UIView*)item setBackgroundColor:[UIColor grayColor]]; }
-(void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier
{
 
That's it! There are lots of neat effects you can use, but don't overdo it, or your users might complain that you gave them vertigo.

Monday, November 18, 2013

Ackuna turns the crowd loose on app localization

Get your app translated through Ackuna. Learn how the crowdsourcing process works and how Ackuna deals with issue of translation accuracy.


The Internet is a worldwide phenomenon and that has caused increasing headaches for application developers as more and more devices connect. Smartphones and tablets in particular have extended computing's power to huge swaths of people who never had the inclination or money to buy computers. In turn, that has created a challenge with localization -- that is, making apps sensitive to language and the nuances of language.

Just consider the differences in reading direction. Where many languages are read from left to right, others are read right to left. Then there's the issue with capitalization. According to Microsoft's Developer Network page, when using ASCII, simply adding or detracting 0x0020 to a letter's corresponding "code point," you can create uppercase and lowercase letters. It's not so for Latin numbers and letters with accents, meaning you can't just add or subtract a single value to and from characters to get the results you're looking for. 

Other challenges include formatting, user interface, and string-related issues, as well as:
  • Code pages that list character codes in a specific order have to use, in some cases, special identifiers to reference the code pages. In other cases, as with Chinese, Japanese, and Korean, the double-byte character sets needed won't allow the combinations of these languages.
  • Complex scripts like Arabic, Hebrew, Thai, Vietnamese, and the Indic family require special considerations to get the right displays.
  • Hard-coded font names and font sizes result in fonts not being displayed correctly, or being displayed illegibly.
  • Input Method Editors that allow people who can't use standard 101-key keyboards (because they don't accommodate their languages) have to be supported in the application.
  • Line and word break algorithms are different for Asian DBCS languages and Western languages.
  • Function and short-key combinations have to be carefully thought out because of the differences in keyboards across the globe.

Using the crowd to translate

Underlying all of that is what many might consider to be the simplest aspect of making apps localized: language translation. The reality is, it's not all that simple, and the founders of Ackuna, initially a side project of Translation Cloud, continue to evolve the options available for translating.
MatthewBramowicz111513.jpg
"As the side project of Translation Cloud, Ackuna started off as a way for us to assign proofreading jobs to freelance translators," said Matthew Bramowicz, VP of operations at Ackuna. "So, say someone used Google Translate and it was a messy translation, they could have it perfected at Ackuna. As Ackuna gained a community we decided to switch gears and use Ackuna's existing framework to provide app developers with crowdsourced translation. The business model at this point is based off of Translation Cloud's professional services. We maintain Ackuna as an active translation community for free, but it also serves as a door to Translation Cloud. So, developers using Ackuna can also opt to have their projects professionally translated as an upgraded option. Also, the translators using Ackuna for practice can gain employment through Translation Cloud if they gain enough merit. The translation industry is competitive, and Ackuna is our unique way of gaining attention and nurturing discussion about translation and our services."

The process

At its heart, Ackuna is currently a "gamified learning experience" for about 5,600 translators and 480 developers, but it's also a community where translators can network with each other and with potential clients. The translation process begins when a developer posts a project. The system is getting two to three new projects posted each day. Then, the crowd of translators go to work, with the turn-around time depending upon how much the developer promotes the project on social media, how completely the project was described and illustrated, and the number of languages selected.
ackunacurrent projects_rev_111513.jpg
Translations are free, with translators performing their craft for not only the experience and community aspects but also to take part in application development. Michael Duke, marketing intern at Ackuna, says translators are also inspired to translate apps they've heard about but can't use because they're not yet localized to their regions.

Addressing the accuracy question

MichaelDuke111513.jpg
To ensure accuracy, Ackuna uses a voting system where translations must receive a given number of positive votes. Duke says that as the user base grows so does the accuracy and the speed of the translations. He says the mobile applications being translated are not extraordinarily text-heavy, and the social nature of the service helps to normalize the translations, even though there may be more variations across translations in a crowdsourced environment. Ackuna also encourages developers to provide as much context as they can in the form of screen shots and descriptions.
 
The service is considering making it possible for project posters to specify the geographic locations of their app's translators. However, Ackuna's social nature already allows developers to message specific translators if they happen to like their translation style, or even if they find out they are from specific regions that the app is aimed at.

Popular languages

Bramowicz emphasizes that it's best to understand what markets you want to get into before making a stab at localizing, as entering some regions is unlikely to be cost-effective or time-efficient. Currently, he says the romance languages such as French and Spanish are being translated quickly and that most apps arrive at the service in English. Chinese, Japanese, and Russian are other hot "translate to" languages.

The most difficult translations are those that come in unique language pairs. An example is a project translating Arabic to Chinese. While there may be large numbers of users on either side, there aren't that many translators.

More about the user base

So far, Ackuna is attracting mostly individual developers who don't necessarily have the budget for professional translation or who are exploring the prospect of localization. Bramowicz says that about 70% of projects are first-time projects and the remaining 30% are from developers posting multiple projects. While he admits it's possible that wholesalers could use the service to have multiple translations done that are then sold to others, he says it's unlikely, as developers could see their projects being uploaded to the service. He also says it wouldn't be very time-efficient, since social translation is slower and feeds off the developer's involvement. Those paying for translations would tend to expect them to be completed quickly.
ackuna_growth_chart111513.jpg
Ackuna currently covers most mobile platforms and supports 10 file formats and 22 languages. Duke says jobs that don't get translated are often ones where the developers don't promote or don't provide enough context for the translators to translate accurately. Another reason translations don't get completed is when the developer decides to choose translations for every single language offered. In doing that, they may get three or four languages completely translated, but since they don't get all 22 languages completed, the project can't be counted as a total completion. Still, it's hard to say such jobs will never get completed, and for others it's rare they will take more than a few weeks.

Create your own web service for an iOS app, part two

This installment in our iOS development series details how to connect to the web service you created and start fetching data.

In part one of our series on how to create the web service backend for the iGlobe app, we created the web database, the web service backend, and the iOS frontend (Storyboard). Now we'll focus on connecting to the web service and fetching actual data. This tutorial will work in iOS 6 or iOS 7.

Step 4: Fetch data

a. NSURLConnection

First, let's add some properties to our UsersListViewController.m:

@interface UsersListViewController () 
@property (nonatomic, strong) NSMutableArray *testArray;
@property (nonatomic, strong) NSMutableData *buffer;
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView  *spinner;
@property (nonatomic, strong) NSURLConnection *myConnection;
 
We're going to use NSURLConnection to create a web connection and fetch data, so let's change our viewDidLoad method to look like this:

- (void)viewDidLoad{
    [super viewDidLoad];
    // Animate the spinner
    [self.spinner startAnimating];
    // Create the URL & URLRequest
    NSURL *myURL = [NSURL URLWithString:@"http://www.yourserver.com/iglobe/getusers.php"];
    NSURLRequest *myRequest = [NSURLRequest requestWithURL:myURL];
    // Create the connection
    self.myConnection = [NSURLConnection connectionWithRequest:myRequest delegate:self];
    //Test to make sure the connection worked
    if (self.myConnection){
        self.buffer = [NSMutableData data];
        [self.myConnection start];
    }else{
        NSLog(@"Connection Failed");
    }
}
 
Now NSURLConenction has four delegate methods you must implement. Be sure to add the to our @interface line for UsersListViewController.h (or in .m -- just make sure it's the @interface line and not the @implementation line). Let's add the first method:

# pragma NSURLConnection Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    
    [self.buffer setLength:0];
}
 
This method is called when the app receives a response from the server. We'll simply reset the buffer length. Now we must deal with each time the app receives data from the server:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    
    [self.buffer appendData:data];
    
}
 
We append the received data to the existing buffer data.  Let's handle any error response from the server as well:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    // Do cleanup
    self.myConnection = nil;
    self.buffer     = nil;
    
    // Inform the user, most likely in a UIAlert
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
 
Next, implement the method that is called when the connection finishes loading data from the web server:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"Succeeded!");
    //Create a queue and dispatch the parsing of the data
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Parse the data from JSON to an array
        NSError *error = nil;
        NSArray *jsonString = [NSJSONSerialization JSONObjectWithData:_buffer 
options:NSJSONReadingMutableContainers error:&error];
        
        // Return to the main queue to handle the data & UI
        dispatch_async(dispatch_get_main_queue(), ^{
            
            //Check if error or not
            if (!error) {
                //If no error then PROCESS ARRAY
                self.testArray = [[NSMutableArray alloc] initWithCapacity:50];
                for (NSDictionary *tempDictionary in jsonString) { 
 // Extract each dictionary’s username & put it into our array
                    [self.testArray addObject:[tempDictionary objectForKey:@"username"]];
                }
                // Call reload in order to refresh the tableview
                [self.tableView reloadData];

            }else{
                NSLog(@"ERROR %@", [error localizedDescription]);
            }
            
            //Stop animating the spinner
            [self.spinner stopAnimating];
            
            // Do cleanup
            self.myConnection = nil;
            self.buffer     = nil;
        });

    });
}
 
The important bit is that we've seen the web response logged in our console, thus we know it's an array. We also know the array has dictionaries at each index; therefore, we must loop or iterate through each NSDictionary entry in the array and fetch the "username" key's value or object. We add that object to our self.testArray with each new iteration. In the end, we refresh our tableview to use the newly populated self.testArray with our usernames.

Great, so we can read information from our webservice. As we will see later, reading more complex data from the webservice is just a matter of creating a more complex request string on the iOS side and putting it together with some logic in the php server side. This is one way to fetch data, using NSURLConnection directly in a viewDidLoad. It's better than calling NSURLConnection on the main thread, but we want to make sure our code is re-usable, particularly the web fetch code since it's probably code we will want to use again in future projects. That's the reason we created the SantiappsHelper class.

The Users class is a container for our individual players. Its interface looks like this:

#import 


@interface Users : NSObject {
	
}
@property (nonatomic,copy) NSString *userName;
@property (nonatomic,copy) NSString *userPoints;

-(id)initWithUserName:(NSString*)userName userPoints:(NSString*)userPoints;
@end 
and its very simple implementation looks like this:
#import "Users.h"


@implementation Users


-(id)initWithUserName:(NSString*)nameOfUser userPoints:(NSString*)userPoints;
{
	if ( (self = [super init]) == nil )
        return nil;
	self.userName = nameOfUser;
	self.userPoints = userPoints;
	return self;
}

@end

b. GCD and completion blocks

We want to make our code a bit neater and more portable; we'll achieve this by creating our SantiappsHelper Class. This helper class will concentrate on fetching data from the web. It's very similar to other class files you have worked with before, but basically it has only Class Methods.

#import 
#import "Tag.h"

typedef void (^Handler)(NSArray *users);
typedef void (^Handler2)(NSArray *points);
typedef void (^Handler3)(NSArray *usersPointsArray);

@interface SantiappsHelper : NSObject {
}

+(void)fetchUsersWithCompletionHandler:(Handler)handler;

+(void)fetchPointForUsersArray:(NSArray*)usersArray WithCompletionHandler:(Handler3)handler;

+ (BOOL)postNewTag:(Tag*)passingObject;// from gamebumpconnector

@end
 
These three methods do the following: (1) fetchUsersWithCompletionHandler will fetch the list of users in the game, (2) fetchPointForUsersArray: WithCompletionHandler: will fetch the points for all users, and (3) postNewTag will create a new location tag for a particular user.

The idea is that the user will exchange tokens or tags with another user. Physically the two users at the same location will bump phones to initiate a tag worth two points. As the data is exchanged, each user account will post a 2-point value tag to the database. Originally the game was built to create tags individually at locations, but you can't very well call it a game of tag if you don't tag a second user, can you? 

Let's review what these methods do.

// THIS METHOD FETCHES USER ARRAY
+(void)fetchUsersWithCompletionHandler:(Handler)handler {
	
    NSString *urlString = [NSString stringWithFormat:@"http://www.myserver.com/myApp/getusers.php"];
    NSURL *url = [NSURL URLWithString:urlString];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:
NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
    
    [request setHTTPMethod: @"GET"];

    __block NSArray *usersArray = [[NSArray alloc] init];

    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Peform the request
        NSURLResponse *response;
        NSError *error = nil;
        NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                     returningResponse:&response
                                                                 error:&error];
        if (error) {
            // Deal with your error
            if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
                NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
                return;
            }
            NSLog(@"Error %@", error);
            return;
        }
        
        NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:
NSUTF8StringEncoding];
        //NSLog(@"responseString fetchUsers %@", responseString);
        
        usersArray = [NSJSONSerialization JSONObjectWithData:
[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];

        //Returns handler
        if (handler) {
            dispatch_async(dispatch_get_main_queue(), ^{
                handler(usersArray);
            });
        }
    });
}

//Fetches points for users array
+(void)fetchPointForUsersArray:(NSArray*)usersArray WithCompletionHandler:(Handler3)handler{
    NSError *error = nil;
    
    NSData *data = [NSJSONSerialization dataWithJSONObject:usersArray options:0 error:&error];
    
    if (error)
        NSLog(@"%s: JSON encode error: %@", __FUNCTION__, error);
    
    // create the request
    NSURL *url = [NSURL URLWithString:@"http:/ /www.myserver.com/myApp/readpointsforarray.php"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:data];
    
    __block NSArray *pointsArray = [[NSArray alloc] init];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        // Peform the request
        NSURLResponse *response;
        NSError *error = nil;
        
        // issue the request
        NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:
&response error:&error];
        
        if (error) {
            // Deal with your error
            if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
                NSLog(@"HTTP Error: %d %@", httpResponse.statusCode, error);
                //return;
            }
            NSLog(@"Error %@", error);
            //return;
        }
        
        NSString *responseString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
        //NSLog(@"asyncrhonous: %@",responseString);
        
        pointsArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:
NSASCIIStringEncoding] options:0 error:nil];
        //NSLog(@"pointsArray %@", pointsArray);
        
        if (handler){
            dispatch_async(dispatch_get_main_queue(), ^{
                handler(pointsArray);
            });
        }
    });
}

// Called from MKViewController, creates shared tags points=2
+ (BOOL)postNewTag:(Tag*)passingObject{
    //1.  Log the tag for verification first
	NSLog(@"passingObject:%@,%@,%@,%@,%@",passingObject.sender, 
passingObject.receiver, passingObject.rglatitude, passingObject.rglongitude, passingObject.rgcountry);
	//NSLog(@"tagReceived:%@,%@,%@,%@",tagReceived.originUdid, 
tagReceived.destintyUdid, tagReceived.rglatitude, tagReceived.rglongitude);
 
	//2.REBUILD status string from passingObject
	NSString *s1 = [[NSString alloc] initWithFormat:@"sender=%@&latitude=%@&longitude=%@&country=%
@&receiver=%@&points=2",passingObject.sender,passingObject.rglatitude,passingObject.rglongitude,
passingObject.rgcountry,passingObject.receiver]; 
 
 //3.  Post tag to cloud
    NSData *postData = [s1 dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@
"http:/ /www.myserver.com/myApp/writephp.php"]];
    [request setURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:postLength forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];
    
    NSURLResponse *response;
    NSError *error;
	// We should probably be parsing the data returned by this call, for now just check the error.
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSLog(@"success!");
	[s1 release];
    return (error == nil);
}
fetchUsersWithCompletionHandler:
At the beginning of our helper class, we defined three typedefs; this just means we're defining a type, which we called Handler. We'll see in a minute what these are used for. Basically, this method creates a GET type request, which will be used to GET our users from the web service we created. It creates a block array to be used inside the block to store our returned data. Before executing the block (which contains the NSURLConnection method call), we dispatch the operation to the background queue and execute it there. This means that a slow or busy server will not block our main thread. The main thread is responsible for drawing operations and user interaction. If a heavy-duty operation such as fetching data from a web server or processing images and video were to be run on the main thread, the user would not be able to interact with the app until that task was complete.

The handler is the typedef that is created once the method has completed; therefore, we check to see if it exists. Once it does exist, we call back to the main thread, returning the now populated usersArray. That usersArray will be used to fill our list of users in our tableview.
fetchPointForUsersArray: WithCompletionHandler

This method is responsible for sending in an array of users and fetching their points back. It talks to the last php file we created and returns the usersArray, which contains a dictionary with users and their points.

postNewTag
The final method is in charge of logging a newly created tag and then posting it to the database.

We have one method for writing data to the database (postNewTag) and two methods for reading data from the database (fetchUsers… and fetchPointForUsersArray…). We read and write points (or Tags) and read users. We must have a way to write users; we'll cover this later in a class called ModalViewController, which will accept a login from a user to create a new user.  

Before we bump phones and exchange tags we must be able to create tags, so let's create a Tag Class and its ViewController and plot them on the map view. Once we have that, we will save our user information to our app and be ready to bump.

In the next installment of this series, we'll look into creating tags and storing them so we can exchange them.

Sunday, November 17, 2013

Create your own web service for an iOS app, part one

Follow the steps in this tutorial to learn how to create the web service backend for an iOS app called iGlobe.

In my previous TechRepublic series, we used an existing web service to create an iOS app ("Creating a web service, parts one and two"). In this series, we'll create the web service backend for a different app called iGlobe, which is a game I created a while ago.

Basically, we want the user to be able to tag a place or a person and get points for that action. (The competitor with the most points at the end of the game wins a money pot.) In order to do this, the app has to interact with a web service, which we'll create. Our web service will need to be able to:
  • Store each player's username information
  • Receive users' points
  • Present users' points
In this iOS 6/7 tutorial we'll use a helper class called SantiappsHelper, which contains the code to run these connections to the web. A standalone class in such cases is usually called a library, which takes care of those processes. If you only require one instance of such a class (like in our case), you create a Singleton pattern. You only want one instance of the connection, because you don't want many instances of the connection class creating, receiving, and disconnecting -- that could end up in multiple connections to the same resource at different times, which could confuse you or the server.

Here's what we’ll cover in this series:
1. Create the web database
2. Create the web service backend
3. Create iOS frontend (Storyboard)
4. Fetch data
  • a.  NSURLConnection
  • b.  GCD and completion blocks
5. Add the Bump API
6. Throw in social

Step 1: Create the web database

Web services are usually large databases of information; our database will need a table to store all the information we mentioned above. We interact with databases in four main ways: Create, Read, Update, and Delete (CRUD) data. So let's take a short detour and talk about databases -- specifically, their structure and how we interact with them.

Databases

Databases are information stores, which can be written as files (such as Word or PowerPoint). The information in such files has a predetermined structure that Word and PowerPoint know how to read and access in order to present what you want and let you edit it and store it again; the problem is only Word will read a docx file, and only PowerPoint will read a pptx file. The great advantages of databases are they store information in a very compact way and can be read by many different interfaces. The simpler the database, the more interfaces can read it.

We'll use a database that is usually available for free in most web hosting services. My web hosting service has phpMySQL, which comes included with a free package. If you want other databases such as MSSQL, you need a paid service. Figure A is what my database management interface looks like.
Figure A
iGlobeFigA_100113.png

See an enlarged view of Figure A.
We have a database named iglobe on localhost with two tables: users and tags. The users table (Figure B) contains a primary key with a username, a password, a password hint, a first and last name, as well as an email, a phone number, an address, and such regular stuff.
Figure B
iGlobeFigBb_100113.jpg

See an enlarged view of Figure B.
The tags table (Figure C) also has its own primary key (tagID), the corresponding username, an identifier, the tag's latitude and longitude, the date it was created, and how many points it's worth to that user. There is also a country field, which was implemented later as the project progressed (it's been in the works since 2011).
Figure C
iGlobeFigC_100113.png

See an enlarged view of Figure C.

Step 2: Create the web service backend

The idea for our web service will be to read from these tables and write to them whatever data users request or post to them. This part requires you to know some PHP. Let's start by looking at what the code to read a table looks like.

 
encode($arr);

?>
 
First, we include the JSON.php file in order to access JSON files on your server (make sure your webserver or host provides you with at least PHP 5.2). Then, we make a connection to the database using the database user and password as well as the database host. Now we create an array object so once we execute the mysql_query where all entries from the users table are collected into $rs, we can put that object into our $arr[] object. Finally, we encode the $arr into $json and echo it onto the screen.

Once this code is up and ready along with your database (including some records), you can direct your browser to this file (which I called myserver.com/getusers.php). I get the following result:

[{"id":"35","username":"zlitsami ","password":"932d1c42a4e4880e57037994fd3584b1","password_hint":"",
"lastname":"","firstname":"","email":"joe@iglobe.com","phone":"","address1":"","address2":"","city":"","state":"
","zip":"","country":"","url":"","permissions":"1","udid":"9","userCreated":"2013-01-01 14:27:22","time_queued"
:null,"time_sent":null}, {another}, {another}]
 
This is an array that has many elements in it. Each element is a user's table entry. Each entry is a dictionary of key value pairs. Look familiar?
Now that we know how to read information from our database, let's create the code for writing to the database.

 
[sender]','$_POST[latitude]','$_POST[longitude]','$_POST[country]','$_POST[receiver]','$_POST[points]')";

if (!mysql_query($sql,$con))
  {
  die('Error: ' . mysql_error());
  }

echo "1 record added to tags";


mysql_close($con)
?>
 
We connect to our database again, and we create a sql statement with values to insert (these values come from a form that was either online or on a mobile device).  We execute that sql statement with our connection and echo the results for verification to the user. I called this file writephp.php.
Before we move onto iOS, let's test our service online. Create an HTML file called Writeform.html and save this code to it:







Name:
UDID(unnecessary):
Latitude:
Longitude:
Country
Receiver
    
 
Now load the form on your web browser and submit data to your database.
I don't want to make this web service too complicated because I want to keep your attention on the iOS side, so let's create a form to eventually read points from our web service for a particular user. Create another HTML file called Testform.html and save this code to it:

 



User:
And create its php counterpart:

'$username' GROUP BY username");
// THIS RETURNS ARRAY NOT READ PROPERLY BY iOS JSON
$resultado = array();
while($obj = mysql_fetch_object($result)) {
	$resultado[] = $obj;
}
Echo $json->encode($resultado);

?>
We'll use this last bit of code later once we get more data into the database.
So far, we have a resource that returns the points for a particular user, readpoints.php; this is what's called a web service endpoint. Web services can have many endpoints. In a game or an app, we might want to get a lot of users' points at once to fill up a leader board, for example. We might want to fetch a lot of transactions from an invoice database instead of one by one. So let's get ahead of ourselves and create an endpoint to manage a set of input data. In our case, we must be able to pass the web service a set of users. Our file would look something like this:
 
 "ok", "code" => 0, "original request" => $post_data);
else
    $response = array("status" => "error", "code" => -1, "original_request" => $post_data);

//2. CALL DB QUERY
$link = mysql_pconnect("localhost", "username", "password") or die("Could not connect");
mysql_select_db("iglobe") or die("Could not select database");

//3. CREATE FINAL ARRAY TO RETURN
$arrayToReturn = array();

//4. CYCLE THROUGH USERS
foreach ($post_data as $value) 
{
  //CREATE QUERY
  $result = mysql_query("SELECT username, SUM(points) AS PUNTOS FROM tags WHERE username=
'$value' GROUP BY username");

  //EXECUTE QUERY & ADD EACH USER/POINTS DICTIONARY TO $resultado ARRAY
  $resultado = array();
  while($obj = mysql_fetch_object($result)){
	$arrayToReturn[] = $obj;
  }
}
Echo $json->encode($arrayToReturn);
?>
 
This basic php code takes the passed in array as we mentioned and loops through the database to get the points for each user. This is important because we save the app a lot of trips to the web server database.

Step 3: Create iOS frontend (Storyboard)

We'll now work on our iOS Storyboard or frontend. Then we'll hardcode data and fetch web from the actual backend; this way, we can see what our frontend will require in terms of data models, and then we can fetch web data and replace our data in those data models. We'll also be learning two ways of fetching data: inline, messy code and neat and tidy coding.
Follow these steps:
  1. Create a new Empty project using Storyboards, ARC, iPhone, and NO Core Data.
  2. Go to the storyboard and drag a UITableViewController onto the grid.
  3. Create a class called UsersListViewController. In Storyboard, select the scene and in the Identity Inspector make our scene UsersListViewController type from the dropdown list.
  4. Run a quick test to make sure our tvc is working.
  5. Build & Run. You should get an empty tableview.
Let's review what we'll do in this section:
  • Add an array property to your .m file
  • Prefill that array in viewDidLoad
  • Eliminate the pesky warning lines
  • Make tableview return one section
  • Make tableview return array count
  • Make tableview cell return array objects
This should be second nature to you by now, so I'll blaze through the specifics. 
Here's the property code:
 
@property (nonatomic, strong) NSArray *testArray;

Here's the viewDidLoad code:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.testArray = [[NSArray alloc] initWithObjects:@"me", @"you", @"them", nil];
    NSLog(@"array %d", [self.testArray count]);
}
Here's the return array count code:

return [self.testArray count];

Here's the cFRAIP code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Configure the cell...
    cell.textLabel.text = [self.testArray objectAtIndex:indexPath.row];
    return cell;
}
 
Before you Build & Run, select the UITableViewCell in Storyboard and, in the Attributes Inspector, make sure you use Cell as the Reuse Identifier. Your app should work fine.
If you Build & Run now, users should be displayed in the tableview. Cool!  That's what we're going to want to do -- that is, display a list of users in a tableview and then add in the points, like a score table.
Figure D is a mockup of what our app will look like. In essence, we'll have a tab bar controller manage three views: Users, Map, and Instructions. We'll also throw in a Login view as the app launches. This should give you an idea of what kinds of tasks we'll need to perform in order to accomplish this.
a)  Present a login view controller
b)  Save user and pass information
c)  Fetch user data from the web service
d)  Plot points on a map
e)  Display instructions in a view
Figure D
iGlobeFigD_100313.png

See an enlarged view of Figure D.
You should be able to re-create this in your Storyboard. Here are the basic steps:

1. Select your existing UsersViewController scene and, from the Editor menu, select Embed In | Tab Bar Controller. You should have a scene and a class for UsersViewController, and the scene should be set to its Class Type in the Identity Inspector. 

2. Clear out the second scene that was added when you embedded your tableview scene in a tab bar (by clear out, I mean make sure it doesn’t have any labels or other controls in it). Now drag a UIMapView into it. Add a UINavigationBar to the top and two buttons (Plot and Bump) on either side. Create a MapViewController class for it and set its type. Add a MKMapView IBOutlet property and two UIBarButtonItem IBOutlet properties and connect them. Add the MKMapViewDelegate.

3. For the last view add another UIViewController and drag a UIWebView and a UINavigationBar into it. Create its class file and name it InstructionsVC. Add a UIWebView IBOutlet property and connect it. Add the UIWebViewDelegate and don’t forget to set its scene type. 

4. Add a UIViewController, call it ModalViewController (Figure E is what mine looks like), and create all of the IBOutlet properties for it -- that's four labels with static text (User, Pass, Email, and Pass Requires…). There are three UITextFields with placeholder text to guide the user. There are three UIButtons for different actions. The person icon is a button with a Background Image set to the image; it will be the button the users will use to upload their image to the web server.

Other class files we could create now are TagListController, Tag/Users Model, and Annotation/PlacemarkVC.
Figure E
iGlobeFigE_100113.png

See an enlarged view of Figure E.
Take a couple of minutes to visualize what the app layout will look like now that we have a better idea of where we're headed, and then compare your visualization to the initial sketch you made of your app.

In part two, we'll connect to the web service and fetch actual data.

Wednesday, November 13, 2013

How CODE2040 strives to make IT more diverse

CODE2040 is working to springboard the IT careers of Black and Latino students and ultimately close the wealth and skills gaps for minorities in the United States. 


Through a great deal of persistence and dedication, Tristan Walker rose from being the child of a single mother in Queens, NY to working as the head of business development at Foursquare. He has since left Foursquare to work on his own projects. One of his projects is CODE2040, a foundation that seeks to get minority college students internships at IT firms.
siliconvalleyraceproblem111213.jpg
Per the CODE2040 website, projections indicate that by 2020, there will be one million unfilled software jobs. From the figures provided by CODE2040, racial and ethnic minorities stand to gain the most from entering the field, as computer science jobs command a starting salary roughly twice the median household income of Black and Latino families, and the unemployment rate for science, technology, engineering, and mathematics (STEM) workers is lower than other fields, while the unemployment rate for racial and ethnic minorities is three times the national average. The other point that is widely emphasized by CODE2040 is that the United States will be majority-minority by the year 2040 according to demographers. This projection is the basis for the name CODE2040. 
 
In 2012, CODE2040 placed five college students (as part of a pilot program) in internships with Hawthorne Labs, Jawbone, Nutrivise, Rockmelt, and Tumblr. In 2013, 18 students have been placed in internships at various places, including Facebook, Foursquare, Code for America, and the Department of Technology of the City and County of San Francisco. CODE2040 plans to expand to match more students to internship positions in 2014. Find out how your organization can become a partner of CODE2040. Or, if you're a student, visit the CODE2040 site to learn how to apply for a tech internship.

Real diversity vs. manufactured diversity

In the United States, the educational system is not doing enough to encourage high school students of any race to get a computer science degree. A symptom of this problem is the continued abuse of the H-1B visa program, which has been used to allow foreign nationals with technical expertise in under-served fields to live and work in the United States. This year, IBM was fined $44,000 for allegedly stating a preference for H-1B and F-1 visa holders over U. S. citizens. Infosys reached a $34 million settlement for allegedly improperly using B-1 business visas (temporary business permits) to avoid the limitations on the number of H-1B employment visas.

Earlier this year, NPR ran a story about aging programmers being pushed aside in favor of H-1B visa applicants. In that story, Bruce Morrison notes that H-1B visa applicants tend to be less demanding than Americans and have greater choices on where they will accept work.
Last year, IT journalist Robert X. Cringely wrote a long exposé on the history, misconceptions, and abuses of the H-1B visa system and how it depresses wages for U.S. citizens.

Final thoughts 

CODE2040's push to focus on and develop homegrown talent from high performing Black and Latino students across the United States underscores the fact that Americans are capable of performing these jobs -- when properly trained -- and the development of homegrown talent would (at least in theory) signal a reduced need for employees on H-1B visas. With Bloomberg declaring that the American Dream is fading for Generation Y professionals, it is vital to the continued economic prosperity of the country to ensure that current college students be given a fair shake at the job market, particularly in growth sectors such as IT. CODE2040 is at the forefront of that effort.