Category Archives: Software Development

Writing an Apple Help Book

Writing and distributing Apps for OSX compared to those written for the iOS environment is mostly an entirely new adventure. The underlying code is the same, but the concepts of UI design are radically different. Then there is the entire topic of Help Files, or in the case of OSX, the Apple Help Book.

Having several decades of experience as a software designer, developer and distributor, the concept is not new territory. Deploying it in OSX is, however a new skill to be learned. To help those that follow boil all this down I’m going to expose my personal notes on the subject. These notes were derived while using XCode 4.2, in January 2010 [ Detailed Online Information at developer.apple.com ]

Why Apple Help Book?

Flexibility — Writing and deploying an Apple Help Book with the application provides indexed search capability of the Help data, via the OSX Help menu while running the App. Once registered in the Application package itself, it integrates smoothly into the user experience.

What is special about Apple Help Books

They are HTML based. — This makes production of help files for the Application simple. HTML editors are widely available for those that do not have the technical chops to produce the file the good old fashioned way.

Rich Content — Help books can contain video content and even Apple Script to automate help navigation or even perform application manipulation directly from the Help construct. Niiiiiice.

What is a Help Book?

HTML Content — The book consists of a collection of HTML files that constitute the Help documentation for your application.

Indexing Help — Apple provides Help Book Indexing tool located in /Developer/Applications/Utilities, when the Developer package is installed. .

Help API — Contextual help is made available, from the Help Book via a provided Apple Help API. You do not need to utilize this capability for basic help function, but it’s available for advanced Help capabilities.

Exact Match Search for small words — A common problem with basic search functions, is that small or common letter groups may product far to general of a response. Example would be searching for the term ‘CD’. A Help Book with the properly configured Exact Match Searching would be able to return targeted help results for ‘CD’.

Can live In the Cloud — Concept of Apple Help supports the ability to access and deliver content from remote servers (aka the cloud), in three different ways; Internet-only, Internet-primary and Local-primary. It also has the ability to signal need to download and updated Help file for local (end-user) storage.

Links to Online Resources — It’s perfectly acceptable to code the Help Book and it’s title items to be links to locations on the web. An example of this might be a link from the Help system back to the producer’s main website for support or additional help content.

Authoring a Help Book

Full Document Online –[ Click Here ] for the full article on Apple’s Developer Website.

Xcode 4 – very simple way to open a website from App

This is going to be a VERY quit little missive. Basically, it’s about knowledge retention. The more I write about something, the better chance I have remembering some detail months or years hence. Today, I’m recording, for posterity (sounds pretty snooty, I know), the Objective-C command for opening a URL.

I won’t pretend to know all the details of this, and why it requires so many messages to be passed. In this case I’m admitting I was a cut-and-paste code monkey, standing on the shoulders of others. So, enough babbling, here is the meat of it. This ASSUMES you have a way to call this, such as a class method, or something. To clarity, this is writting into your implementation (.m) file.


- (IBAction)visitAuthorWebsite {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://apps.daviddemartini.com/icidr"]];
}

The above does NOT maintain context within your app. This tosses the user out into Safari to view the page. Had I added a URL View page to the application, it would have appeared there. Perhaps in version 2.1, but for now, for today, this works. I’ve tested it, verified it, and trust it.

Here it is again, broken out into chunks


- (IBAction)visitAuthorWebsite {
[
[
UIApplication sharedApplication
]
openURL:[
NSURL URLWithString:@"http://apps.daviddemartini.com/icidr"
]
];
}

I was thrilled when the example turned out to be so simple. I hope someone finds this useful.

XCode 4 – Dismissing a Keyboard after UITextField input

I originally ran into this issue back in November of 2010, while writing the original version of my iCIDR tool (hey network admins, you should buy this awesome tool now, before the price goes up!).

It’s trivial to enable a keyboard and to change the button, BUT to make it go away, and then actually wire that event into your code to do something use is not as simple as I had originally hoped!

Strategy entails using the keyboards ‘Return’ key to signal that one is done. Seems useful, but only if you have a single line text input. In my case, that’s what I’m looking at, single line input, so that’s the solution I’m going to try.

First order of business is to highlight your text area (I’m only going to be addressing the text input object), and shift to the properties tab (icon looks like this: ). Change the dropdown to the ‘Return Button’ text you wish to use. I like to use ‘DONE’, so, that’s what this image shows. Oh.. and yeah, you are also getting a little sneak peak at the newest generation of iCIDR.

The tricky part is understanding that you need to make sure your ViewController’s header file is modified to implement UITextFieldDelegate.

This is how that might look:

@interface iPhoneCalculatorViewController : UIViewController {
UITextField* seeTextField;
}
@end

Now, when the view loads, the Text box in the view needs to be addressed and wired up to the Keyboard action. Even though I defined a nice fancy label name for my object, so far I’ve only found this method to address the object ID by it’s ID. I think this is pretty funky but, cest la vie. Here is where I set the tag, also in the same view organizer dialogs:

Following that I enabled viewDidLoad (boilerplate code normally commented out), created a local variable pointer to the View’s input text box, and then set a delegator to that pointer in the main ViewContoller.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
UITextField *iPhoneInputText = (UITextField *) [self.view viewWithTag:1001]; // try to locate the object with the tag
iPhoneInputText.delegate = self; // assign a delegation.
[super viewDidLoad];
}

Moving along, I had to implement the The textFieldShouldReturn method in my ViewController class. This had to be defined to execute on the abandonment of the first responder:


// Should trap all Keyboard Return Events
- (BOOL) textFieldShouldReturn:(UITextField*)textField {
[textField resignFirstResponder];
//[self calculateOperation]; // ENTRY POINT
return NO; // default return value is YES, this is changed to NO to.... (sorry, now I've forgotten!)
}

Here is the keyboard that pops up, with the highlighted ‘Done’ button.

iCIDR 2.0 - keyboard in action (still photo)

The method (BOOL) textFieldShouldReturn is now registered to get the text input object the keyboard was typing into.

Now it’s your job (and mine…) do make it do something useful!!


Support the author, buy the App!

iCIDR - David DeMartini

iOS 5 – what I really like about the new iPhone OS

What’s not to like about a shiny new OS update for the Apple Mobile platforms? It’s new, so, it has to be better? RIGHT?

Well, yes, it IS BETTER! And it fixes some long nagging short complaints I’d had, mostly simple little things. The most interesting ones I have tried so far, are discussed here.

Check out my ‘iOS New Features Rundown Post for more details!

Now you can assign custom Text Notification Tones

YES! Finally! I can set a custom tune (like a silent one.. for those really annoying people) for both phone and text communications for any Contact in your Contacts List. And not only that, they implemented it so you don’t need to use the same tone for both. This is something I really REALLY like!

Setting separate Ringtone and SMS Tones - new in iOS5

iPad Split Keyboard. Thumb-typing made easy!

Every wish that your thumbs were a little longer to reach the keys in the center of the iPad keyboard? Well, maybe you wouldn’t want them longer, how weird would your hand look, right? Well, at least they could have done something….. and…. they did. Introducing the split keyboard on the iPad.

Split keyboard makes thumb-typing easier on the iPad!

Above, I am testing out this cool new feature while updating my Brewer’s Log. Too bad most of you won’t be able to taste this amazing Pumpkin IPA when it get’s kegged in less than a week!

iCloud delivery of your purchased Apps, from the cloud!

I’ve already found this to be one VERY useful feature. What I’m showing here is the App Store on my phone, and you can see one of the Apps has the little cloud icon next to it. That means I’ve purchased this application, but it’s not installed on this device. Now, instead of having to be wired into a computer to get easy access to my already purchased Apps, it’s available, over the cellular network, or WiFi, from a cloud storage area just for your apps. So, what’s the big deal? Well, if you want to add the app to this device, it’s as simple as pushing the ‘iCloud’ button and viola.. it’s downloading to your device!

iCloud and App Store working together

Another really nice feature, that is not illustrated in this photo, is that you can configure your account to automatically download any purchased app to ALL your devices (automatically, not need to sync) from this iCloud account. I’ve use this feature already as well and I love it. Again, no need to plug into the computer, or track down the app in the App store. It’s all done for you (of course you can turn this feature off it you like), easy as Apple pie!

News Stand – get your electronic magazines on your mobile device!

Do you have a hard-copy magazine that you subscribe to? Maybe they have a mobile version too. If that is the case, you can (again automatically) receive the latest edition, downloaded to your News Stand (here comes that iCloud thing again…) as soon as it’s published. This is a feature I’ll be making use of for certain (sorry, I’d not yet tracked down my magazines when I took this photo… I’ll be sure to update it when I do).

New App - the Apple Newstand - get it hot off the presses!

Handling of SMS and E-mail alerts on various screens

For you current iPhone users, you know how annoying it is to get an SMS (or if you have e-mail alerts on, and e-mail) while on the phone or using another app. It pops up a modal dialog box and completely interferes with whatever it is that you are doing.

NO MORE! The alert boxes are no longer modal, and if you receive an voice mail and an SMS at nearly the same time, the alerts are neatly stacked together, and after a few seconds, these automatically disappear from the screen. They are smaller, less obnoxious and they GO AWAY on their own! Finally!

OSX Lion (10.7) Arrives

The LION has arrived, and is being installed on millions of devices around the world. Or, so it would seem based on how long it took to download the 3+ Gigabyte latest version of Apple’s OSX operating system. And at the reduced (by $100 from the last update) price of $30, I imagine it’s going to fly off the virtual shelves. I say that because, for the first time, there are NO CD install options (or so said Apple during WWDC 2011). Watch that long but informative video here:

WWDC 2011 – LION, iOS5, iCloud Preview

Before I ramble on about some of my favorite features, it’s important to point out that this upgraded is not for everyone. Slower Code 2 Duo systems will see a little slowdown in how things run. Even on my early Core 2 Duo system with a measly 2GB of memory, it’s not much more than an occasional annoyance when things load. Once everything is up and running, I don’t see any performance hit. The new version is also limited to certain processor classes, so unless you have one of these processors, the installation instructions say you won’t be able to enjoy the latest:

    Supported Processors

  • Core 2 Duo
  • i3
  • i5
  • i7

Once you download the software, it took about 40 minutes to complete the installation. It’s a hands-off install (I ran mine overnight) once you have it started. You can walk away, come back about 1 hour later and you are ready to go. No nasty multiple restarts and driver re-installs or trash like that.

By now, you might be wondering, or saying out loud “So what? How do I buy this supposedly great upgrade?!?”. It’s not a single step operation, but overall all it’s pretty simple.

The first thing you will need to do is run the Software Update:

Select the 'Software Update' option from Apple dropdown.

This will upgrade your Apple App Store (seen down there in the taskbar, this is not the same thing as your Apple iTunes App store for handheld devices).

Find the App Store in Taskbar (3rd from right in mine)

From there, you will easily find the LION product you can purchase.

Apple App Store - LION download is VERY easy to find.

If you have not yet purchased it under your Apple ID, then you will be offered the product at around $30. Now, here is the part that I really like, if you have already purchased the upgrade (such as me), you don’t have to pay for it again, to install it on another device that you own, and have linked to your Apple ID! This is fantastic! Not only is it very inexpensive, the installation process is designed to work well for families and people with multiple systems. Something a competitor might learn something from…. (although I’m sure they would screw it up 1000-way to Sunday, LOL).

Once installed, you are flooded with 250 NEW features in the latest incarnation of OSX. That’s not 250 enhancements, that 250 NEW Features! Wow. So, what are those features? I’m not going to list all 250. I’m not even going to spend much time trying to describe them myself, so I’ll direct you do the Apple website, and you can pick the ones YOU are interested in:

250+ New Features in OSX LION

Now, the ones I’ve really enjoy so far are these:

Launchpad

[details] I like to keep the items I run often in my taskbar, but there is not room to keep everything I might ever run there. When I want to go find some seldom used app, I used to have to use the Finder, open Applications and poke through list. NOT ANY MORE! With a simple 4-finger swipe on the track pad (or clicking that rocket icon you might have noticed in my screen shot above) you are given an iOS (that’s iPhone/iPad/iPod style interface for the techno-neophytes that are now lost). ALL of the apps are there. If you have too many to fit on one page, well, you just swap left and right, find what you want an LAUNCH! It’s a really cool feature!

View of the Launch Pad

You can see that Apps in sub-directories are presented in the same method as they are on the iOS OS. When Apple said they took usability lessons from iOS and brought them to LION, it was much more than just some marketing hype. It’s real, and it works.

Mission Control

[details] Spaces has been changed too, and now it’s Mission Control. Like multiple desktops (like me) then you can access, add, remove them with a simple 3-finger up-swipe to see all your desktops:

Spaces - access to multiple desktops

It’s easy to move running apps from space to space too, and you might notice the dashboard is the first desktop on the right, so you can get there using spaces. So, why is that cool? Well, to shift from space to space it’s a simple matter of a 3-finger swipe left or right to access the space you want. Again, it’s just that simple!

Autosave

[details] This is such an awesome, intelligent, useful feature. Gone are the days of you cursing because you forgot to save a document, and for some reason the application closed, maybe because you restarted and forgot it was open (although the OS will remind you), or the system crashed (I’ve NEVER had my MAC crash.. EVER), or you simple hit the wrong button and didn’t save! OH NO. Well, this has you covered. Gone are the annoying popups (do you want to save?), because it will save it for you, when you need it to. Combined with Versions.. you have a very secure and safe environment in which to create, anything.

Versions

[details] And it will save different versions of your document AS YOU EDIT, so you can revert back to a previous state at any time. If you are familiar with time machine, you’ll understand how it works. I don’t have a demo to show you on my computer (yet) but I’ve tested it on another. This is one you want to read about for sure!

Having used the new OS for only 24 hours how, I’ve just scratched the surface of the new capabilities. I will say with complete confidence, I am glad that I upgraded, I’m using the Launch Pad and Mission Control heavily already.

iOS SDK with XCODE 4.2 is a huge change from version 3.x

Wow.. talk about a learning curve. The new iOS SDK 4.x (new to me, have not been writing iPhone apps much this year) is a radical change from the version I learned with.

I don’t doubt that I’ll find my around and start re-compiling my code with the new SDK for both iOS4 and iOS5 soon, but right now, I’m stalled trying to do something pretty simple like adding a 3rd view to the Tabbed interface. Once I get my old code ported to the new SDK, tested for 4.3, I’ll start working on the 5.0 version to be released later on this year.

At least the weather outside is HORRIBLE, so I’m not missing out on any quality MC riding today. :/

iPhone Cellular Tracking – Redux. Can it be turned off, at all?

If you managed to catch my previous post on the iPhone Cellular Location Tracking Controversy, you saw that I did a little more research into the issue than most of the other articles. Or, at least I showed my work. So why post today? I’m going to walk through a check to see if turning of ‘Location Services’ and *not* approving any of the services to use it for a week, stopped or at least reduced the amount of data recorded.

Here is what I did…. and at the end of the article, we’ll both know the results.

Syncing the iPhone to laptop

Connected my phone to the laptop at 08:49, and specifically told iTune to sync. Once that was done, I changed to the iPhone backup directory:

Looking for updated files
:Backup me$ cd /Users/me/Library/Application\ Support/MobileSync/Backup

Next, I checked to see which directories had been most recently updated:

:Backup me$ ls -ltr
total 0
drwxr-xr-x  1929 me  staff  65586 Mar 16  2010 21562bef54882a56a05f4047db0dd1ea95783af1
drwxr-xr-x  1323 me  staff  44982 Apr  2 08:16 d56742670a5e045f4a76ebb7fd93c728054c0ebe-20110402-081551
drwxr-xr-x   719 me  staff  24446 Apr 25 16:33 669ed5e78e2afe06caad469294edd80d4b3261b9
drwxr-xr-x  1371 me  staff  46614 Apr 26 08:49 d56742670a5e045f4a76ebb7fd93c728054c0ebe

Locating the Manifest files that contain filename for the consolidated.db data file. This netted 4 database files. The one I am most interested in is the one created during the sync at 08:49 this morning.

:Backup me$ find . -name 'Manifest.mbdb*' -exec ls -l {} \; 
-rw-r--r--  1 me  staff  226086 Apr  2 08:16 ./d56742670a5e045f4a76ebb7fd93c728054c0ebe-20110402-081551/Manifest.mbdb
-rw-r--r--  1 me  staff  167152 Apr  2 12:21 ./669ed5e78e2afe06caad469294edd80d4b3261b9/Manifest.mbdb
-rw-r--r--  1 me  staff  167022 Apr 25 16:33 ./669ed5e78e2afe06caad469294edd80d4b3261b9/Snapshot/Manifest.mbdb
-rw-r--r--  1 me  staff  232090 Apr 26 08:49 ./d56742670a5e045f4a76ebb7fd93c728054c0ebe/Manifest.mbdb

Changing to the directory, I ran the python script that lists contents of the db, and it’s data files, looking for the true name of the consolidated.db. There are over 1300 data files in that directory. It looks like the data file name remains unchanged (4096c9ec676f2847dc283405900e284a7c815836).

:Backup me$ cd ./d56742670a5e045f4a76ebb7fd93c728054c0ebe

:d56742670a5e045f4a76ebb7fd93c728054c0ebe me$ ls -1 | wc -l
    1369

:d56742670a5e045f4a76ebb7fd93c728054c0ebe me$ ~/phonedb2.py | grep consolidated
-rw-r--r-- 00000000 00000000 19128320 1303587917 1303587917 1301716372 (4096c9ec676f2847dc283405900e284a7c815836)RootDomain::Library/Caches/locationd/consolidated.db
File located, copy and compare to previous

Now that I know where the file is, I’m going to copy it to my home directory, and compare it to the other DB files I’ve saved off over the last week. Looking at the list, you can see that the size of the file has not changed, since I started to track this last week. Now, that does not necessarily mean there are no new records in the table, but, it’s a pretty decent indication that it does not. But, an examination of the table, and comparison to the data from the last extract will quickly tell the tail!

:d56742670a5e045f4a76ebb7fd93c728054c0ebe me$ cp 4096c9ec676f2847dc283405900e284a7c815836 ~/iPhoneTracking.3.db

:~ me$ ls -ltr iPhone*  
-rw-r--r--  1 me  staff  19128320 Apr 21 10:19 iPhoneLocation.1.db
-rw-r--r--  1 me  staff    225280 Apr 21 10:21 iPhoneLocation.2.db
-rw-r--r--  1 me  staff  19128320 Apr 21 13:23 iPhoneLocation.4.db
-rw-r--r--  1 me  staff  19128320 Apr 21 15:13 iPhoneTracking.1.db
-rw-r--r--  1 me  staff  19128320 Apr 22 14:24 iPhoneTracking.2.db
-rw-r--r--  1 me  staff  19128320 Apr 26 09:16 iPhoneLocation.3.db
Comparing the data. 22-APRIL vs. 26-APRIL.

I simply turned off Location Services on my phone last week, after storing the snapshot on 22-APRIL. During that time I used several apps that use some sort of location information, and in the cases where I was prompted to provide location services, I declined.

But first things first. I think it’s important to show that there is more than just the controversial CellLocation data in this database file. Here is the full list of tables included in consolidated.db

:~ me$ sqlite3 iPhoneTracking.2.db  (this is the file from 22-APRIL)
SQLite version 3.6.12
sqlite> .tables
CdmaCellLocation                   CellLocationCounts               
CdmaCellLocationBoxes              CellLocationHarvest              
CdmaCellLocationBoxes_node         CellLocationHarvestCounts        
CdmaCellLocationBoxes_parent       CellLocationLocal                
CdmaCellLocationBoxes_rowid        CellLocationLocalBoxes           
CdmaCellLocationCounts             CellLocationLocalBoxes_node      
CdmaCellLocationHarvest            CellLocationLocalBoxes_parent    
CdmaCellLocationHarvestCounts      CellLocationLocalBoxes_rowid     
CdmaCellLocationLocal              CellLocationLocalCounts          
CdmaCellLocationLocalBoxes         CompassCalibration               
CdmaCellLocationLocalBoxes_node    Fences                           
CdmaCellLocationLocalBoxes_parent  Location                         
CdmaCellLocationLocalBoxes_rowid   LocationHarvest                  
CdmaCellLocationLocalCounts        LocationHarvestCounts            
Cell                               TableInfo                        
CellLocation                       Wifi                             
CellLocationBoxes                  WifiLocation                     
CellLocationBoxes_node             WifiLocationCounts               
CellLocationBoxes_parent           WifiLocationHarvest              
CellLocationBoxes_rowid            WifiLocationHarvestCounts        

The table that is supposed to contain the data, is CellLocation. Lets show what is in the last 5 records entered in that table. Here are the fields, then the last 5 records:

sqlite> .header ON
sqlite> select * from CellLocation limit 1;
MCC  MNC  LAC  CI  Timestamp  Latitude  Longitude  HorizontalAccuracy  Altitude  VerticalAccuracy  Speed  Course  Confidence

22-APRIL
sqlite> select Timestamp,Latitude,Longitude,HorizontalAccuracy,Speed,Confidence from CellLocation order by Timestamp desc limit 5; Timestamp|Latitude|Longitude|HorizontalAccuracy|Speed|Confidence 325110108.640637|47.24654626|-122.43737727|2164.0|-1.0|70 325110108.640637|47.24717628|-122.43819308|500.0|-1.0|50 325110108.640637|47.24570667|-122.43808859|2138.0|-1.0|50 325110108.640637|47.2472279|-122.43625974|1550.0|-1.0|70 325110108.640637|47.24575543|-122.43641382|500.0|-1.0|50
26-APRIL
:~ me$ sqlite3 iPhoneTracking.3.db SQLite version 3.6.12 sqlite> .header ON sqlite> select Timestamp,Latitude,Longitude,HorizontalAccuracy,Speed,Confidence from CellLocation order by Timestamp desc limit 5; Timestamp|Latitude|Longitude|HorizontalAccuracy|Speed|Confidence 325110108.640637|47.24654626|-122.43737727|2164.0|-1.0|70 325110108.640637|47.24717628|-122.43819308|500.0|-1.0|50 325110108.640637|47.24570667|-122.43808859|2138.0|-1.0|50 325110108.640637|47.2472279|-122.43625974|1550.0|-1.0|70 325110108.640637|47.24575543|-122.43641382|500.0|-1.0|50

CONCLUSION

Based on the evidence collected from my phone, it indicates that turning LOCATION SERVICES OFF DOES STOP CellLocation LOGGING!. So.. there you have it.. My research. I’ve shown my work. Explained my methodology. You can trust me or not, but if you have some evidence, beyond some huckster’s article, that indicates I’m wrong, PLEASE let me know! If I missed something, I want to correct my research and conclusion.

As always, you cell phone may vary.

The Great iPhone Location File Controversy

The Great iPhone Location File Controversy – is it really a problem?

Unless you have been living under a rock, or use a Windows Mobile Device (no difference), you no-doubt have heard about the reports floating around in the last day or so about the infamous consolidated.db location history file that is maintained on each 3G enabled iOS4 devices such as the iPhone and 3G iPad.

In fact, the number of articles discussing this issue (and now this one is also joining the fray) is extensive.

Regardless, it’s an issue of one sort or another, so I decided to delve into this a little bit. My research starts with this site on ReadWriteWeb: Your iPhone Is Tracking Your Every Move.

The article references this page [iPhoneTracker], where you can download and app, or the source code to compile a program to read your consolidated.db file. However, first you must find it! The instructions for finding the file were not completely accurate, there are some references with path misspellings etc. So, I’m going to re-do those pages, show you how I did it, include the Python script I ran to read the database file, and finally the steps I went about to move the file, compare snapshots of the file, and see if turning off location services, as it purported, solves this problem

First order of business is to get the App. Since I’m smart and use a MAC (and LINUX, but I don’t sync my phone to LINUX, so that’s not going to be discussed any further) I grabbed this zip file, extracted the app inside, and dropped it in my Applications pane.

Running the program, I see this:

iPhone Tracker Data File Heat Map

As you can see, I don’t do much wandering around. Zoomed in you can see some of the places along the West Coast I have wandered since I purchased the current device. The App will display ALL data, or you can have it just display a specific weeks’ full of data, form any time frame in the device. Here is a shot of some travel I did during Thanksgiving 2010.

A winter vacation to California, during Thanksgiving

To say the dataset is full of inaccuracy’s is an understatement. Just look at this map of the last two days of travel. I can assure you, I was not in Sumner, nor in Olympia or any of those other places on the eastern side of the sound in a long time, much less that last two days:

A day in the life, covering the last few days. However, there are a lot of errors!

And yet another, from the middle of last year. You can see a lot of ‘hits’ on Vancouver island. However, I have NEVER been there. Ever!

This map is full of errors, I've never been to Vancouver Island!

OK, so, I hope you can see that, the use of the data has it’s limitations. It’s not very accurate. In face it’s pretty inaccurate in enough cases to make it’s utility, dubious.

But, I wanted to know more, so I delved deeper into the files and went in search of the nefarious SQLite file itself. First stop was this page, where I grabbed a script, applied some mentioned patches, located the backups directory and find my consolidated location files (I found 4).

Here is the patched Python script:

#!/usr/bin/env python
import sys

def getint(data, offset, intsize):
    """Retrieve an integer (big-endian) and new offset from the current offset"""
    value = 0
    while intsize > 0:
        value = (value<<8) + ord(data[offset])
        offset = offset + 1
        intsize = intsize - 1
    return value, offset

def getstring(data, offset):
    """Retrieve a string and new offset from the current offset into the data"""
    if data[offset] == chr(0xFF) and data[offset+1] == chr(0xFF):
        return '', offset+2 # Blank string
    length, offset = getint(data, offset, 2) # 2-byte length
    value = data[offset:offset+length]
    return value, (offset + length)

def process_mbdb_file(filename):
    mbdb = {} # Map offset of info in this file => file info
    data = open(filename).read()
    if data[0:4] != "mbdb": raise Exception("This does not look like an MBDB file")
    offset = 4
    offset = offset + 2 # value x05 x00, not sure what this is
    while offset < len(data):
        fileinfo = {}
        fileinfo['start_offset'] = offset
        fileinfo['domain'], offset = getstring(data, offset)
        fileinfo['filename'], offset = getstring(data, offset)
        fileinfo['linktarget'], offset = getstring(data, offset)
        fileinfo['datahash'], offset = getstring(data, offset)
        fileinfo['unknown1'], offset = getstring(data, offset)
        fileinfo['mode'], offset = getint(data, offset, 2)
        fileinfo['unknown2'], offset = getint(data, offset, 4)
        fileinfo['unknown3'], offset = getint(data, offset, 4)
        fileinfo['userid'], offset = getint(data, offset, 4)
        fileinfo['groupid'], offset = getint(data, offset, 4)
        fileinfo['mtime'], offset = getint(data, offset, 4)
        fileinfo['atime'], offset = getint(data, offset, 4)
        fileinfo['ctime'], offset = getint(data, offset, 4)
        fileinfo['filelen'], offset = getint(data, offset, 8)
        fileinfo['flag'], offset = getint(data, offset, 1)
        fileinfo['numprops'], offset = getint(data, offset, 1)
        fileinfo['properties'] = {}
        for ii in range(fileinfo['numprops']):
            propname, offset = getstring(data, offset)
            propval, offset = getstring(data, offset)
            fileinfo['properties'][propname] = propval
        mbdb[fileinfo['start_offset']] = fileinfo
    return mbdb

def process_mbdx_file(filename):
    mbdx = {} # Map offset of info in the MBDB file => fileID string
    data = open(filename).read()
    if data[0:4] != "mbdx": raise Exception("This does not look like an MBDX file")
    offset = 4
    offset = offset + 2 # value 0x02 0x00, not sure what this is
    filecount, offset = getint(data, offset, 4) # 4-byte count of records 
    while offset < len(data):
        # 26 byte record, made up of ...
        fileID = data[offset:offset+20] # 20 bytes of fileID
        fileID_string = ''.join(['%02x' % ord(b) for b in fileID])
        offset = offset + 20
        mbdb_offset, offset = getint(data, offset, 4) # 4-byte offset field
        mbdb_offset = mbdb_offset + 6 # Add 6 to get past prolog
        mode, offset = getint(data, offset, 2) # 2-byte mode field
        mbdx[mbdb_offset] = fileID_string
    return mbdx

def modestr(val):
    def mode(val):
        if (val & 0x4): r = 'r'
        else: r = '-'
        if (val & 0x2): w = 'w'
        else: w = '-'
        if (val & 0x1): x = 'x'
        else: x = '-'
        return r+w+x
    return mode(val>>6) + mode((val>>3)) + mode(val)

def fileinfo_str(f, verbose=False):
    if not verbose: return "(%s)%s::%s" % (f['fileID'], f['domain'], f['filename'])
    if (f['mode'] & 0xE000) == 0xA000: type = 'l' # symlink
    elif (f['mode'] & 0xE000) == 0x8000: type = '-' # file
    elif (f['mode'] & 0xE000) == 0x4000: type = 'd' # dir
    else: 
        print >> sys.stderr, "Unknown file type %04x for %s" % (f['mode'], fileinfo_str(f, False))
        type = '?' # unknown
    info = ("%s%s %08x %08x %7d %10d %10d %10d (%s)%s::%s" % 
            (type, modestr(f['mode']&0x0FFF) , f['userid'], f['groupid'], f['filelen'], 
             f['mtime'], f['atime'], f['ctime'], f['fileID'], f['domain'], f['filename']))
    if type == 'l': info = info + ' -> ' + f['linktarget'] # symlink destination
    for name, value in f['properties'].items(): # extra properties
        info = info + ' ' + name + '=' + repr(value)
    return info

verbose = True
if __name__ == '__main__':
    mbdb = process_mbdb_file("Manifest.mbdb")
    mbdx = process_mbdx_file("Manifest.mbdx")
    sizes = {}
    for offset, fileinfo in mbdb.items():
        if offset in mbdx:
            fileinfo['fileID'] = mbdx[offset]
        else:
            fileinfo['fileID'] = ""
            print >> sys.stderr, "No fileID found for %s" % fileinfo_str(fileinfo)
        print fileinfo_str(fileinfo, verbose)
        if (fileinfo['mode'] & 0xE000) == 0x8000:
            sizes[fileinfo['domain']]= sizes.get(fileinfo['domain'],0) + fileinfo['filelen']
    for domain in sorted(sizes, key=sizes.get):
        print "%-60s %11d (%dMB)" % (domain, sizes[domain], int(sizes[domain]/1024/1024))

I placed the script in my home directory for now. Later I’ll move it off to the Applications directory. Setting the script to executable, I then set out to locate the manifest db files. To do this, I leveraged the find utility (don’t worry Windoze users… you’re not missing this utility, you never had it in the first place). I thus located these 3 manifest db files. The one I’m most interested in is dated today:

Now it’s time to run the python script, and grep for the filename I need.

Locating the physical backup filename for the database file

Filename in hand, I check and verify it’s existence. The important part of the data returned is this:

(4096c9ec676f2847dc283405900e284a7c815836)RootDomain::Library/Caches/locationd/consolidated.db:

That number is the real filename. Checking that I see that the file is there. Making a copy of that file available in my home directory (using cp to copy it… that means ‘copy’ for you Windoze users reading from your bunkers).

Making a copy of the file. One never wants to hack on a 'production' data file.

Word on the street, is that this is a SQLite database file. I should be able to confirm that with a simple strings test, and the rumor is confirmed:

Checking to see if the file is an SQLite database file.

I opened the database file, and selected the first 5 records, and last 30 records from the CellLocation table (rumored to contain the data of interest).

The last 5 records recorded are:

MCC|MNC|LAC|CI|Timestamp|Latitude|Longitude|HorizontalAccuracy|Altitude|VerticalAccuracy|Speed|Course|Confidence
310|410|42979|9468182|325110108.640637|47.24654626|-122.43737727|2164.0|0.0|-1.0|-1.0|-1.0|70
310|410|42997|9468176|325110108.640637|47.24717628|-122.43819308|500.0|0.0|-1.0|-1.0|-1.0|50
310|410|42997|6781206|325110108.640637|47.24570667|-122.43808859|2138.0|0.0|-1.0|-1.0|-1.0|50
310|410|42997|7895312|325110108.640637|47.2472279|-122.43625974|1550.0|0.0|-1.0|-1.0|-1.0|70
310|410|42985|6781206|325110108.640637|47.24575543|-122.43641382|500.0|0.0|-1.0|-1.0|-1.0|50

Now, the time stamping get’s a little tricky.. it’s seconds since January 01, 2001 (I really don’t want to do the GMT / epoch offsets right now), but I don’t need to worry about that, all I really care about is that LAST location the phone recorded for me. If it did, in fact, honor my demand to turn OFF location services, my last point of origin should be in or north of Purdy.

Checking this Latitude and Longitute with Lougle Maps (OK, so it’s a corny movie reference, move on with your big bad selves)… I see….

Well, this does not bode well for the Apple ‘researchers’ that say turning off Location Services solves this problem. As far as I can tell it DOES NOT. I’ll be doing a little more research on this myself later, when I have time to verify the timestamps. But for now, it looks like, regardless if you like it or not.. Big Brother Steve Jobs is WATCHING!!!!

Just a few articles culled from a Google query on iOS4 devices


iOS 4 devices quietly track, store users’ locations | iLounge News

Apr 20, 2011 iLounge news discussing the iOS 4 devices quietly track, store users’ locations. Find more iPad news from leading independent iPod, iPhone,
www.ilounge.com/…/ios4devices-quietly-track-store-users-locations/Cached

Apple tracking location of iOS4 device users, researchers say
Apr 20, 2011 A team of researchers have discovered that iOS4 is secretly obtaining your location and recording it to a hidden file, raising obvious
www.betanews.com/article/Apple-tracking…iOS4device…/1303319892

Got an iPhone or 3G iPad? Apple is recording your moves – O’Reilly
Apr 20, 2011 Ever since iOS 4 arrived, your device has been storing a long list of ….. Why not just visit http://oo.apple.come from any IOS4 device and
radar.oreilly.com/2011/04/apple-location-tracking.html


  • Apple tracks your location in iOS 4 without your permission
    2 hours ago 

    From the moment you switched to iOS 4,your device has been storing a long list of coordinates(latitude – longitude) and timestamps.

    Top Buzz News2846 related articles

  • iCIDR App – found in some interesting places

    iCIDR 1.2 (available soon)
    While checking my Apps stats, I noticed that most of my sales have been from Europe. Europe? Really? How / Why? Germany and Italy were in there too, not just English speaking countries. So I had to ponder, just how many places are listing my ‘New’ app in their App Store Aggregators? Well, I found these links to my iCIDR app in a number of locations, a couple of which surprised me:

    AppShopper

    AppShopper has link to the App listed under ‘Utilities’. So far not enough rating activity has occurred to report.

    Appolicious

    Site is also showing no ratings for the app at this time.

    GiveApp

    GiveApp in Japan is currently listing the App as well. No reviews or comments yet.

    storedApps

    This site seems to be giving it a score of -1 right now. I’d sure like to find out why.

    SMARTPHONE Soft

    Currently only a couple of hits, not reviews or comments yet.

    3GSiPhone.com

    No comments or ratings. Plus the HTML the scrapped off another site (AppShopper.com) has one of the screen shots overlaying the text. Nice. 😐

    DeMartini Videos

    OK, this one is out there. It’s one of those click aggregation, sites. And no, I did not include the link, if you want to send them click revenue, you can find it with Google, it’s on the LAST page or results. Currently my App is no longer mentioned, but the cached version on Google shows it was pulled / scraped off another page because of my last name. And no, I am not related (as far as I know) to that clown at Electronic Arts in San Mateo.

    If you’d like to find out more, and possibly pick up a copy yourself, click on the App Store icon.

    Price: 99 cents

    iCIDR - David DeMartini

    iMy Objective-C / iPhone App Development FAQ

    Organizing ones learned lessons does not take much effort. The key is consistency, documenting things in a common mode (such as a binder, or a small note book, etc.). For decades I have preferred to use small bound ‘journal’ type books. Once full, the retire to my book-case for future reference. They have been incredibly useful over the years, holding esoteric bits of data generally accessible by myself recalling the rough dates I wrote relevant code, or learned a new programming language, reference that with the dated pages in the books (I ALWAYS date the pages in my notebooks) and voila.. if I had a personal epiphany, I recorded it there.

    This time, I’m trying something a little different. Taking those same notes, but this time making them public for others to hopefully gain from the things I’ve learned. So, on with the show.


    Creating a multi-line NSString object

    If you have battled with maintainability in long NSStrings, I found the simple solution. Oddly, a couple of hours searching on the web did not even get close to showing me this. I just used some old C language line continuation syntax, figured out how Objective-C like to do it, and voila…, problem solved.

    It’s quite simple really. The trick is the slash right after the closing double-quote. If you have whitespace, iOS 4.x will nick you with a warning. It still compiled and ran, but a warning is there for a reason, and this syntax throws none.

    As with all these sorts of posts, I hope it helps someone else.

    		NSString* appStoreLink = @"http://itunes.apple.com/us/app/icidr/id400066695?mt=8&ign-mpt=uo%3D4";
    		NSString* msg = [NSString stringWithFormat:\
    					 @"Check out this cool App I found in the App Store!\n"\
    					 "\n"\
    					 "%@ helps you understand CIDR Block IP notation.  It's packed with some great "\
    					 "features I think you'll find very useful.\n"\
    					 "\n"\
    					 "Version %@ of %@ is available now, just follow this App Store for more information:\n"\
    					 "%@"\
    					 ,appName
    					 ,version
    					 ,appName
    					 ,appStoreLink];
    

    yes, that’s REAL code from one of my apps.

    Dismissing a Keyboard after UITextField input

    Strategy entails using the keyboards ‘Return’ key to signal that one is done. Seems useful, but only if you have a single line text input. In my case, that’s what I’m looking at, single line input, so that’s the solution I’m going to try.

    First things first, rename the Return key to Done. Ah… so now you see how to prompt the user to hit the enter key when they are ‘Done’. Tricky. Dirty-tricky? Maybe, but it will work.

    WAS:

    This is accomplished by changing the ReturnKey property dropdown available in the UITextField property inspector. The dialog looks like this:

    Dialog dropdown to change the 'Return' key to 'Done'

    NOW:

    OK, that was the EASY part… the next part requires cutting some code, over-riding some methods and thinking a few things through.

    The tricky part is understanding that you need to make sure your object has implemented UITextFieldDelegate. This is how that might look:

    @interface seeView : UIViewController <UITextFieldDelegate> {
        UITextField* seeTextField;
    }

    Following that you’ll need to enable viewDidLoad (boilerplate code) and implement some delegation:

    - (void)viewDidLoad {
    	
    	UITextField *seeText = (UITextField *) [self.view viewWithTag:102];  // try to locate the object with the tag
    	seeText.delegate = self;  // assign a delegation.
    	[super viewDidLoad];
    }
    

    But the fun is not quite over yet.. The textFieldShouldReturn method will need to be defined to execute the abandonment of the first responder:

    // Should trap all Keyboard Return Events
    - (BOOL) textFieldShouldReturn:(UITextField*)textField {
        [textField resignFirstResponder]; 
        return YES;
    }

    You also need to know something about wiring classes and the File Owner together in the relevant Nib file. But you know how that plays out already, right?

    UPDATE: 19-NOV-2010

    After banging my head into the table for, uh, about 3 hours trying to get the ‘viewDidLoad‘ method to fire off… while digging around in the dusty corners of sites.. I FINALLY found THE ANSWER to this dilema. So.. now I’m going to add it in here so I don’t effing for get as well!!!

    Make sure your Tabs have the type set to the NIB type NOT UIViewController!


    Display only numbers for certain keyboard events

    It turns out this was just as easy as changing the Return button on the main keyboard to read Done. The same dropdown is play. The option I selected was Numbers and Punctuation. It looks like this:

    Keyboard after changing the property to 'Numbers & Punctuation'


    Dismissing the Keyboard after any Button Press

    ** This is the 3rd thing I need to tackle. First I have to get some math stuff ironed out and tested, as well as some more advanced string deconstruction and manipulation. This will be an important thing to get worked out, since the keyboard will cover 1/2 my UI but the main button is still visible, meaning people will hit that instead of closing the keyboard first. That needs to be taken into consideration and coded for. People don’t want to click twice when they can click once. Seems pretty logical to me.


    Regular Expression Solutions

    About 8 hours of hunting, hacking, a little screaming and finally some yelling in rejoice.. I have sorted out, FINALLY a simple, contained example of Regular Expression executing in Objective-C. This may for work you, it may not. It might not have the error control you like, well.. you’re getting it for free so.. don’t get too worked up about it.

    Basically, you’re going to need to use the regex.h C libraries to get it done. The upside is that it’s already on your system. Here is the snipped of code that I used to do some regexing on a string. Obfuscated since I’m not going to show exactly what I’m doing (oops.. my tin hat almost fell off). I’ve added a few comments. Now.. how did I learn this? By a lot of trial and error, frustrated looks at ‘iPhone Cookbooks’ that are so out of date 1/3 of the code is depricated… and then.. I found this little gem: Advanced Strings In Cocoa
    so most of the credit goes to those that went before I.

    - (BOOL)validateString:(NSString*)seeStr {
    	// Take the textField apart into pieces for computation
    	NSString* pattern = @"^[:character:]$";
    	NSString* result = nil;
    
    	regex_t preg;
    	int err = regcomp(&preg,[pattern UTF8String],REG_EXTENDED);	
    	if(err) {
    		char errbuf[256];  // This was the biggest mistake I was making, not using CHAR here
    		regerror(err,&preg,errbuf,sizeof(errbuf));
    		UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"Internal Error" message:@"ERROR Code 17" delegate:nil cancelButtonTitle:@"OK"otherButtonTitles:nil] autorelease];
    		[av show];
    		[NSException raise:@"CSRegexException" format:@"Could not compile regex %@: %s",pattern,errbuf];
    	}
    	
    	const char *cstr=[seeStr UTF8String];  // This is another important bit of casting
    	regmatch_t match;
    	if(err = regexec(&preg,cstr,1,&match,0)==0){
    		result = [[[NSString alloc] initWithBytes:cstr+match.rm_so length:match.rm_eo-match.rm_so encoding:NSUTF8StringEncoding] autorelease];
    //		UIAlertView *av = [[[UIAlertView alloc] initWithTitle:@"MATCH!!!" message:result delegate:nil cancelButtonTitle:@"OK"otherButtonTitles:nil] autorelease];
    //		[av show];
    		return YES;
    	}
    	else {
    		char errbuf[256];
    		regerror(err,&preg,errbuf,sizeof(errbuf));
    //		[NSException raise:@"CSRegexException" format:@"Could not complete a match %@: %s",pattern,errbuf];
    		return NO;
    	}
    

    It worked like a charm, I used a very complex pattern (not shown here) for a long string validation and it was spot on. And quick. And it worked. I hope this helps someone.


    String Splitting

    It turns out that slitting strings is Pretty Easy, or at least a lot easier that I had expected. This is a part of my daily programming life.. string manipulation… and one of those most common manipulations are splitting things apart into arrays for iterative processing.

    So.. how hard is it to do in Objective-C? Not had at all, in fact, it’s all that it takes!

    NSString *msg = @"The Dog/Cat is lazy";   //  included here for context of msg
    NSArray *parts = [[NSArray alloc] initWithArray:[msg componentsSeparatedByString:@"/"]];
    

    Yeap.. that’s all it takes!!

    Now let’s say the string was this: “The Dog/Cat is lazy”. And I want to get the first ‘part’ of that sentence, the part before the ‘/’. It’s as easy as pie (based on the line of code above):

    NSString *aboutDog = [parts objectAtIndex:0];
    

    So the variable aboutDog would contain the string at the first index (indexes start at 0 but you know that, because you’re not a sap developing for Windoze) and the value there is… TADA… The Dog. Fun. Easy… and if you know the index count (working on that) you can build a simple loop to do whatever you want!


    NSString to Integer

    The pain of robust variable casting… means you can’t be PERL/PHP lazy with your variables… you’d better know what you are putting in there, or where you are using it. So.. sometimes you feel like a nut uh.. string, and some times you don’t. Today I didn’t so off with it’s head! Sorta..

    In this little story.. the string is named parts and I’m going to make a copy of it in the form of an integer with the name bits. So.. there you have my parts and bits all laid out for you to see (and if you’re a cute girl under 40, and you like what you see, give me a ring… uh huh). Uh.. digressing.. yeah.. code. Sorry. So that’s all there is to it. You’re welcome.

    int      bits     = [[parts objectAtIndex:1] intValue];
    


    Objective-C Math – just the basics Mama


    App Store and Device Icon General Recommendations

    General .png image sizes for Apple iPhone / iPad / iPod Apps:

    App Store 512×512
    .png
    App Store Artwork Size (512 x 512)
    App Icon 57×57
    .png
    App Icon
    Spotlight Icon 29×29
    .png
    Spotlight

    Add a line to UI View with UI Builder

    Seems like a simple enough thing to handle, but I don’t see any elements that would provide this function.


    Sending Things off to e-Mail Helper App

    I finally had time to dig into this and find out how to make it all work.

    There are a few things you must do to get In-App e-mail working. The first of which is to Add the Mail Framework to your set of frameworks. See the screen snipped here. The one you want is UIMessageFramework:

    Once that has been added to your Framework, the fastest road to deployment that I found was to add the function right into the view you’re working with. Sure, it’s better from the statndpoint of pure OO programming to class this out, but I have a simple tool (from the standpoint of the UI, it was not simple for me to write the mathy bits) and it does not warrant strict OO policy enforcement.

    So.. I just added the following (bold) to my View’s header file:

    #import <UIKit/UIKit.h>
    #import <MessageUI/MFMailComposeViewController.h>
    
    @interface iCIDRViewController : UIViewController <UITextFieldDelegate, MFMailComposeViewControllerDelegate>  {
    

    With the required framework referenced and appropriate delegation added, it’s off the the main code (.m) file.

    You will need this block of code for the mail controller. Good news is that the SDK IDE will basically write the entire method for you, just need to add braces and you’re off:

    - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
    	[controller dismissModalViewControllerAnimated:YES];
    }
    

    Finally, you write the bits for sending the mail. How you call this, is up to you. I used a ToolTab Navigation button to trigger it. Do as you please. As a bonus, I top my hand on multi-line (visually) singular NSString construction. Here is where the rubbers meet the roadies:

    - (IBAction)shareApp:(id)sender {
    	MFMailComposeViewController *picker = 
    	[[MFMailComposeViewController alloc] init];
    	picker.mailComposeDelegate = self;
    	
    	NSString* appStoreLink = @"http://itunes.apple.com/us/app/icidr/id400066695?mt=8&ign-mpt=uo%3D4";
    	NSString* msg = [NSString stringWithFormat:\
    					 @"Check out this cool App I found in the App Store!\n"\
    					 "\n"\
    					 "Version %@ of %@ is available now, just follow this App Store for more information:\n"\
    					 "%@"\
    					 ,@"1.0"
    					 ,@"iCIDR"
    					 ,appStoreLink];
    	[picker setSubject:[NSString stringWithFormat:@"Check out this cool App I found (%@)",appName]];
    	[picker setMessageBody:msg isHTML:NO]; 
    	[self presentModalViewController:picker animated:YES];
    	[picker release];
    }
    
    

    Random Character String Generation

    Crude but effective. I know there has to be a better way to do this, but for today, for now, for the moment, it’s going to be this:

    NSString *pool = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    
    -(NSString *) genString: (int) len {
    
        NSMutableString *rand = [NSMutableString stringWithCapacity: len];
    
        for (int i=0; i