Our thoughts in real time
iPhone
November 07, 2011
Android Pattern Lock on iPhone
I'm a loyal user of iPhone. I've been using it since its first version and now I'm on 4S. Though I'm staying in iOS camp and don't have any intention of moving, I occasionally get the urge to test out and see what's out there. That's the reason why I convinced myself to own and play with Dell Streak 5" phone and Google Nexus S. They are both smart smart-phones. But it's hard to pick their pros and cons by simply "playing" with it. You've got to use it. Daily. For everything including surfing, speed dialing, social-networking and games. Everything. And that's something that I didn't get a chance to do until last summer when I was traveling and in need of an unlocked phone. I could make my iPhone work (don't ask me how) but thought I'd give an Android phone a real kick.
So, I spent 3 weeks using my Dell Streak full-time. I'm not going to tell you about how bad or good Android phone is compared to its counterparts, or discuss my constant efforts to find for an outlet to charge the phone. But I do want to talk about one feature that I used a lot but can't find on iPhone: It's the well-known pattern lock feature. If you've never seen it, here is a screenshot.
The idea is simple. Rather than entering 4 digit PIN, you connect the 9-dots (3 by 3) presented on the screen to form a secret pattern. The number of dots connected to form a pattern can be as many as the available dots on screen. In the case of Android lock, the max is 9 dots and the min is 4 dots. In my 3 weeks of usage, I think I can unlock faster using pattern lock versus using PIN lock. And that's when I started to wonder why in all the apps that I've ever used on iPhone, none of them provides this type of lock. Mint is one of my most frequently used apps. I'd love to see it using pattern lock instead of pin lock. It will save me about 0.5 second every time I use the app. Yeah, that's a very valuable 0.5 second.
So, I set aside 2 hours of my time and created a component that provides the pattern lock feature. If you're an iOS developer, feel free to use it in your app. Mention Grio if you feel indebted to us. Lock your user's data more securely. If security is less of your concern, do it for the sake of giving users more options.
The code is pretty self-explanatory. The main class is DrawPatternLockViewController. You can set the matrix size by changing the following constant:
#define MATRIX_SIZE 3
In the sample app, there's a Lock button, which will bring up the pattern lock screen.
It's very easy to use the component. Create an instance, register callback and you're all set. Here's the sample code:
DrawPatternLockViewController *lockVC = [[DrawPatternLockViewController alloc] init];
[lockVC setTarget:self withAction:@selector(lockEntered:)];
[self presentModalViewController:lockVC animated:YES];
When the user is finished drawing, lockEntered will be called. You can take a look at that function in PatternLockAppViewController. It will compare what user entered with a hardcoded pattern. In this implementation, I store the pattern in an NSString. If you replace the dots with series of numbers (1, 2, 3, ...) from top left moving in the right direction downward, you'll see the dots to be similar to the keypad on a phone. And a pattern string stores those numbers by concatenating them in proper order. So, if you draw a pattern by connecting the dots along the edge clockwise starting from top left toward bottom right corner, you'd end up with an NSString with value @"0102030609". You can easily change the way you store and interpret a pattern by changing getKey function in DrawPatternLockViewController.
Note: The dot was designed by Grio's talented designer, #Yui. This project was written using iOS 5 with ARC on. You can download it here.
November 02, 2011
Dieter Rams’s Ten Commandments
You might not be aware of it yet, however SFMOMA (the San Francisco Museum Of Modern Art) is currently hosting Dieter Rams’s “Less but Better” product exhibit.
You might also not be aware of who Dieter Rams is, since he is not directly related to the software industry.
Indeed, for almost 40 years Dieter Rams was Chief of Design at Braun’s: he designed record players, razors, radio sets, etc.
He didn’t have much to do with computers.
So why talk about him?
Well, he is one of the most prominent design engineers of our era. His designs have affected many fields, as well as influenced and inspired many designers. This includes Apple’s Vice-President of Industrial Design: Briton Jonathan Ive.
A few seconds looking at the designs and the similitudes are striking:
|
iPhone Calculator App. |
ET 66 calculator, 1987, by Dietrich Lubs for Braun |
Dieter Rams work is remarkable in that he was not just an innovator; he was also an aggregator and a simplifier, putting words to ideas. He strived to do better with less.
“Surfaces that were without apology, bold, pure, perfectly proportioned, coherent and effortless” – Jonathan Ive.
Over the years, Rams was able to refine, and then define his design ideals. They are summed up in his Ten Principles for good design (also referred to as his Ten Commandments):
- Good design is Innovative.
- Good design makes a product useful.
- Good design is aesthetic.
- Good design makes a product understandable.
- Good design is unobtrusive.
- Good design is honest.
- Good design is long lasting.
- Good design is thorough, down to the last detail.
- Good design is environmental friendly.
- Good design is as little design as possible.
Ten principles that follow themselves.
I could dig in to each principle, however that is not the object of this blog article… Those principles are quite self-explanatory, since most of those ideas are the writ form of notions we all have. Some can also be correlated to familiar terminologies (ie. KISS).
In short, Dieter Rams started realizing in the 80’s that design engineering was a mess – “an impenetrable confusion of forms, colors and noises”. Being an important contributor to that mess, he asked himself a question we should ask ourselves daily when we create: “Is my design good design?”
This realization is common to many fields, including the software industry. The spreading of the Web changed our society. More companies, more engineers, more clients, more products, etc. Boom! Within a short decade we have a world of new industries.
To be able to keep as much of an understanding as possible, we devise paradigms, design patterns and frameworks, all so that we can find some common understanding; an understanding not just among programmers (see for example Brad’s article about naming conventions: http://blog.grio.com/2011/09/name-recognition.html) but also for the public. Users do not want to feel lost.
Website designs evolve just as much as product designs do, with a notable difference: Users zap through pages. They read this, tweet that. And thus the design has slowly evolved towards more simple layouts, where at a glance, they know what is where:
- Google: Text field with a “Search” button.
- Tweeter: 140 characters.
- Amazon: 1-Click.
One function, sharp edges, rounded corners.
Apple’s designs have been following those ideas for more than a decade now, however those principles were really only pushed to their first extreme with the iPod in 2001.
|
|
It’s simple, to the point, does it’s job, looks nice, and looks solid (even in pink).
Just as the users started realizing that they were looking for familiar landmarks on websites, Apple designs got accessible, keeping only those landmarks. And obviously it works.
So what am I getting at?
We are aware of these ideas: Functionality, simplicity and reliability. We have to contend with these issues daily. We are aware, but not all the time and not with such a precise understanding.
Dieter Rams’s Ten Principles (as mentioned earlier) are an aggregation of those ideas that he put into writing; ideas we can all understand.
So don’t hesitate to refer to that list once in a while, it might help you figure out a flaw, or some enhancement you could make to your websites, apps and/or architectures.
From a visual perspective, designs inspired by Braun or Apple products, products that are seen and understood by a majority of people, will only be made more understandable by their familiarity.
Don’t hesitate to take the time to check out the Dieter Rams expo. Observe his and his team’s work, with the ten principles in mind. Look at how the products are functional, aesthetic, detailed; how it is not fashionable and therefore never out of style.
Running until the 20th of February 2012.
Ten Principles for good design: http://www.vitsoe.com/en/gb/about/dieterrams/gooddesign
Posted at 05:19 PM in iPhone, Java, Web/Tech | Permalink | Comments (1) | TrackBack (0)
September 23, 2009
Uploading Files from iPhone
We recently completed an iPhone application that allows user to, among other things, perform voice-recording and upload the recorded file to a server. For uploading, it uses HTTP multipart POST against an Apache webserver. This type of arrangement is a very common way of sending a file to a webserver. I am certain that similar implementations can be found in several different programming languages. Here is my Objective C implementation...I tried to focus on brevity and encapsulation.
The code:
* Upload a file to a webserver thru http post.
* uid is the http request id
* requestData is list of key/value pairs to be sent along with the file
* url is the destination url
* filePath is absolute file path of the file to be uploaded
*/
- (void)postUpload:(NSString *)url
:(NSString *)uid
:(NSDictionary *)requestData
:(NSString *)filePath
{
NSURL *theURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:theURL
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:20.0f];
[theRequest setHTTPMethod:@"POST"];
[theRequest setValue:uid forHTTPHeaderField:@"uid"];
// define post boundary...
NSString *boundary = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *boundaryString = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[theRequest addValue:boundaryString forHTTPHeaderField:@"Content-Type"];
// define boundary separator...
NSString *boundarySeparator = [NSString stringWithFormat:@"--%@\r\n", boundary];
//adding the body...
NSMutableData *postBody = [NSMutableData data];
// adding params...
for (id key in requestData) {
NSString *formDataName = [NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key];
NSString *formDataValue = [NSString stringWithFormat:@"%@\r\n", [requestData objectForKey:key]];
[postBody appendData:[boundarySeparator dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[formDataName dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[formDataValue dataUsingEncoding:NSUTF8StringEncoding]];
}
// if file is defined, upload it...
if (filePath) {
NSArray *split = [filePath componentsSeparatedByString:@"/"];
NSString *fileName = (NSString*)[split lastObject];
NSData *fileContent = [NSData dataWithContentsOfFile:filePath options:0 error:nil];
[postBody appendData:[boundarySeparator dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"audioBody\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[@"Content-Type: application/octet-stream\r\n\r\n"
dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:fileContent];
}
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r \n",boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[theRequest setHTTPBody:postBody];
NSURLResponse *theResponse = NULL;
NSError *theError = NULL;
NSData *reqResults = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&theResponse
error:&theError];
if (!reqResults) {
NSLog(@"Connection error for URL: %@", [url description]);
UIAlertView *alert = [UIAlertView alloc];
[[alert initWithTitle:@"Error in connection"
message:[theError localizedDescription]
delegate:self
cancelButtonTitle:nil
otherButtonTitles:@"Quit", nil] autorelease];
[alert show];
}
}
NSArray *keys = [NSArray arrayWithObjects:@"recipients", @"title", @"textBody", nil];
NSArray *objects = [NSArray arrayWithObjects:uids, inputSubject.text, inputMessage.text, nil];
NSDictionary* postData = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
// post now...
[[WebService alloc] postUpload:URL_ENDPOINT_SEND_MESSAGE
:appDelegate.uid
:postData
:[recorder recordFilePath]];
<?php
define('FILE_SAVE_DIRECTORY','/folder/to/save/uploaded/files/');
error_log("Requesting upload file....");
error_log(print_r($_SERVER, true));
error_log("\n\n");
error_log(print_r($_FILES, true));
if($_POST && isset($_FILES['file'])) {
error_log(" the file:...." . $_FILES['file']['name']);
$file = $_FILES['file'];
$dst = FILE_SAVE_DIRECTORY . basename($file['name']);
if (move_uploaded_file($file['tmp_name'], $dst)){
echo 'Upload Successfully: '.$dst;
}
else {
echo 'Error uploading the file.';
}
}
?>
Posted at 03:53 PM in iPhone | Permalink | Comments (1) | TrackBack (0)
May 25, 2009
Feel My Pain: NSXMLParser Problems Upgrading to the iPhone 3.0 SDK
I set out a couple of weeks ago to port the Killer Deals application from the iPhone 2.2.1 SDK to 3.0. “No problem”, I thought; the application doesn’t veer far from the standard APIs and controls, and it is a relatively straightforward application, implementation-wise.
As I was making my updates (some minor method and property name changes here and there), I stumbled upon a show stopper: The NSXMLParser was no longer parsing my XML data. After scratching my head for a few hours, I finally figured out the problem.
It seems Apple has removed support for Windows-1252 character encoding in the latest iteration of the NSXMLParser. I used this encoding to prevent the appearance of those funny characters you occasionally see on a web page. Those funny characters are caused by a character-encoding mismatch between a text renderer and the document. Some Microsoft tools use the Windows-1252 character encoding for documents by default. Unfortunately, some of the Killer Deals data is only available in this encoding.
While the previous version would give a NSXMLParserUnknownEncodingError and continue to parse, the new version simply stops parsing after the error (although strangely, the parserDidStartDocument() method is still called after the error).
At this point, I was going to force the encoding to UTF-8 on the server, and put a hack in the code to convert the ‘invalid’ characters to the proper UTF-8 equivalents. That ended up in an invalid character error via the parser.
It
seems Apple has degraded the parser and offers less tolerance for ‘unsupported’
formats. Does this have anything to do with bad blood between Apple and
Microsoft? Hmmm…
Posted at 10:54 AM in iPhone | Permalink | Comments (1) | TrackBack (0)
April 26, 2009
Dealing with JSON on iPhone
You can easily use the JSON (JavaScript Object Notation) data format in client-server communications when writing an iPhone app. This blog is not suggesting that JSON is a more superior format for data exchange than its counterparts such as XML. In fact, we have many projects that don't use JSON. However, handling JSON is relatively straight forward in ObjectiveC.
Unfortunately, Apple iPhone SDK (as of this writing, the latest is iPhone 2.2.1) doesn't come with a built-in JSON parser. But I found out a good one called json-framework. It is both a generator and a parser. As a generator, json-framework can create JSON data from an NSDictionary. As a parser, you can pass to json-framework an NSString that consists of JSON data and it will return a NSDictionary that encapsulates the parsed data.
#import "JSON/JSON.h"
NSDictionary *requestData = [NSDictionary dictionaryWithObjectsAndKeys:
@"grio", @"username",
@"hellogrio", @"password",
nil];
NSString* jsonString = [requestData JSONRepresentation];
{"username":"grio","password":"hellogrio"}
SBJSON *json = [[SBJSON new] autorelease];
NSError *jsonError;
NSDictionary *parsedJSON = [json objectWithString:jsonResult error:&jsonError];
NSDictionary* menu = [parsedJSON objectForKey:@"menu"];
NSLog(@"Menu id: %@", [menu objectForKey:@"id"]);
NSLog(@"Menu value: %@", [menu objectForKey:@"value"]);
Menu id: file
Menu value: File
NSDictionary* popup = [menu objectForKey:@"popup"];
NSArray* menuItems = [popup objectForKey:@"menuitem"];
NSEnumerator *enumerator = [menuItems objectEnumerator];
NSDictionary* item;
while (item = (NSDictionary*)[enumerator nextObject]) {
NSLog(@"menuitem:value = %@", [item objectForKey:@"value"]);
}
menuitem:value = New
menuitem:value = Open
menuitem:value = Close
Posted at 10:10 PM in iPhone | Permalink | Comments (6) | TrackBack (0)
April 18, 2009
Broadcasting Information: How to use the iPhone Notification Center
There are times when building an iPhone application that you need to inform other parts of the program to do something, without regard to what or where those parts are. The NSNotificationCenter (aka “notification center”) is the way to go in these cases. Using notifications supports ‘loose coupling’, which is a good thing in software design.
A notification center object provides a mechanism for broadcasting information (“notifications”) within a task. An NSNotificationCenter object is basically a notification dispatch table. For those familiar with Windows programming, this pattern is similar to the Windows Message Loop architecture.
For example, one object may be performing a long-running operation (such as downloading a file) and needs to inform view controllers to change the view to indicate this busy state. While we could pass around a bunch of object references, it is much easier to post a notification and let the listening objects decide what to do.
First, we’ll register with a notification center to receive notifications. Here’s the call:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMethod) name:@"DoSomething" object:senderICareAbout];
So what’s going on here? First, we get a reference to the default notification center for the current task (application). Then we add ourselves (the calling object) as an observer of the notification.
The selector specifies the method we want to call when this notification arrives. The selector method takes one argument, which is an NSNotification object. It contains information about the notification, such as the name and sender object.
The notification name is the ‘message’ we are listening for. This parameter can be nil if we want to receive all notifications.
Finally, the object parameter is used to limit incoming notifications to those from a particular object. This can also be nil if you don’t care where the notification came from.
Now that we have an object listening, we can send (“post”) a notification. Here’s the code to do that:
[[NSNotificationCenter defaultCenter] postNotificationName:@"DoSomething" object:self];
The parameters here are pretty straightforward. We post the name of the notification, and the sender object (self in this case). If we don’t care about the sender object, we can use:
[[NSNotificationCenter defaultCenter] postNotificationName:@"DoSomething"]
which will set the sender object to nil in the received NSNotification.
A notification center delivers notifications to observers synchronously. In other words, the postNotification: method does not return until all observers have received and processed the notification. If you want to send notifications asynchronously, you should use a notification queue (NSNotificationQueue). We’ll save that discussion for another day.
A note of caution: in multithreaded applications, notifications are delivered in the same thread that the notification was posted, which may not be the same thread in which an observer registered, so be careful. If you are threading a long process, you should send the notification in the main thread, not the thread created for the process.
Now you have the information you need to start broadcasting notifications to your hearts content. This will help keep your application code mean and clean.
Posted at 08:52 PM in iPhone | Permalink | Comments (0) | TrackBack (0)
April 10, 2009
KillerDeals 1.0.1 hits the shelves
Last Thursday KillerDeals v1.0.1 hit the shelves in the iPhone AppStore. It was only a minor update to fix a few bugs that appeared post release. So we got right to work hammering out a quick fix and Apple approved the update in no time flat. This was good news.
The necessity for the "emergency" update got us all really talking about the future of this application and where we want to head in the future. Here's a short list of the plans that will be rolling out over the next few releases:
- Create a KillerDeals web service - Currently our application communicates directly with the ODAT web sites, retrieves the content, parses it, and redisplays it in its current format. Although this is a completely acceptable approach, we are at the mercy of Backcountry. If their developers change the website format or content, our app is too tightly coupled to handle this change and thus we would need to release another application version through the iPhone store. But if we moved the retreval and parsing code to a Grio owned server, we can make changes on the "fly" without ever having to release an update through Apple. Much faster response time to content related issues.
- Push notifications -This app was meant to incorporate Apple's Push Notification system. Apple has been promising this technology for quite some time and iPhone 3.0 will finally deliver it. We all very excited about this. We have some very unique ideas about how Push alerts should be configured but you'll just have to wait to check out our innovative approach.
- More sites - Recently added to the ODAT family are Brociety.com and Chainlove.com. Expect to see these sites in KillerDeals sooner than you might think. But with 6 stores the tabs might get a little crowded. So we'll just have to see what our crafty Grio developers come up with.
- More information - KillerDeals is lacking a few bits of crutial information in its current state. Items and time remaining are key indicators for the ODAT sites. Expect to this this data ticking down the seconds in an upcoming version of KillerDeals
In summary, I guess you can expect KillerDeals just to get better with every release. Remember we are exicted to here from our users so drop us a note or leave us a blog comment. We'll be waiting.
Posted at 08:19 PM in iPhone, News | Permalink | Comments (0) | TrackBack (0)
March 13, 2009
KillerDeals v1.0 iPhone Application submitted to Apple
Recently, we submitted our new iPhone application to Apple and it is currently under review. It has been approved and is available at iTunes store. It's called KillerDeals. It's a lifestyle application targeted at users who are either currently fans of sites like Steepandcheap.com and Tramdock.com or new users who are looking for amazing retail deals on outdoor clothing and equipment.
KillerDeals gives you access to 4 of the hottest sources for outdoor gear on the web in the one convenient application. Now you can check the latest deals anytime, anywhere.
KillerDeals features products from:
- SteepandCheap ®
- TramDock ®
- WhiskeyMilitia ®
- ChainLove ®
KillerDeals displays all the relevant information for each gear deal including photos, detailed descriptions, prices, and even customer reviews.
Not familiar with these sites? Here's the scoop: These sites offer one awesome piece of gear at a stupidly low price until it sells out; then the next deal appears. The first crazy deal drops
each midnight (MT) and sells ‘til it’s gone. And they always sell fast. After that another steal drops and sells till it's gone, then another, and another, ... Get the idea? Killer.
Here's some screenshots to get you excited.
Posted at 12:33 PM in iPhone, News | Permalink | Comments (3) | TrackBack (0)