Thursday, December 29, 2011

Add Tab Bar Controller in a View instead of a Window

Links used for research while experimenting:

Monday, December 26, 2011

Git Cheat Cheet

Friday, December 16, 2011

Use web analytics as a mirror for your users

The question on you mind may be: What do I possibly have to gain by exposing my website analytics to my users?
Well, if used simply & sparingly, it offers you a chance to engage your users by letting them satiate their curiosity about where they belong in the overall community that frequents the given website.

The following tools are polar opposites in how they decide to track the information that will be exposed but both offer the ability to expose the data and controls for which parts to expose:
  • SeeTheStats - hooks into your Google Analytics account to collect the data and will let you expose it in a controlled manner.
  • Piwik - runs locally on your webserver to collect real-time data and expose it in a controlled manner. You can check this link to find hosts that offer automated installation of Piwik on your webserver. In my experience PowWeb is another host that does this well.

Saturday, December 10, 2011

Provision & Automate free Amazon EC2 instances with Hosted Chef

  1. Sign-Up with Amazon via this link and they will offer one year of usage (micro instance) to help you understand and evaluate EC2. Read their terms of usage carefully and make sure to only provision the free instances if you don't want to get dinged on costs due to ignorance or carelessness on your own part.
  2. Now the free instance image provided by Amazon happens to run RHEL4 but the out-of-the-box Chef-Client requires a higher version of that OS! Luckily some, if not all, of the very same issues have already been tackled in a post on the opscode's help website. And here's a gist of my experience with it as well. In case it is difficult to follow, let me highlight the fact that the modified file that allows you to properly finish the chef-client installation process on the AWS image for Linux was found here: CHEF-2515
  3. Some Notes:
    • Either command works well:
      • knife ec2 server create -x ec2-user -i /Users/xxx/.ec2/ec2.pem --template-file ~/chef-repo/.chef/bootstrap/centos5-gems-modified.erb
      • knife ec2 server create --ssh-user ec2-user -i ~/.ec2/ec2.pem -d centos5-gems-modified
    • I find it crucial to configure the appropriate Amazon security group as part of the instance creation otherwise its ends up using the "default" group. To do this, add it to your "knife ec2 server create" command with "-G groupName" and remember that it can be a comma separated list of security groups "-G secGroup1,secGroup2" if you want. Example:
      • knife ec2 server create --ssh-user ec2-user -i ~/.ec2/ec2.pem -d centos5-gems-modified -G elasticsearch

Monday, December 5, 2011

EC2 Bare Minimum

  1. install ec2 tools
    cd ~/.ec2
    ssh -i /Users/XXX/.ec2/ec2.pem ec2-user@ec2-XXX.compute-1.amazonaws.com
    sudo yum install screen (why?)
  2. For moving data to & from the EC2 instance, simply use scp which runs on top of ssh like so:
  3. A lot of images will flunk all ssh connection attempts! It seems like this has nothing to do with the permissions on the pem file that you use to connect to them or opening up port 22 by default in their security policies. They just don't work even after all that, all the attempts spit out:
    • either, Permission denied (publickey,gssapi-keyex,gssapi-with-mic)
    • or, Net::SSH::AuthenticationFailed
    So don't waste your time on them! Here's a small list of useless ones that you should avoid:
    • ami-31d41658, Red Hat Enterprise Linux 6.1, x86_64
    • ami-8f4083e6, RHEL-5.6-Starter-EBS-x86_64-11-Hourly2

Monday, November 14, 2011

Ideas for improving GitHub's Gist

Lets list and acknowledge some of the work that has already started happening around GitHub's Gist when it comes to using it for embedding code snippets in blogs:
  • Loading the Gist through an iFrame to shorten page load times.
    • A nice side effect is the ability to get around any non-working Blogger templates like the Dynamic Template that was recently released but doesn't work with an embedded Gist (remark date: Nov. 14th 2011).
New Ideas:
  1. Tell the script to provide line numbers.
  2. Allow a line-start/offset and line-end/length attribute for the embedded script so that users can have one large file and then simply talk about the chunks in sections. This will remove the need to break up the original large Gist into multiple individual Gists.
  3. Tell the script the array or range of line numbers to highlight.
    • The following Gist will look for a div with the id of "foo" and place the script's formatted content in there. It is an actual example of the code running on this blog page to perform basic highlighting:
Until all this happens, Syntax Highlighter will remain # 1!



Saturday, November 12, 2011

FAIL: Installing Simon Listens on Mac

  1. Mac OS X 10.6.8 on MacBook Pro with an Intel Core
  2. Download and unzip Simon Listens, then:
  3. cd simon-0.3.0
    ./build.sh
    
  4. You will run into errors like kde and cmake are missing.
  5. Instructions for installing KDE on a mac (cmake is included) are available here.
  6. I chose to perform the MacPorts based installation. The following command takes a long time to run.
    sudo port -v install kdesdk4
    
    I waited an hour before I simply decided to leave the MacBook running overnight. So I don't know the total time it ended up taking.
  7. I was warned that macports in general is a bit out of date, so I decided to update that too.
    sudo port selfupdate
    
  8. I was also warned that some of the existing Macports installs may be outdated, so I fixed that as well. Though I wonder if this is what caused the overall experiment to fail in the end?
    sudo port upgrade outdated
    
  9. I decided to run some extra/redundant commands in order to make sure that the kdesdk4 really did install its counterparts:
    sudo port -v install cmake
    sudo port -v install qt4-mac
    sudo port -v install kdelibs4
    
    All of them were already installed so I was satisfied with the sanity test.
  10. I pasted whatever I was instructed to:
    cd ~
    mkdir bin
    vi bin/findup
    
  11. I decided to append-to/edit the .bash_profile instead of creating .profile file. And once again I pasted whatever I was told to:
    chmod 777 bin/findup
    vi .bash_profile
    
  12. Performed all the steps leading up to the checkout as instructed but did the checkout over a non-SSL (cleartext) channel:
    mkdir -p ~/Desktop/kde/build
    mkdir -p ~/Desktop/kde/home
    mkdir -p ~/Desktop/kde/inst
    mkdir -p ~/Desktop/kde/src
    mkdir -p ~/Desktop/kde/src/branches
    mkdir -p ~/Desktop/kde/src/trunk
    vi ~/kde/src/trunk/.my-setup
    cd ~/kde/src/trunk/
    env
    svn co svn://svn.kde.org/home/kde/trunk/kdesupport
    
  13. Performed the actual build and created a directory that didn't exist but I had put-off creating because I thought perhaps the KDE build/installation process would create it:
    cd kdesupport
    cmakekde
    sudo chown -R $USER ~/Library/Preferences/KDE
    mkdir ~/Library/Preferences/KDE
    sudo chown -R $USER ~/Library/Preferences/KDE/
    sudo launchctl load  -w /Library/LaunchDaemons/org.freedesktop.dbus-system.plist
    launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist
    
  14. This time the build for Simon Listens went much further but ultimately it failed!
    cd simon-0.3.0
    ./build.sh
    
  15. I lost the link but somewhere on the web it was suggested that the errors I saw in the build will happen when binary files have been built for a 32-bit on a 64-bit system! And the way to get around it was by passing some arguments to let Simon's build script skip this stuff. But I found way too many configure files and couldn't make heads or tails of which one to use and how to tell it do what I need to workaound the problem.
    find ./ -name configure
    <6-7 results all in different nested directories>
    
    cd simon-0.3.0
    grep "enable-charconv" julius/julius/*
    vi julius/julius/configure
    ./build.sh --enable-charconv=no
    vi julius/julius/configure.in 
    ./build.sh 
    
  16. The build error seemed to have the word iconv mentioned in a bunch of places so I decided to figure out which MacPorts install was part of the problem and remove them:
    port list | grep iconv
    sudo port uninstall libiconv @1.14_0
    sudo port uninstall libiconv @1.14_0+universal
    
    But then the one being used (libiconv @1.14_0+universal) couldn't actually be removed because there were dependencies present!
  17. I also found out that that Mac actually ships with a 64-bit flavor of libiconv by default and I was able to locate the said files on my system:
    find /usr/lib -name libiconv*
    /usr/lib/libiconv.2.4.0.dylib
    /usr/lib/libiconv.2.dylib
    /usr/lib/libiconv.dylib
    
  18. Found some clues on how to try out the Mac's system libiconv library as a fallback without uninstalling MacPorts' version of the lib ... but that too didn't help the Simon build work because now the Mac's system libiconv couldn't agree on version #s with some of the other MacPorts based libs ... so I reverted that trial change as well:
    mkdir ~/temp
    sudo mv /opt/local/lib/lib
    sudo mv /opt/local/lib/libiconv* ~/temp/
    ./build.sh 
    sudo mv ~/temp/libiconv* /opt/local/lib/
    
  19. Finally I decided to remove all the clutter that had been placed on my system during this FAIL-ed attempt to the best of my abilities:
    sudo port uninstall kdesdk4 @4.7.3_0
    sudo port uninstall cmake
    sudo port uninstall --follow-dependents qt4-mac
    sudo port uninstall --follow-dependents kdelibs4
    cd ~
    rm -rf kde/
    rm -rf ~/Library/Preferences/KDE
    vi /Library/LaunchDaemons/org.freedesktop.dbus-system.plist
    launchctl list
    launchctl list | grep dbus
    launchctl remove org.freedesktop.dbus-session
    launchctl list | grep dbus
    cd ~
    rm -rf bin/
    vi .bash_profile
    

Wednesday, November 2, 2011

Image Editing Toolset for Mac

All of the following are free and available on the Mac:
  • Grab - Awesome tool for taking snapshots of on screen material.
  • iPhoto - Decent~ish for cropping.
  • Preview - Pretty good for cropping. You can follow the instructions given here. Hint: Select what should remain then press "cmd+k" to crop out the rest.
  • Gimp

How to build a webapp with CouchApp and CouchDB

This is a chronicle of the additional work I had to do as I followed Ed Parcell's Tutorial: Using jQuery and CouchDB to build a simple AJAX web application. It is supposed to serve as a supplement.

Ed's post covers the use of CouchApp as far as generating templates to work on is concerned. As a supplement, my post will leverage the Evently framework (already a part of the generated CouchApp 0.8.1) to do the same exact thing.
  1. When asked to edit index.html for the first time in Ed's post, here is how the content will differ if you want to do things the Evently way:
    <!DOCTYPE html>
      <html>
    
      <head>
        <title>Address Book</title>
        <link rel="stylesheet" href="style/main.css" type="text/css">
      </head>
    
      <body>
        <h1>Address Book</h1>
        <div id="add"><button type="button" id="add">Add</button></div>
        <div id="addressbook"></div>
      </body>
    
      <script src="vendor/couchapp/loader.js"></script>
    
      <script type="text/javascript" charset="utf-8">
        $.couch.app(function(app) {
          $("#addressbook").evently("addressbook", app);
        });
      </script>
    </html>
    
  2. Lets take a moment to acknowledge all the differences:
    1. Links to json2.js, jquery.js or jquery.couch.js are not present in the head section. This is now taken care of by loader.js (view source +/-), which loads all of the required javascript files.
    2. An additional script section:
      $.couch.app(function(app) {
            $("#addressbook").evently("addressbook", app);
          });
      
      is used to wire-up the addressbook div element with an evently widget (also named addressbook but the names don't really have to match, its not a requirement) which we will be writing later in this tutorial.
  3. The Evently approach also mandates a rewrite of Ed's refreshAddressbook function such that the html and javascript are placed in separate files and tied together via the use of the Mustache template engine. So instead of injecting any more javascript into your index.html, follow these steps:
    1. Navigate to the addressbook directory, which was generated by couchapp for you when you ran, in the past:
      couchapp generate app addressbook
      cd addressbook
      
    2. Create an evently widget named addressbook by simply creating the appropriate directory structure:
      mkdir -p evently/addressbook/
      
    3. Create _query.js file to hold the view which should be loaded from CouchDB when the addressbook div element is hooked-up with the addressbook widget (refer to line # 19 in index.html source provided earlier):
      function () {
        $.log('Inside evently/addressbook/_init/query.js');
      
        return {
          "view" : "phonenumbers",
        };
      }
    4. Create data.js file to process the data returned by CouchDB which will be available for the Mustache template engine's use:
      function(data) {
        $.log('Inside evently/addressbook/_init/data.js');
        $.log(data.rows);
        return {
          "addressbook" : data.rows
        };
      }
    5. Create mustache.html file to substitute the data and render the html directly into the addressbook div element:
      {{#addressbook}}
      <div class="address">
        <span class="name">{{key}}</span>
        <span class="phonenumber">{{value}}</span>
        <a href="#edit" class="edit">edit</a>
        <a href="#delete" class="delete">delete</a>
      </div>
      {{/addressbook}}
      
    6. Uplaod the app:
      couchapp push
    7. Before we go any further let's test what we've written so far. You'll notice that I've thrown in logs and alerts into the javascript above. If all is well then we should see an alert pop up with the data being retrieved from the server. Also if you have debugging tools like Firebug or Web Inspector at your disposal, then you should be able to see the data printed out in the console.
      1. Safari and Firefox on my mac work well and I hope that you too see a popup stating [object Object] and your console output looks something like:
        Entering view callback in evently/addressbook/_init.js
        jquery...util.js (line 3)
        Object { total_rows=2, offset=0, rows=[2]}
        jquery...util.js (line 3)
        Exiting view callback in evently/addressbook/_init.js
        
      2. B U T ... if you head over to your iPhone's safari browser and try the same thing, you may be in for a huge disappointment! Nothing will happen. And if you enable the Debug Console, you'll see javascript errors stating that jQuery does not exist!
        • If you run into this then based on the wisdom gleaned from this post on stackoverflow, here's what you need to do:
          1. Get all the supporting JS files listed under the _utils URL to be directly present in your own addressbook couchapp:
            cd vendor/couchapp/_attachments/
            wget http://fermyon.iriscouch.com/_utils/script/jquery.couch.js
            wget http://fermyon.iriscouch.com/_utils/script/sha1.js
            wget http://fermyon.iriscouch.com/_utils/script/json2.js
            wget http://fermyon.iriscouch.com/_utils/script/jquery.js
            
          2. Update loader.js to serve the files out of your own addressbook couchapp:
            vi vendor/couchapp/_attachments/loader.js
            
            ...
            couchapp_load([
              "vendor/couchapp/sha1.js",
              "vendor/couchapp/json2.js",
              "vendor/couchapp/jquery.js",
              "vendor/couchapp/jquery.couch.js",
              "vendor/couchapp/jquery.couch.app.js",
              "vendor/couchapp/jquery.couch.app.util.js",
              "vendor/couchapp/jquery.mustache.js",
              "vendor/couchapp/jquery.evently.js"
            ]);
            
        • If things are still breaking then there is one other analysis & workaround for this dilemma presented here in a blog post by Lee Boonstra.
        • If you are still having issues then use the Safari on your Mac's iOS Simulator to browse the couchapp because it gives more detailed information in its Debug Console than the actual browser on the iPhone. If you are still seeing something like the following image:
          Then you still haven't fixed the problem as described in the steps above so go back and make sure you follow all the instructions to get it resolved.
    8. Now lets finish off editing _init.js:
      put code here...
      
  4. mustache stuff

Monday, October 31, 2011

Hacking Weebly: PowWeb's Drag & Drop Builder

Background

  • What led to the Weebly hack-a-thon?
  • What had kept me from using Weebly in the first place?
  • What keeps my love & "ughhh" relationship with Weebly going?

Lets Hack!

  • How can images be replaced if the Weebly template does not allow it?
  • How can expandable/collapsible (+/-) areas be added?

Thursday, October 20, 2011

HTML5 vs. Native Mobile Apps

Technologies that are enabling HTML5 to either deploy to multiple mobile platforms or keep-up with the native look:
  • PhoneGap
  • Sencha Touch
  • jqTouch
  • jQuery Mobile
    • There is a minor issue in iOS that doesn't properly set the width when changing orientations with these viewport settings, but this will hopefully be fixed a a future release.
    • It's not currently possible to deep link to an anchor (index.html#foo) on a page.

Monday, October 17, 2011

ElasticSearch and CouchDB: Match made in heaven?

  • In ElasticSearch (ES):
    each indexed document is given a version number. This version number can be supplemented with an external value (for example, if maintained in a database). To enable this functionality, version_type should be set to external.
    Sounds nice, primed for CouchDB, right? But:
    The value provided must be a numeric, long value greater than 0, and less than around 9.2e+18.
    The was CouchDB does versioning, it isn't numeric because it appends two numbers to create a sequence/version, for example:
    1-1234567890
    So how is this handled in case of a CouchDB stream for ES?
  • How does ES facilitate the generation of an "_id" based on the data in the incoming document?
    • Does it allow to take the value of a field of that document? For example, there is a way to perform "_routing" via one of the incoming document's fields for distributed indexing across shards. So what about something for picking out the id?
    • Does it allow to concatenate values of multiple fields of that document?
  • Same question as the one above for CouchDB.
  • TBD

Sunday, October 9, 2011

Scalability Madness

CouchDB CouchDB-Lucene CouchIO / CouchOne / CouchBase Solr Elastic Search MongoDB BigCouch
Full-Text SearchNoYes?YesYesMay Be with Photovoltaic?
Distribute-ableYes??YesYes??
Distribute-ed???NoYes??
Schema-lessYes???Yes??
Tools for Importing Data???Yes???
Comments?Does it go Toe to Toe against all the features of Lucene exposed by Solr? best for HTML5 dev? it is distributable but not distributed. SolrCloud has very few features Compass got punted to invent Elastic Search.??

Wednesday, October 5, 2011

Splitting up large XML data files for use with DIH in Solr

It is ridiculously beneficial to split up XML files if you will be using Solr's Data Import Handler (DIH) to process the data. I personally saw an improvement from a speed of 166 entries/minute to 30860 entries/minute after splitting up all the large XML data files into an individual file for every entity that is to become a lucene document in Solr.

It was only on a whim but the script that allowed me to experiment with this and yield the desired results was found here:
awk '/<item>/{close("row"count".xml");count++}count{f="row"count".xml";print $0 > f}' *.xml

So if your file looks something like:

  
    Item 1
    Description 1
    ...
  
  ...
  
    Item 20000
    Description 20000
    ...
  


Then all the items from 1 to 19,999 will be divided up by this script into idividual files named row1.xml, row2.xml ... row19999.xml and look like:

  Item N
  Description N
  ...

But the last (20,000-th) item will have a trailing tag:
  <item>
    <title>Item 20000</title>
    <description>Description 20000</description>
    ...
  </item>
</items>

If you have processed 10 files, each with 20000 entries using the splitter command mentioned above ... then basically every 20000, 20000*2 ... 20000*10 numbered file will need to have the trailing tag deleted from it. To that end, the following script can be edited by providing the # of original files in the while loop's comparison statement:
#!/bin/sh
if [ $# -eq 0 ]
then
  echo "Error - Number missing form command line argument"
  echo "Syntax : $0 number"
  echo " Use to print multiplication table for given number"
exit 1
fi
n=$1
i=1
while [ $i -le 10 ]
do
  echo "sed -ibak '/items>/d' row`expr $i \* $n`.xml"
  sed -ibak '/items>/d' row`expr $i \* $n`.xml
  i=`expr $i + 1`
done
And then running the script by passing it the # of the last entry (20000-th):
./sanitize.sh 20000
sed -ibak '/items>/d' row20000.xml
sed -ibak '/items>/d' row40000.xml
sed -ibak '/items>/d' row60000.xml
sed -ibak '/items>/d' row80000.xml
sed -ibak '/items>/d' row100000.xml
sed -ibak '/items>/d' row120000.xml
sed -ibak '/items>/d' row140000.xml
sed -ibak '/items>/d' row160000.xml
sed -ibak '/items>/d' row180000.xml
sed -ibak '/items>/d' row200000.xml

Import Dynamic Fields from XML into Solr via DIH

Given an XML file that needs to be imported into Solr, you may often run into some uncommon data values that would be:
  • best grouped together under the banner of some dynamic field defined in schema.xml file,
  • with their mapping left up to the discretion of an admin tweaking the data-config.xml file, just before running DIH.

Off the cuff, one may be at a loss on how exactly to accomplish this ... and in doubt if it can even be done! You've seen this done for databases with Data Import Handler (DIH) but not with the XPath handler, or URL datasource, or File datasource for XML.

Well it can be done and here's an example:
  1. Lets say your XML file looks something like this:
    
      
        hammer
        tough and durable
        heavy
        2 inches
      
      
        nail
        sharp and thin
        hazard
        1 inch
      
    
    
  2. Now disposition, width, dangerLevel, height are pieces of data that you may not be able to plan ahead for, in your schema.xml file. So instead it makes sense to leave some wiggle room by defining somewhat predictable dynamic fields like so:
    
    
    
    Keep in mind that you have some flexibility and responsibility here in terms of choosing the type of the dynamic field ahead of time.
  3. Now when your customers or customer-facing-admins who are handling the data-import.xml file and will be looking to kick-off DIH against an XML file that they know best ... it will be quite an easy for them to come up with something like the following on the spot:
    
    
    
    
    
    and still have an agreed upon well-oiled working index at the end of the day.

Monday, October 3, 2011

Embedding Videos in Joomla 1.7

  1. Log in as the administrator
  2. Hover over the Extensions drop-down and click on Extension Manager
  3. In the Install from URL section, paste the URL pointing to a zip file for the AllVideos Joomla plugin.
    • http://joomlaworks.googlecode.com/files/plg_jw_allvideos-v4.0_j1.5-1.7.zip
  4. Once you see a notification on screen for a successful installation, click on the Manage tab
  5. Locate the row that lists the AllVideos plugin and click on the red status icon in order to toggle it to enabled.
  6. Hover over the Extensions drop-down and click on Plugin Manager
  7. Locate the row that lists the AllVideos plugin and click on the title of the plugin itself.
  8. Configure the plugin based on your needs.

Sunday, October 2, 2011

Screencast Toolset

Best toolset that I've found for working on the Mac with screencasts:
  1. ScreenR
  2. for recording.
  3. Jing for recording.
  4. SimpleMovieX for merging.
    1. The videos merged using this tool will not work as intended on Vimeo or YouTube. They will stop at the very first location that was stitched together.
  5. Final Cut Pro for merging.
    1. The videos merged using this tool can be seamlessly uploaded to top providers like YouTube and everything in the video works as intended. But the content may show up as Public by default! So make sure to secure your content afterwards.

Monday, September 19, 2011

Compound primary key for Solr's Data Import Handler (DIH)

If you ever find yourself with a datasource where you need to concatenate multiple columns or values to form the primary or unique key ... but hasn't been already done for you ... then you can do so on-the-fly with Solr's DIH using the TemplateTransformer like so:

Wednesday, September 14, 2011

Import data from Amazon RSS feeds into Solr

  1. For this example, lets use the RSS feed for new products that have been tagged as blu-ray, here's the URL:
  2. Before we import any data into Solr, lets take a moment to understand the format of the data from the RSS feed. Here's a sample item from the RSS feed:
    1. There are 5 basic fields per item: title, guid, link, pubdate and description.
    2. If we take a closer look at the HTML CHUNK inside description, we will find more information like:
      • the image URL
      • the price for a new item
        Buy new: $10.99
        
      • the price of used items starting from a lowerbound:
        
          22 used and new
          from $6.35
        
        
      • In fact, the HTML Chunk is like a complete webpage, it even has a separate description section inside of itself!
        ...
      • All this is rather messy and unpredictable (sometimes its there, sometimes its not, sometimes its a class, sometimes its an id, sometimes there are duplicates) but we must try to make the best of it.
  3. Having understood the complexities of our source of data, we are now ready to configure the Data Import Handler (DIH) for Solr.
    1. Navigate to the directory which has the out-of-the-box sample core for configuring RSS feeds. And edit the rss-data-config.xml file for importing data to look as follows:
      cd /trunk/solr/example/example-DIH/solr/rss
      vi conf/rss-data-config.xml
      
      
       
       
        
         
         
         
        
       
      
      
  4. In order to grab and add the price from the description:
    1. add the price as a field to the schema.xml file
      cd /trunk/solr/example/example-DIH/solr/rss
      vi conf/schema.xml
      
      
      
      
      
    2. add the RegexTransformer to the chain of transformers in the rss-data-config.xml file
      cd /trunk/solr/example/example-DIH/solr/rss
      vi conf/rss-data-config.xml
      
      
         ...
      
      
    3. add the regex to find and extract the price
      cd /trunk/solr/example/example-DIH/solr/rss
      vi conf/rss-data-config.xml
      
      
         ...
         
         
      
      
      Please keep in mind that this is NOT the best regex that you can use. This one simply grabs the last set of digits with a dollar sign in front of them in the HTML, so it may grab the used price instead of the new price! Feel free to come up with a better regex for yourself.
    4. If there are any items where the regex is incapable of pulling the price due to malformed HTML or any other reason, you can choose to either skip those items and not add them to Solr. OR you can introduce your own dummy price for them. Here's how you may do so:
      • add the use of the ScriptTransformer function you'll define into the chain of transformers in the rss-data-config.xml file
        cd /trunk/solr/example/example-DIH/solr/rss
        vi conf/rss-data-config.xml
        
        
           ...
        
        
      • add the following script to rss-data-config.xml file if you want to skip the items where a price wasn't available or couldn't be deduced
        cd /trunk/solr/example/example-DIH/solr/rss
        vi conf/rss-data-config.xml
        
        
        
        
        ...
        
        
      • add the following script to rss-data-config.xml file if you simply want to inject a dummy price
        cd /trunk/solr/example/example-DIH/solr/rss
        vi conf/rss-data-config.xml
        
        
        
        
        ...
        
        
  5. The following can be added to pull out the image URL:
    
       ...
       
       
    
    
  6. The following can be added to pull out the date (SimpleDateFormat was used as a reference to devise a format string to parse the incoming dates from the feed such as Mon, 12 Sep 2011 21:14:23 GMT):
    
       ...
       
       
    
    
  7. Start the Solr server:
    cd /trunk/solr/example
    java -Dsolr.solr.home="./example-DIH/solr/" -jar start.jar
    
  8. Navigate to the following URL
    http://localhost:8983/solr/rss/admin/dataimport.jsp?handler=/dataimport
    
  9. Click the Full Import with Cleaning button to import data from Amazon RSS feed into Solr.

Junk / Errata
url="http://www.amazon.com/rss/tag/blu-ray/new/ref=tag_rsh_hl_ersn"
regex=".*Buy new.*span.class..tgProductPrice...(\d*.\d*)"
stripHTML="true"

Saturday, September 10, 2011

Multicore master-slave replication in Solr Cloud

  1. If you've already done some work with Solr Cloud then you may want to start fresh by cleaning up any previous ZooKeeper configuration data in order to run this example exercise smoothly.
    cd /trunk/solr/example/solr
    rm -rf zoo_data
    
  2. We will create the following setup:
    1. there will be 2 Solr instances, each with 3 cores
    2. 1 of the 3 cores will be a master and the other 2 will be slaves
    3. the slaves of one instance will be configured to use the master of the other one
    4. The infrastructure will look like:
      • Solr-Instance-A
        • master1 (indexes changes for shard1)
        • slave1-master2 (replicates changes from shard2)
        • slave2-master2 (replicates changes from shard2)
      • Solr-Instance-B
        • master2 (indexes changes for shard2)
        • slave1-master1 (replicates changes from shard1)
        • slave2-master1 (replicates changes from shard1)
  3. We can reuse the multicore directory of the out-of-the-box example. It already has the core0 and core1 directories, lets create an additional core:
    cd /trunk/solr/example/multicore
    cp -r core0 core2
    
  4. If we were NOT using Solr Cloud which has us upload an universal configuration at startup, then we would perform the following sub-steps:
    • Replace any mention of core0 with core2
      sed -ibak 's/core0/core2/g' core2/conf/solrconfig.xml
      sed -ibak 's/core0/core2/g' core2/conf/schema.xml
      sed -ibak 's/zero/two/g' core2/conf/schema.xml
      
    But right now these are pointless because each individual core's configuration will not be used ... instead the configuration in ZooKeeper will be used.
  5. Edit solr.xml as follows:
    
        
        
        
    
    
  6. Copy over the example's zoo.cfg from the single-solr setup over to the multicore setup:
    cd /trunk/solr/example
    cp ./solr/zoo.cfg ./multicore/
    
  7. Copy example to example2 in order to create another Solr instance
    cd /trunk/solr
    cp -r example example2
    
  8. Edit solr.xml for example2 as follows:
    cd trunk/solr/example2/multicore
    vi solr.xml
    
        
        
        
    
    
  9. So where is the configuration that we will we be uploading to ZooKeeper? And what should we edit? Well, the most well formed configuration is sitting in the out-of-the-box single core example so let us simply upload it from there to ZooKeeper! And have it configured such that it can be applied conditionally to all our cores based on the java params that we specify at startup!
    1. Let us begin by editing the solrconfig.xml file of the single solr core example as follows:
      cd /trunk/solr/example/solr/conf
      vi solrconfig.xml
      
             
               ${enable.master:false}
               commit
               startup
               schema.xml,stopwords.txt
             
             
               ${enable.slave:false}
               http://${masterHost:localhost}:${masterPort:8983}/solr/${masterCoreName:master1}/replication
               00:00:60
             
      
      
    2. We cannot pass the true/false values via -Denable.master or -Denable.slave at startup because it will end up applying globally to all the cores (1 master & 2 slaves) and there isn't a way to start only one core at a time from the command line. So we must leverage each individual multicore's solr.xml to provide core specific values as follows:
      cd /trunk/solr/example/multicore
      vi solr.xml
        
          
            
          
          
            
          
          
            
          
        
      
      
      cd /trunk/solr/example2/multicore
      vi solr.xml
        
          
            
          
          
            
          
          
            
          
        
      
    3. Now let us start the 1st instance of the multicore Solr with the appropriate java params and let ZooKeeper know exactly where to get its universal-config (bootstrap_confdir) from:
      cd /trunk/solr/example
      #java -Dbootstrap_confdir=./solr/conf \
      #     -Dsolr.solr.home=multicore \
      #     -DmasterHost=localhost -DmasterPort=7574 -DmasterCoreName=master2 \
      #     -DzkRun \
      #     -jar start.jar
      java -Dbootstrap_confdir=./solr/conf -Dsolr.solr.home=multicore -DmasterHost=localhost -DmasterPort=7574 -DmasterCoreName=master2 -DzkRun -jar start.jar
      
    4. Start the 2nd instance of the multicore Solr with the appropriate java params:
      cd /trunk/solr/example2
      #java -Djetty.port=7574 \
      #     -DhostPort=7574 \
      #     -Dsolr.solr.home=multicore \
      #     -DmasterHost=localhost -DmasterPort=8983 -DmasterCoreName=master1 \
      #     -DzkHost=localhost:9983 \
      #     -jar start.jar
      java -Djetty.port=7574 -DhostPort=7574 -Dsolr.solr.home=multicore -DmasterHost=localhost -DmasterPort=8983 -DmasterCoreName=master1 -DzkHost=localhost:9983 -jar start.jar
      
  10. Now, you can check the ZooKeeper status here:
    http://localhost:8983/solr/master1/admin/zookeeper.jsp
    

And that's all there is to it, feel free to leave any feedback as comments below.

Friday, September 9, 2011

My Solr Cloud Wishlist

  1. Add code in Solr such that the admin may configure a limit on how many documents is way too many to hold in single Solr core and kick-off an automated process to:
    1. Either, CREATE another core (same or separate machine?) and add it to the ZooKeeper configuration with a weight that signifies that all new addition should happen to this new core's index only. Though I wonder how an update (delete+add) would work?
    2. Or, begin sharding the existing core. This could be done by CREATE-ing a copy of it (core_copy) and distributing the index into two shards (core_shard1,core_shard2) using a scheme/policy that does so in a best-effort manner such that the scoring wouldn't get thrown off by too much due to each individual shard's differing IDF. Then SWAP the two sharded-cores in as replacement for the overloaded core. What would happen to any changes made during this process?

Setup Solr master-slave replication with ZooKeeper

Reading Chapter 9: Scaling Solr from the book Solr 1.4 Enterprise Search Server before jumping into the world of Solr Cloud is essential for anyone who wants to understand what the embedded ZooKeeper can or cannot do. This is because you have to know how to configure al the nuts & bolts in Solr manually before you can gain a natural understanding of what the automation does and does not take care of for you.

If you go through the basic exercises for Solr Cloud, then you will come across Example B: Simple two shard cluster with shard replicas. It is important to note that the wording here can be a bit misleading based on what you are looking to accomplish. It is not replication that is being set up there. Instead, that example uses "replicas" as "copies", to demonstrate high search availability.

Here are the tested & tried steps for replication with a master-slave setup that will fit-in with a ZooKeeper managed Solr Cloud:
  1. If you've already done some work with Solr Cloud then you may want to start fresh by cleaning up any previous ZooKeeper configuration data in order to run this example exercise smoothly.
    cd /trunk/solr/example/solr
    rm -rf zoo_data
    
  2. Collection is a ZooKeeper oriented terminology to indicate a bunch of Solr cores that share the same schema and this has nothing to do with the name of a Solr Core itself. Lets keep this fact plain to see by editing the solr.xml file and providing an appropriate name for the core & collection:
    <cores adminPath="/admin/cores" defaultCoreName="master1">
     <core name="master1" instanceDir="." shard="shard1" collection="collection1"></core>
    </cores>
    
  3. Navigate to the configuration directory for the example in the trunk & begin editing solrconfig.xml using your preferred text-editor:
    cd /trunk/solr/example/solr/conf
    vi solrconfig.xml
    
  4. Uncomment and edit the replication requestHandler to be as follows:
    <requesthandler name="/replication" class="solr.ReplicationHandler" >
           <lst name="master">
             <str name="enable">${enable.master:false}</str>
             <str name="replicateAfter">commit</str>
             <str name="replicateAfter">startup</str>
             <str name="confFiles">schema.xml,stopwords.txt</str>
           </lst>
           <lst name="slave">
             <str name="enable">${enable.slave:false}</str>
             <str name="masterUrl">http://localhost:8983/solr/replication</str>
             <str name="pollInterval">00:00:60</str>
           </lst>
    </requestHandler>
    
  5. Navigate out of the examples directory and create another copy of it
    cd /trunk/solr/
    cp -r example example2
    
  6. Edit the solr.xml file for the example2 directory:
    1. change the name of the core to indicate that it is a slave
    2. leave the name of the shard as-is to indicate which shard it is a replica of
    3. leave the name of the collection as-is because this slave core should join the same collection as its master in ZooKeeper config
    cd /trunk/solr/example2/solr
    vi solr.xml
    
    <cores adminPath="/admin/cores" defaultCoreName="slave1">
     <core name="slave1" instanceDir="." shard="shard1" collection="collection1"></core>
    </cores>
    
  7. Start the master core, the use of java params allows us to call this out as a master at startup:
    cd /trunk/solr/example
    java -Dbootstrap_confdir=./solr/conf -Denable.master=true -DzkRun -jar start.jar
    
  8. Start the slave core, the use of java params allows us to call this out as a slave at startup:
    cd /trunk/solr/example2
    java -Djetty.port=7574 -DhostPort=7574 -Denable.slave=true -DzkHost=localhost:9983 -jar start.jar
    
  9. After starting the slave, towards the end of the logs for the slave, you should be able to spot info to affirm that replication is working:
    INFO: Updating cloud state from ZooKeeper...
    Sep 9, 2011 6:20:00 PM org.apache.solr.handler.SnapPuller fetchLatestIndex
    INFO: Slave in sync with master.
    

Sources:
  • http://lucene.472066.n3.nabble.com/Solr-Cloud-is-replication-really-a-feature-on-the-trunk-td3317695.html
  • http://lucene.472066.n3.nabble.com/Replication-setup-with-SolrCloud-Zk-td2952602.html

Sunday, August 21, 2011

Adding Core Data to an already existing XCode4 project

  1. The mechanism to add the model is simple:
    Ctrl+Click > New File > Core Data > Data Model > Next > Save
    but this only works if you Ctrl+Click on the project!
    • If you Ctrl+Click anywhere else, for example lets say on the Resources folder, then you will get a useless .xcdatamodeld file which simply won't open in the editor.
    • So make sure to add the model file by Ctrl+Click(ing) on the project name and nothing else.
    • Beware, if try to move it to an appropriate location like Resources at a later time, it will stop working again. After the move, once again, no editor will come up if your try to re-select the .xcdatamodeld file.
    • You must have it directly under the project in order to be able to work with it.

Monday, August 8, 2011

Versioning iPhone App: Beta & Production Builds

A little bit of Google search yields the following great resources to help get started in the arena of versioning your iOS apps:
In this blog entry, I'll:
  • attempt to provide the grand total of everything I learned from these articles which are based in XCode3,
  • provide instructions on how to do the same in XCode4, and
  • explain some of the finer points which weren't clear to me without trial & error.
  1. This snapshot shows how to add apple generic versioning to your XCode4 project.
  2. Open YOUR_PROJECT in XCode 4 and navigate to YOUR_PROJECT > Targets > YOUR_PROJECT > Summary > iOS Application Target > Version
  3. Even though it may seem intuitive to think so but this is NOT the version number that the iPhone app users will see.
  4. Instead this is an internal build number that you can increment as many times as you like and whenever you deem appropriate. Follow the links at the top to learn more.
  5. You can easily comfirm this by attempting to edit this property in YOUR_PROJECT > Target > YOUR_PROJECT > Info (maps to YOUR_PROJECT-Info.plist file) > Custom iOS Target Properties > Bundle Version
    and then confirming that the change took effect again in Summary > iOS Application Target > Version
  6. If you Ctrl+click and ask it to Show raw keys/values
  7. Then you'll notice that this maps to the key: CFBundleVersion
  8. If you would eventually like to build your "About" page in the app and format a string that shows up something like version 0.1.0 (build 42), where 42 will correspond to the value of CFBundleVersion, then instead of giving it an initial value like 1.0, I would suggest setting it to 1
  9. So now what corresponds to the 0.1.0 part of the version 0.1.0 (build 42) in your about page? Well, the Bundle versions string, short property with the raw key value of CFBundleShortVersionString would make up the 0.1.0 piece of that string.
  10. And could this 0.1.0 be the version # that is displayed as your app's version in the App Store? Yes! this is known as the marketing version and it is what shows up to the users of the App Store.
  11. What's up with the x.y.z version structure? As one of the links in the beginning explains, X - major revision number, Y - minor revision number, Z - maintenance or patch release. This is a very nice & simple concept to use.
  12. Why should the starting of an App's marketing version be from 0.1.0? Because an application would like to enter the App Store as 1.0.0, so you don't want to use 1 as your major (X) version in the very beginning when you are probably in the process of distributing Ad-Hoc builds for alpha/beta testing. 1 as your minor (Y) version makes sense 0.1.0 because your app starts with the smallest set of features and then grows to perhaps 0.2.0, 0.3.0 and so on. Also the 0 as your patch (Z) version makes sense because there is nothing for you to patch at the very start. In conclusion: minor.major.patch is conveniently 0.1.0 for starters.
  13. Here's what my email notification message looks like when I use TestFlightApp to publish beta builds to testers: v0.1.0 (4) of ShoppinPal is ready for a TestFlight
  14. To check your CFBundleVersion via Terminal, use:
    agvtool what-version
  15. All the following commands increment CFBundleVersion by 1 but do nothing to CFBundleShortVersionString:
    agvtool next-version
    agvtool next-version -all
    agvtool bump -all
  16. To check your CFBundleShortVersionString via Terminal, use:
    agvtool what-marketing-version
  17. To change CFBundleShortVersionString, you will yourself have to understand if the change is major, minor or patch and then use:
    agvtool new-marketing-version 0.2.0
  18. With XCode4 there really shouldn't be any good reason not to use Git so remember to commit to the repository every time after you change the version. Perhaps even take the time to tag the repo so that you can pull the exact source for debugging if someone ever reports an issue against a specific version/build #

Tuesday, August 2, 2011

PayPal: Why their mobile integration grinds my gears

Sometimes it is frustrating to see a company or business excel at something and then be so bad at advertising it! PayPal's mobile integration makes that list because it doesn't show up in the first page of google search results.

Why would you have something so awesome but fail to show-up when people search for you? Perhaps they are protected against search engine crawlers? Anyway, allow me to break it down for you the reader, who found this blog through a google search ... and let you know what I think is the best way to get up and running with the PayPal iPhone in-app payment awesomeness:
  1. The 1st 4 slides are enough to put a smile on any face: Mobile Express Checkout - best practices
  2. Here's the landing pad for mobile devs. Let me make it even simpler, get this reading material in the following order:
    1. Mobile Payments Library Developer Guide and Reference – iOS Edition
    2. Sandbox User Guide
    3. PayPal Express Checkout Integration Guide
  3. Only importing the PayPal header files and libPayPalMEP.a is not enough. There are a few indirect dependencies too, you can figure out what they are by looking at the configuration for the Simple and Interactive demos ... so make sure to add them to your project as well:
    • libz.dylib
    • libxml2.dylib
    • Security.framework
  4. You will probably have no trouble with the simple integration but as your business model and transactions become more & more complex, you'll run into error codes which will be hard to make heads or tails of. At that time sign-in to your PayPal developer account and refer to
    DOC-1412 for the meaning behind the cryptic codes.
    • One example of such a cryptic code is 589023, which maps to: "If a fractional amount is rounded due to currency conversion, funds could be lost"
    • Now this is confusing on a whole different level because there may not be any currency conversion involved in the transaction at all! So the error might seem to make no sense at all BUT it turns out that PayPal just wants your app to submit NSDecimalNumbers that do not have more than 2 decimal places. I didn't understand this simple requirement until I came across this post. Luckily, this blog makes it clearer on how to apply such a policy when you have made a decision.

Happy coding!

Sunday, July 31, 2011

IBAForms: An Engaging Experience

I recently found out about IBAForms, which is an Objective-C framework for easily building forms for the iPhone. I liked it a lot so I thought I'd make it tad easier for other beginners like myself:
  1. I've uploaded How To Use IBAForms Part 1 of the screencast where I cover the basics of importing IBAForms so that you may begin using it. Relying on the screencast isn't necessary as the Adding IBAForms to your project section of the README covers the same content but it may help to watch it in action.
  2. Here's one of the commands used in the screencast, used in the Terminal, which you may find a little bit more helpful than pausing the screencast and trying to visually copy it from there:
    git submodule add https://github.com/ittybittydude/IBAForms.git IBAForms
  3. In case you find the submodule approach less than desirable, feel free to remove it.
  4. In How To Use IBAForms Part 2 screencast, you'll get more hands-on instructions on how to actually start adding the form fields.
When in doubt head over to the user groups for IBAForms.

Wednesday, July 20, 2011

Using Git, XCode4 and Unfuddle together

  1. With XCode4 supporting Git out-of-the-box it is highly encouraging to know that any endeavor that you "the developer" might embark on ... will benefit from a default development branch (on your local box) known as master repository (unless you explicitly de-select a checkbox in XCode4 when creating your project).
  2. As your single-developer project gains a coherent enough form for you to want to share it with other developers ... Unfuddle will pop into the picture because it provides: (a) Git hosting, and (b) it is free for upto 2 developers. This is the perfect setup for figuring out if Git and XCode4 are the right fit for you.
    • A lot of folks who make use of GitHub hosting (your source code is open to the world) seem to be concerned about how XCode4 doesn't live up to their expectations because it doesn't provide an automated process or any clear cut instructions for configuring and pushing the local master-repo over to a remote upstream repository (lets call it origin-repo) when this phase in their development life-cycle finally arrives.
    • Well the instructions over @ Unfuddle couldn't be more direct and clear-cut, they get the job done in under 10 minutes flat. They are suited exactly for a project where a local master-repo already exists and you want to push it to a remote origin-repo.
    • After the push setup is completed and your team member has cloned the origin-repo onto their own dev machine as a master-repo, you are ready to roll.
  3. Lets say the n00b member makes a change to their local files. They can quickly find out what has changed using
    git status
  4. Now the n00b wants to commit the changes, then they can use
    git commit -m "comment"
  5. But wait, this only committed the changes to the local master-repo on the n00b's machine, next to get this pushed over to the origin-repo use:
    git push git@subdomain.unfuddle.com:subdomain/abbreviation.git
    ... where subdomain and abbreviation must be replaced with values appropriate to your account and repository.
  6. Now lets say you realize that it doesn't make much sense to have the *.xcodeproj files/folders under source control as their contents might differ based on the machine they are on, well then:
    git rm --cached <file>
    would remove file from version control, while keeping it in the working repository. And to avoid from ever mistakenly adding it back to source control, just add a .gitignore file and inside it put the expression to ignore the file/folder in question.
  7. Then use
    git commit -m "comment"
    for putting the change into master-repo

  8. And
    git push git@subdomain.unfuddle.com:subdomain/abbreviation.git
    for pushing the change to the origin-repo

  9. Finally use
    git pull git@subdomain.unfuddle.com:subdomain/abbreviation.git
    to get everything back over to your own local master-repo

  10. Some common tasks that may crop-up once you move to a multi-dev env could be simple things like wanting to rename the project. For XCode4 this was a particularly simple yet difficult find for me. Apparently according to Konstantin Salavatov: in XCode4 clicking on the project name after selecting it, starts the process of renaming it. This is absolutely correct.

  11. Here's a set of links to keep close at hand for quick reference:
    • http://www.arlt.eu/blog/2009/12/01/importing-iphone-keys-p12-and-pem-into-snow-leopards-keychain/
    • https://git.wiki.kernel.org/index.php/GitCheatSheet

Tuesday, July 19, 2011

TestFlight Fever

I recently heard about TestFlight and I became extremely excited. Now having never done any ad-hoc distribution for iPhone apps in the past, I thought that I had hit the jackpot! I quickly signed up, sent invites to my team members and then rushed to upload a build ... so that everyone could start testing instantly :)

But oh boy was I in for a surprise. TestFlight became a victim of my false expectations:
  1. False expectation # 1: I don't have to maintain the profiles for the folks that I invite for testing my app via TestFlight.
    • WRONG! The 1st thing that TestFlight warned me about was the fact that it could not accept my build because I was probably using a distribution profile. Now since a dist-profile doesn't have a record for any of the devices from my team members, it wouldn't work. So TestFlight warned me about this and told me to go perform my build with an Ad-Hoc profile.
  2. False expectation # 2: TestFlight will clearly document and guide a novice like me how to setup an Ad-Hoc configuration in XCode4.
    • UPDATE! They now prompt you with an informative link that will give you all the help you need.
    • WRONG! While they may make a welcome improvement in the future, I could not find any help on the immediate TestFlight site ... but just like any good engineer of the Google-era I was able to find an excellent article on the web for it that got me past this hurdle.
  3. False expectation # 3: Just this one time, I can get away by faithfully following the instructions of other folks and not having to bang my head bloody w/ trial & error.
    • WRONG! The XCode4 + Ad-Hoc distribution configuration article got me off to a flying start but I quickly realized that I had no idea how to create an Ad-Hoc profile.
    • After mucking about I figured out the steps are:
      • Goto iOS Provisioning Portal
      • Select Provisioning from the left pane
      • Select the Distribution tab from the right
      • Click the New Profile button
      • Make sure to mark the Ad Hoc bullet option as the distribution method.
      • Select all the Devices that can use this profile.
      • Download and install this new profile and prepare your build with it.
  4. False expectation # 4: Any TestFlight teammates who accept their invites and whose devices weren't explicitly added as a part of the Ad-Hoc profile at the time of its creation ... will be injected into the Ad-Hoc profile auto-magically.
    • SUPER WRONG! You have to go edit you Ad-Hoc profile and add the devices yourself. TestFlight does NOT commandeer your Apple provisioning account to do this for you.
    • What TestFlight DOES offer us (the users) is the ability to easily collect the UDIDs of our fellow teammates. Think about it, having your teammates get an email that they open via their iPhone/iPad ... and which results in their UDID being shuttled over to a central location in TestFlight for easy collection is already a huge boon! Perhas the only boon one should be asking for :)
    • Gone are the days of running around looking for a computer so that you can connect to iTunes and painstakingly jot down an impossibly long identifier.
    • Anyways, make sure to add the UDIDs for all of your team members as they accept the invites and sign up. Collect them from TestFlight and go update your Ad-Hoc profile as & when needed ... by adding and then selecting the decives for inclusion by marking the checkboxes under the Devices section of the Ad-Hoc profile.

In conclusion TestFlight rocks out loud and hopefully I've covered any novice errors that would have otherwise prevented others from thinking likewise.

Friday, July 15, 2011

Combining Tab Bar Controller and Navigation Controller

It may seem elusive but the best practices for combining Navigation Controller with Tab Bar Controller are very well covered by the Apple docs. Furthermore, even the instructions on "HOW TO DO IT" when using NIB files versus doing so programmatically are available too.

Having gone through this process myself, I must admit that I made various misinterpretations with the NIB file related instructions, which wasted way too much of my time. I even reached a point where I had discovered a new-ish but probably improper way of making things function. On a whim I luckily went through the whole process again ... only to this time (thankfully) discover the correct approach.

So as a supplement, I've made a screencast that aims to close this gap for any other novices. Enjoy!

While my screencast deals heavily with the NIB file based approach, the 7th iTunes University lecture by Paul Hegarty starting at 29:50, demonstrates how to do so programmatically.

Wednesday, July 13, 2011

Integrating with ZBar SDK 1.1

It took only 35 minutes to get a barebones scanning app up & running on the iPhone with ZBar SDK. As far as I'm concerned ZBar is a pretty good rival to RedLaser. If you haven't done already, head over to this section in the ZBar SDK docs and try out the example which will have you singing its praises in no time.

I followed it word for word other than the fact that I used UILabel instead of UITextView.

Sunday, July 10, 2011

Integrating with RedLaser SDK 3.0.0

If you've had the chance to try out the RedLaser app on your iPhone, then you know its usability is excellent and it is simply the best technology out there when it comes to scanning bar codes. As such, you may be rightfully tempted to download their SDK and try it out for yourself.

Now even though you'll find their out-of-box documentation in the 6MB-ish download to be enlightening, you may find yourself struggling and without any instructions when it comes to getting any project of you own up & running other than their RLSample project. I must say that the ZBar folks do a much better job of documenting similar integration items for their barcode scanning SDK. I just hope that the newly released RedLaser SDK 3.1.1 build1 will offer better docs than the Redlaser SDK 3.0.0 build21 that was used at the time of writing this blog entry.

Also you may feel dubious about purchasing the extremely expensive license without first figuring out if you have the skills to take your idea all the way. And you can't get into their forums without getting a license. So, I've put together some highlights to help you grapple with the SDK and get to a point where you can perform trial & error on your own to see if you have the right stuff :)
  1. Add files to "<your project>"...
  2. Navigate to wherever you downloaded and extracted the RedLaserDevSDK-3.0.0build21
    • The naming is not set in stone, it is spelled out here only to give you a feel for what you may be looking for on your mac.
  3. Drill-down into the RedLaserDevSDK-3.0.0build21/Sample/ folder and select the RedLaserSDK directory
    • Make sure to select the checkbox to Copy items into destination group's folder(if needed)
    • Also select the bullet option which will Create groups for any added folders
  4. Just to see if you can get anything simple to work on your own, you may try to run the build after pasting the barebones code from the Using the 3.0 RedLaser SDK.pdf file included with the SDK download.
    • Here's the code to throw into a method of your choosing:
      BarcodePickerController *picker = [[BarcodePickerController alloc] init];
      // Let's keep things simple by using the default built-in overlay
      //[picker setOverlay:customOverlay];
      [picker setDelegate:self];
      picker.orientation = UIImageOrientationUp;
      [[UIApplication sharedApplication] setStatusBarHidden:YES];
      [self presentModalViewController:picker animated:TRUE];
      [picker release];
    • Ofcourse you'll need to add #import "RedLaserSDK.h" at the top where you've pasted this code.
    • But the build still won't go through because some frameworks need to be added for the build to succeed.
  5. Goto <Your Project> > TARGETS > Build Phases > Link Binary With Libraries > +
    • libRedLaserSDK.a - XCode would have probably automatically placed this in the list when you imported the ./RedLaserDevSDK-3.0.0build21/Sample/RedLaserSDK directory.
    • CoreVideo.framework - makes sense since we need to the camera is used to perform the scans. If you started with something like 24 errors, adding this will bring them down to 18.
    • Security.framework - If you started with something like 18 errors, adding this will bring them down to 3.
    • libiconv.dylib - If you started with something like 3 errors, adding this will bring them down to 0.
  6. In addition you should add an empty implementation for what happens when a barcode is successfully read.
    • Here's the code:
      - (void) barcodePickerController:(BarcodePickerController*)picker
                         returnResults:(NSSet *)results
      {
          [[UIApplication sharedApplication] setStatusBarHidden:NO];
          [self dismissModalViewControllerAnimated:TRUE];
      }
  7. With this you can at least do a test build and run in the simulator mode which will not do much other than throw up the mock scanner screen from RedLaser.
  8. If you actually try to plug in your iPhone and directly test your app on the device you will run into a whole new slew of build errors! I received a total of 22 errors. Once again, goto <Your Project> > TARGETS > Build Phases > Link Binary With Libraries > +
    • libRedLaserSDK.a - XCode would have probably automatically placed this in the list when you imported the ./RedLaserDevSDK-3.0.0build21/Sample/RedLaserSDK directory.
    • AVFoundation.framework - If you started with something like 22 errors, adding this will bring them down to 6.
    • CoreMedia.framework - If you started with something like 6 errors, adding this will bring them down to 0.
  9. Now with a lot of luck and a little bit of elbow grease, when you connect your iPhone then build & run you app, you should see it launch and have the default RedLaser full-screen camera-view-like overlay being shown. All you will see is what you camera sees, no fancy buttons ... no nothing. But given that you never actually invoked any camera/media related functionality yourself, have faith in the fact that you've cleared the first phase in your experiments to integrate with RedLaser.
  10. Before beginning the 2nd phase, I had hoped to perform a simple experiment that would turn out as described below ... but that didn't happen so until I figure that out, this is it :(
    • Remembering that there are an extremely limited # of scans allowed w/ RedLaser SDK ... take the iPhone over to a barcode and try to scan it ... given the barebones code that we've talked through thus far, all that should happen is that the camera overlay screen will just disappear after a successful scan and you'll be left looking at an empty plane white view that had loaded this stuff to begin with.

Tuesday, June 7, 2011

Building a step up/down notched slider for iPhone

If you implement the following delegate method with similar code then you will gain the desired effect as you move the slider with your finger on-screen.

- (IBAction) sliderValueChanged:(UISlider *)sender {
    float value = [sender value];
    if (value < 0.5) {
        value = 0;
    } else if (value > 0.5 && value <1.5) {
        value = 1;
    } else if (value > 1.5 && value <2.5) {
        value = 2;
    } else if (value > 2.5 && value <3.5) {
        value = 3;
    } else if (value > 3.5 && value <4.5) {
        value = 4;
    } else if (value > 4.5 && value <5.5) {
        value = 5;
    } else if (value > 5.5 && value <6.5) {
        value = 6;
    } else if (value > 6.5 && value <7.5) {
        value = 7;
    } else if (value > 7.5 && value <8.5) {
        value = 8;
    } else {
        value = 9;
    }
    self.slider.value = value;
}

SIGABRT when opening URLs in iPhone App

The code for opening URLs from an iPhone app is relatively simple:
NSURL *url = [NSURL URLWithString:@"http://pulkitsinghal.blogspot.com"];
[[UIApplication sharedApplication] openURL:url];


Yet the 1st time I tried to use it, I kept running into the SIGABRT exception.
Ofcourse I blame the code right? Yup!
But after many searches on Google yielded no clues at all, I went back to the stack trace:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[IconDetailsViewController gotoAuthorWebSite:]: unrecognized selector sent to instance 0x4e619b0'

For the life of me, I couldn't figure out why it was claiming that the method/selector gotoAuthorWebSite was not present when I could see it in my code and the webpage was launching as well! How could it do what it was supposed to do and still claim that the code that was supposed to do the work didn't exist?

Turns out that at some point of time there was a case-sensitive discrepancy between the signatures in my .h and .m files!
- (IBAction) gotoAuthorWebSite:(UIButton*)sender;
- (IBAction) gotoAuthorWebsite:(UIButton*)sender{...}

And I happened to have resolved this inconsistency but only after I had already wired up both these methods to the UIButton component in the NIB file! So the problem was that the non-existent method (with the incorrect casing) had not been auto-removed as the following picture shows:
After manually deleting this incorrect mapping from the NIB file, everything was peachy!

Wednesday, June 1, 2011

Building IconFinder App for iPhone

After having gone through more than half of the Stanford 2010 Fall lectures on iTunes University, the urge to branch out and write something meaningful struck me. But I found myself sorely lacking on any fundamentals for getting data off the web! This is where a friend recommended RestKit and while trying to make a "real" app I also ran across IconFinder. This quickly turned around into a project where I decided to integrate with the IconFider API using RestKit to make an app that lets users explore the goodness of IconFinder as an app.
  • Initial Demo # 1
  • Revised Demo # 2
  • Get an up to date copy of the RestKit source code:
    • git submodule add -b 67-object-mapping-2.0 git://github.com/twotoasters/RestKit.git RestKit
    • cd RestKit
    • git pull
  • In order to validate the idea, I used this approach to quickly get an image to show up in the table. But surprisingly half of the images were coming over while the other half weren't, looking more closely at the JSON data I realized that the URLs with spaces in them were the ones that weren't showing up in the table so I started searching and found the right set of clues along with the solution here.
  • But overall the simplistic approach for loading images synchronously is a bad idea as I read in detail here. So having made sure things looked the way I wanted them to I moved on to refine the code some more. I got lucky and found a SDWebImage library/framework that already did this in an asynchronous and efficient manner.
    • git submodule add git://github.com/rs/SDWebImage.git SDWebImage
  • So far I had searched only using a static string, in order to see if I could get a nice & simple table view to be populated with an image and some text. Now, I wanted to add a search bar to make this into a meaningful icon browsing/finding app that it was supposed to be. I had read a lot about how to do this using NIB files but luckily I also found some guidance on how to add the UISearchBar programatically.
    • - (void)addSearchBar
      {
          if(!_searchBar) {
              _searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 41.0)];
              _searchBar.showsCancelButton = YES;
              _searchBar.delegate = self;
              self.tableView.tableHeaderView = self.searchBar;
          }
      }
  • Just because I had started out with a barebones "Window based Application" project in XCode with the intention of keeping things simple, it did not mean that I was thrilled at the idea of having to tackle all the UI work programatically now. It was time to take it up a notch.
    • I knew how to work programatically and I knew how to work with NIB files if I started my project with everything in-place courtesy of XCode ... BUT as is the case with many beginners, these were two mutually exclusive approaches in my mind and it is difficult to make a mental leap where the two are one & the same. With a little help and a bit of experimentation I got over that hump. I'll try to post a quick screencast to show it in action.
  • Now it was time to make the search a bit more specific by allowing the users to search for the icons based on size that could be controlled by a slider to jump between ranges like 16x16px, 48x48px, 512x512px etc. Once again I had some help from the web and ended up developing my own simple scheme.