SlideShare a Scribd company logo
solving little problems
My First iPhone App
On contract…

  for non-developer…

  who wants to use his own
  developer account.

Presents some challenges.

Only my second Objective-C
program…

  what can I learn?
Huh?

On app store now (yay!).

Simple idea—audio translation of
silly phrases.

Moderate dataset (4 languages, 202
phrases, 808 audio files).

  May expand later with StoreKit.
Four Things

Core Data

Slow Startup and Progress

Debug Archiving

Build Numbering with git
1. Core Data
               Used for language/phrase
               reference information for easy
               update.

               Slightly confusing since I
               know SQL well.

               Lots of work to set up queries.

               Matt Gallagher (Cocoa with
               Love) showed code that helps.
Core Data: one line fetch

I adopted and extended this
code as a category now on
GitHub:

  github.com/halostatue/
  coredata-easyfetch
Apple’s Example
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Employee"
                                    inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

// Set example predicate and sort orderings...
NSNumber *minimumSalary = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)",
                                     minimumSalary];
[request setPredicate:predicate];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}
Apple’s Example
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Employee"
                                    inManagedObjectContext:moc];




            12+
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

// Set example predicate and sort orderings...
NSNumber *minimumSalary = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)",
                                     minimumSalary];
[request setPredicate:predicate];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES];




        Statements!
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}
Matt’s Example

  [[self managedObjectContext]
     fetchObjectsForEntityName:@"Employee"
            withPredicate:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)",
                     minimumSalary];




 1 Statement!
coredata-easyfetch
   #import “NSManagedObjectContext-EasyFetch.h”

   …

   [[self managedObjectContext]
      fetchObjectsForEntityName:@"Employee"
           predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)",
                       minimumSalary];


Made a category so it’s easy                       All objects, unsorted (1)
to reuse.
                                                   All objects, sorted (2)
Refactored easy filtering.
                                                   Some objects, unsorted (2)
Added easy sorting.
                                                   Some objects, sorted (4)
How I Use It

 + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc
 {
   return [[[moc fetchObjectsForEntityName:@"DataVersion"] mutableCopy] autorelease];
 }

 + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc
 {
   NSArray* fetched = [moc fetchObjectsForEntityName:@"Language"];
   NSMutableArray* result = [fetched mutableCopy];
   [result release];

     return result;
 }

 + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc
 {
   NSArray* fetched = [moc fetchObjectsForEntityName:@"Phrase" sortByKey:@"phraseId" ascending:YES];
   NSMutableArray* result = [fetched mutableCopy];
   [result release];
   return result;
 }
2. Progress
              Original plan:

                Copy all media files from
                bundle to document area.

              Problem:

                Slow (~20–30 seconds)

              Solution:

                Show a progress monitor.
MBProgressHUD
MBProgressHUD
Created in April 2009

  Matej Bukovinski

  http://github.com/matej/
  MBProgressHUD/

Other options exist, such as
DSActivityView

  http://www.dejal.com/
  developer/dsactivityview
Beautiful, but…
…the progress HUD blocked
the application logo

…immediately on launch.

Request:

  Move the HUD to a
  different spot, and

  don’t show it unless the
  work takes 2 seconds or
  more.
Easy enough…
Added x- and y-offsets

Added a delay-before-showing

   Later changed by Matej to
   be a “grace time”

Pushed back to master and
MBProgressHUD is reasonably
active for a little project.

My version is at github.com/
halostatue/MBProgressHUD
3. Save the dSYM

             Had crashes on the client’s
             iPhone that I couldn’t
             reproduce.

             Hadn’t been saving my
             dSYM bundles…so I
             couldn’t symbolicate the
             logs.
Symboliwhat?
Again, with feeling…

Craig Hockenberry:

  http://furbo.org/2008/08/08/
  symbolicatifination/

  Crash logs have address
  offsets; if you have the
  dSYM bundles, you can
  “symbolicate” to restore
  method names.
Simple script…
# http://furbo.org/stuff/dSYM_Archive.txt
ARCHIVE_ROOT="$PROJECT_DIR/dSYM_Archive"

if [ $SDK_NAME = "iphoneos2.0" ]; then
    echo "Archiving $CONFIGURATION in $ARCHIVE_ROOT at `date`" >> /tmp/dSYM_Archive.out
    if [ $CONFIGURATION = "Distribution-Free" -o $CONFIGURATION = "Distribution-Paid" -o $CONFIGURATION =
"Distribution-Beta-Free" ]; then
        ARCHIVE_CONFIG_FOLDER="$ARCHIVE_ROOT/$CONFIGURATION"
        mkdir -p -v "$ARCHIVE_CONFIG_FOLDER" >> /tmp/dSYM_Archive.out
        ARCHIVE_FOLDER="$ARCHIVE_CONFIG_FOLDER/`date "+%Y-%m-%d-%H%M%S"`"
        echo "Copying $DWARF_DSYM_FOLDER_PATH to $ARCHIVE_FOLDER" >> /tmp/dSYM_Archive.out
        cp -R $DWARF_DSYM_FOLDER_PATH $ARCHIVE_FOLDER >> /tmp/dSYM_Archive.out
    fi
fi
My script…
      #!/bin/bash

      function prepare_adhoc_package()
      {
        if [ ${#} -eq 1 ]; then
          BASE_NAME="${1}"
        else
          BASE_NAME="${PROJECT}"
        fi

          PAYLOAD_FOLDER="${ARCHIVE_FOLDER}/Payload"
          mkdir -p "${PAYLOAD_FOLDER}"

          if [ -d "${ARCHIVE_FOLDER}/${BASE_NAME}.app" ]; then
            cp -rp "${ARCHIVE_FOLDER}/${BASE_NAME}.app" "${PAYLOAD_FOLDER}"
            cp "${ARCHIVE_FOLDER}/${BASE_NAME}.app/iTunesArtwork" "${ARCHIVE_FOLDER}"
            (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload)
          else
            for app in "${ARCHIVE_FOLDER}/*.app"; do
              cp -rp "${app}" "${PAYLOAD_FOLDER}"
              cp -rp "${app}/iTunesArtwork" "${ARCHIVE_FOLDER}"
            done

           (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload)
          fi

          rm -rf "${ARCHIVE_FOLDER}/iTunesArtwork" "${PAYLOAD_FOLDER}"

          open "${ARCHIVE_FOLDER}"
      }

      function archive_dsym_iphoneos()
      {
        case ${CONFIGURATION} in
         *Distribution*) : ;;
         *)         return ;;
        esac

          ARCHIVE_ROOT="${HOME}/projects/dSYMArchive/${PROJECT_NAME}"
          ARCHIVE_DATE=`date +"%Y%m%d-%H%M%S"`

          echo "Archiving ${CONFIGURATION} in ${ARCHIVE_ROOT} at ${ARCHIVE_DATE}" >> /tmp/archive_dsym.out

          ARCHIVE_CONFIG_FOLDER="${ARCHIVE_ROOT}/${CONFIGURATION}"
          mkdir -p -v "${ARCHIVE_CONFIG_FOLDER}" >> /tmp/archive_dsym.out
          ARCHIVE_FOLDER="${ARCHIVE_CONFIG_FOLDER}/${ARCHIVE_DATE}"
          echo "Copying ${DWARF_DSYM_FOLDER_PATH} to ${ARCHIVE_FOLDER}" >> /tmp/archive_dsym.out
          cp -Rp ${DWARF_DSYM_FOLDER_PATH} ${ARCHIVE_FOLDER} >> /tmp/archive_dsym.out

          case ${CONFIGURATION} in
           *[Bb]eta*) prepare_adhoc_package "${1}" ;;
           *)      return ;;
          esac
      }

      case @${SDK_NAME} in
       @iphoneos*) archive_dsym_iphoneos "${1}" ;;
       @*)      : ;;
       @)       echo "Not running under Xcode." ;;
      esac
My script…
        #!/bin/bash

        function prepare_adhoc_package()
        {
          if [ ${#} -eq 1 ]; then
            BASE_NAME="${1}"
          else
            BASE_NAME="${PROJECT}"
          fi

            PAYLOAD_FOLDER="${ARCHIVE_FOLDER}/Payload"
            mkdir -p "${PAYLOAD_FOLDER}"

            if [ -d "${ARCHIVE_FOLDER}/${BASE_NAME}.app" ]; then
              cp -rp "${ARCHIVE_FOLDER}/${BASE_NAME}.app" "${PAYLOAD_FOLDER}"
              cp "${ARCHIVE_FOLDER}/${BASE_NAME}.app/iTunesArtwork" "${ARCHIVE_FOLDER}"
              (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload)
            else
              for app in "${ARCHIVE_FOLDER}/*.app"; do
                cp -rp "${app}" "${PAYLOAD_FOLDER}"




 You can read that, can’t you?
                cp -rp "${app}/iTunesArtwork" "${ARCHIVE_FOLDER}"
              done

             (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload)
            fi

            rm -rf "${ARCHIVE_FOLDER}/iTunesArtwork" "${PAYLOAD_FOLDER}"

            open "${ARCHIVE_FOLDER}"
        }

        function archive_dsym_iphoneos()
        {
          case ${CONFIGURATION} in
           *Distribution*) : ;;
           *)         return ;;
          esac

            ARCHIVE_ROOT="${HOME}/projects/dSYMArchive/${PROJECT_NAME}"
            ARCHIVE_DATE=`date +"%Y%m%d-%H%M%S"`

            echo "Archiving ${CONFIGURATION} in ${ARCHIVE_ROOT} at ${ARCHIVE_DATE}" >> /tmp/archive_dsym.out

            ARCHIVE_CONFIG_FOLDER="${ARCHIVE_ROOT}/${CONFIGURATION}"
            mkdir -p -v "${ARCHIVE_CONFIG_FOLDER}" >> /tmp/archive_dsym.out
            ARCHIVE_FOLDER="${ARCHIVE_CONFIG_FOLDER}/${ARCHIVE_DATE}"
            echo "Copying ${DWARF_DSYM_FOLDER_PATH} to ${ARCHIVE_FOLDER}" >> /tmp/archive_dsym.out
            cp -Rp ${DWARF_DSYM_FOLDER_PATH} ${ARCHIVE_FOLDER} >> /tmp/archive_dsym.out

            case ${CONFIGURATION} in
             *[Bb]eta*) prepare_adhoc_package "${1}" ;;
             *)      return ;;
            esac
        }

        case @${SDK_NAME} in
         @iphoneos*) archive_dsym_iphoneos "${1}" ;;
         @*)      : ;;
         @)       echo "Not running under Xcode." ;;
        esac
dsym-archiver

http://github.com/halostatue/
dsym-archiver

Main features:

  Build adhoc packages.

  Archives dSYM bundles.
One gotcha…
Should be done on every
releasable build.

Needs to run after code signing
for an ad-hoc package to be
usable.

Create a wrapper project; make
the original iPhone target a
dependent project.

Add a custom build step that
runs dsym-archiver at the end.
4. git Build Stamps
              Good idea to have build
              stamps in CFBundleVersion
              for error reporting.

                svn uses repository
                revision number

                Updated on every check-
                in.

              git doesn’t have a repository
              revision number.
CIMGF to the Rescue?
Marcus Zarra wrote a Perl
script to use the short git
commit sharef in April
2008.

  Uses regex replacement.

  Released before the
  iPhone SDK.

  I prefer Ruby.
My Rewrite

Rewrote the basic script to
use Ruby with RubyCocoa.

   I’m too lazy to compile
   MacRuby.

Info.plist loaded with
dictionaryWithContentsOfFile.


Formalized a version class.
iTunes Connect‽
The git SHA reference has
problems on iPhone
projects:

  Non-contiguous,
  confuses iTunes old/new
  for ad hoc builds.

  iTunes Connect wants
  only incrementing dotted
  integers.
git Tags
 git doesn’t have
 incrementing revisions

 We can fake it with tags on
 the commits.

   Downside is that
   repeated builds on the
   same commit means a lot
   of tags.
build-number.rb (1/2)

   #!/usr/bin/ruby

   require 'osx/cocoa'
   …

   # Full implementation at http://github.com/halostatue/xcode-git-version/
   class ProgramVersion
    attr_accessor :major, :minor, :patch, :build, :ref, :ref_type
    def <=>(other); …; end
    def self.parse_version(version_string); …; end
    def short_version; …; end
    def long_version; …; end
    def increment_build!; …; end
   end
build-number.rb (2/2)
   # Load the plist
   info_plist = OSX::NSDictionary.dictionaryWithContentsOfFile(path).mutableCopy
   # Get the current HEAD
   current_head = %x(git describe --always).chomp
   # Get the list of build tags and turn them into version numbers.
   build_tags = `git tag -l build-* --contains #{current_head}`.chomp.split($/)
   build_tags.map! { |tag| ProgramVersion.parse_version(tag.sub(/^build-/, '')) }

   # Get the CFBundleVersion from the plist.
   old_version = ProgramVersion.parse_version(info_plist['CFBundleVersion'])
   # Add the old version to the list of build tags.
   build_tags << old_version.dup
   # Get the largest version we know about for this head.
   new_version = build_tags.max
   # Increment the build number
   new_version.increment_build!

   puts "Version modified: #{old_version} -> #{new_version}"

   # Set the long version (M.m.p.b)
   info_plist['CFBundleVersion'] = new_version.long_version
   # Set the short version (M.m or M.m.p).
   info_plist['CFBundleShortVersionString'] = new_version.short_version
   # Write the file.
   info_plist.writeToFile_atomically(path, 1)
   # Tag the version
   `git tag build-#{new_version.long_version}`

More Related Content

What's hot

Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
Kris Wallsmith
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
Nate Abele
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Kris Wallsmith
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
Nate Abele
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
Kris Wallsmith
 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?
Alexandru Badiu
 
Introduction to the Pods JSON API
Introduction to the Pods JSON APIIntroduction to the Pods JSON API
Introduction to the Pods JSON API
podsframework
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
Nate Abele
 
Undercover Pods / WP Functions
Undercover Pods / WP FunctionsUndercover Pods / WP Functions
Undercover Pods / WP Functions
podsframework
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Developmentjsmith92
 
Legacy applications - 4Developes konferencja, Piotr Pasich
Legacy applications  - 4Developes konferencja, Piotr PasichLegacy applications  - 4Developes konferencja, Piotr Pasich
Legacy applications - 4Developes konferencja, Piotr PasichPiotr Pasich
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
XSolve
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixturesBill Chang
 
Perl Web Client
Perl Web ClientPerl Web Client
Perl Web Client
Flavio Poletti
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
GOG.com dev team
 
Nubilus Perl
Nubilus PerlNubilus Perl
Nubilus Perl
Flavio Poletti
 
this
thisthis
Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3
José Lorenzo Rodríguez Urdaneta
 

What's hot (20)

Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
 
The Origin of Lithium
The Origin of LithiumThe Origin of Lithium
The Origin of Lithium
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
What's new in the Drupal 7 API?
What's new in the Drupal 7 API?What's new in the Drupal 7 API?
What's new in the Drupal 7 API?
 
Introduction to the Pods JSON API
Introduction to the Pods JSON APIIntroduction to the Pods JSON API
Introduction to the Pods JSON API
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Undercover Pods / WP Functions
Undercover Pods / WP FunctionsUndercover Pods / WP Functions
Undercover Pods / WP Functions
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
Legacy applications - 4Developes konferencja, Piotr Pasich
Legacy applications  - 4Developes konferencja, Piotr PasichLegacy applications  - 4Developes konferencja, Piotr Pasich
Legacy applications - 4Developes konferencja, Piotr Pasich
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
 
Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
Perl Web Client
Perl Web ClientPerl Web Client
Perl Web Client
 
Symfony without the framework
Symfony without the frameworkSymfony without the framework
Symfony without the framework
 
Nubilus Perl
Nubilus PerlNubilus Perl
Nubilus Perl
 
this
thisthis
this
 
Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3
 

Similar to solving little problems

Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
Andrew Dupont
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
DynamicInfraDays
 
Introduction To Moco
Introduction To MocoIntroduction To Moco
Introduction To MocoNaoya Ito
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
Lars Jankowfsky
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
John Wilker
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
Ben Scofield
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developersSergio Bossa
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Kris Wallsmith
 
On secure application of PHP wrappers
On secure application  of PHP wrappersOn secure application  of PHP wrappers
On secure application of PHP wrappersPositive Hack Days
 
Doctrine and NoSQL
Doctrine and NoSQLDoctrine and NoSQL
Doctrine and NoSQL
Benjamin Eberlei
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Ivan Chepurnyi
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
Nathan Smith
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
Lindsay Holmwood
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan Wage
 
Windows Azure Storage & Sql Azure
Windows Azure Storage & Sql AzureWindows Azure Storage & Sql Azure
Windows Azure Storage & Sql Azure
Maarten Balliauw
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers Steal
Ben Scofield
 
JavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesJavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best Practices
Siarhei Barysiuk
 

Similar to solving little problems (20)

Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
ContainerDays NYC 2016: "OpenWhisk: A Serverless Computing Platform" (Rodric ...
 
Introduction To Moco
Introduction To MocoIntroduction To Moco
Introduction To Moco
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
I Phone On Rails
I Phone On RailsI Phone On Rails
I Phone On Rails
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
 
Terrastore - A document database for developers
Terrastore - A document database for developersTerrastore - A document database for developers
Terrastore - A document database for developers
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
On secure application of PHP wrappers
On secure application  of PHP wrappersOn secure application  of PHP wrappers
On secure application of PHP wrappers
 
Doctrine and NoSQL
Doctrine and NoSQLDoctrine and NoSQL
Doctrine and NoSQL
 
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for PerformanceMeet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
Meet Magento Sweden - Magento 2 Layout and Code Compilation for Performance
 
Ant
AntAnt
Ant
 
DrupalCon jQuery
DrupalCon jQueryDrupalCon jQuery
DrupalCon jQuery
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Windows Azure Storage & Sql Azure
Windows Azure Storage & Sql AzureWindows Azure Storage & Sql Azure
Windows Azure Storage & Sql Azure
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers Steal
 
JavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesJavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best Practices
 

Recently uploaded

Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Product School
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
DianaGray10
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Product School
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 

Recently uploaded (20)

Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...Mission to Decommission: Importance of Decommissioning Products to Increase E...
Mission to Decommission: Importance of Decommissioning Products to Increase E...
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4UiPath Test Automation using UiPath Test Suite series, part 4
UiPath Test Automation using UiPath Test Suite series, part 4
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 

solving little problems

  • 2. My First iPhone App On contract… for non-developer… who wants to use his own developer account. Presents some challenges. Only my second Objective-C program… what can I learn?
  • 3. Huh? On app store now (yay!). Simple idea—audio translation of silly phrases. Moderate dataset (4 languages, 202 phrases, 808 audio files). May expand later with StoreKit.
  • 4. Four Things Core Data Slow Startup and Progress Debug Archiving Build Numbering with git
  • 5. 1. Core Data Used for language/phrase reference information for easy update. Slightly confusing since I know SQL well. Lots of work to set up queries. Matt Gallagher (Cocoa with Love) showed code that helps.
  • 6. Core Data: one line fetch I adopted and extended this code as a category now on GitHub: github.com/halostatue/ coredata-easyfetch
  • 7. Apple’s Example NSManagedObjectContext *moc = [self managedObjectContext]; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:moc]; NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; [request setEntity:entityDescription]; // Set example predicate and sort orderings... NSNumber *minimumSalary = ...; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary]; [request setPredicate:predicate]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES]; [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; [sortDescriptor release]; NSError *error = nil; NSArray *array = [moc executeFetchRequest:request error:&error]; if (array == nil) { // Deal with error... }
  • 8. Apple’s Example NSManagedObjectContext *moc = [self managedObjectContext]; NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:moc]; 12+ NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; [request setEntity:entityDescription]; // Set example predicate and sort orderings... NSNumber *minimumSalary = ...; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary]; [request setPredicate:predicate]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstName" ascending:YES]; Statements! [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; [sortDescriptor release]; NSError *error = nil; NSArray *array = [moc executeFetchRequest:request error:&error]; if (array == nil) { // Deal with error... }
  • 9. Matt’s Example [[self managedObjectContext] fetchObjectsForEntityName:@"Employee" withPredicate:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary]; 1 Statement!
  • 10. coredata-easyfetch #import “NSManagedObjectContext-EasyFetch.h” … [[self managedObjectContext] fetchObjectsForEntityName:@"Employee" predicateWithFormat:@"(lastName LIKE[c] 'Worsley') AND (salary > %@)", minimumSalary]; Made a category so it’s easy All objects, unsorted (1) to reuse. All objects, sorted (2) Refactored easy filtering. Some objects, unsorted (2) Added easy sorting. Some objects, sorted (4)
  • 11. How I Use It + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc { return [[[moc fetchObjectsForEntityName:@"DataVersion"] mutableCopy] autorelease]; } + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc { NSArray* fetched = [moc fetchObjectsForEntityName:@"Language"]; NSMutableArray* result = [fetched mutableCopy]; [result release]; return result; } + (NSMutableArray*)fetchAllFromManagedObjectContext:(NSManagedObjectContext*)moc { NSArray* fetched = [moc fetchObjectsForEntityName:@"Phrase" sortByKey:@"phraseId" ascending:YES]; NSMutableArray* result = [fetched mutableCopy]; [result release]; return result; }
  • 12. 2. Progress Original plan: Copy all media files from bundle to document area. Problem: Slow (~20–30 seconds) Solution: Show a progress monitor.
  • 14. MBProgressHUD Created in April 2009 Matej Bukovinski http://github.com/matej/ MBProgressHUD/ Other options exist, such as DSActivityView http://www.dejal.com/ developer/dsactivityview
  • 15. Beautiful, but… …the progress HUD blocked the application logo …immediately on launch. Request: Move the HUD to a different spot, and don’t show it unless the work takes 2 seconds or more.
  • 16. Easy enough… Added x- and y-offsets Added a delay-before-showing Later changed by Matej to be a “grace time” Pushed back to master and MBProgressHUD is reasonably active for a little project. My version is at github.com/ halostatue/MBProgressHUD
  • 17. 3. Save the dSYM Had crashes on the client’s iPhone that I couldn’t reproduce. Hadn’t been saving my dSYM bundles…so I couldn’t symbolicate the logs.
  • 19. Again, with feeling… Craig Hockenberry: http://furbo.org/2008/08/08/ symbolicatifination/ Crash logs have address offsets; if you have the dSYM bundles, you can “symbolicate” to restore method names.
  • 20. Simple script… # http://furbo.org/stuff/dSYM_Archive.txt ARCHIVE_ROOT="$PROJECT_DIR/dSYM_Archive" if [ $SDK_NAME = "iphoneos2.0" ]; then echo "Archiving $CONFIGURATION in $ARCHIVE_ROOT at `date`" >> /tmp/dSYM_Archive.out if [ $CONFIGURATION = "Distribution-Free" -o $CONFIGURATION = "Distribution-Paid" -o $CONFIGURATION = "Distribution-Beta-Free" ]; then ARCHIVE_CONFIG_FOLDER="$ARCHIVE_ROOT/$CONFIGURATION" mkdir -p -v "$ARCHIVE_CONFIG_FOLDER" >> /tmp/dSYM_Archive.out ARCHIVE_FOLDER="$ARCHIVE_CONFIG_FOLDER/`date "+%Y-%m-%d-%H%M%S"`" echo "Copying $DWARF_DSYM_FOLDER_PATH to $ARCHIVE_FOLDER" >> /tmp/dSYM_Archive.out cp -R $DWARF_DSYM_FOLDER_PATH $ARCHIVE_FOLDER >> /tmp/dSYM_Archive.out fi fi
  • 21. My script… #!/bin/bash function prepare_adhoc_package() { if [ ${#} -eq 1 ]; then BASE_NAME="${1}" else BASE_NAME="${PROJECT}" fi PAYLOAD_FOLDER="${ARCHIVE_FOLDER}/Payload" mkdir -p "${PAYLOAD_FOLDER}" if [ -d "${ARCHIVE_FOLDER}/${BASE_NAME}.app" ]; then cp -rp "${ARCHIVE_FOLDER}/${BASE_NAME}.app" "${PAYLOAD_FOLDER}" cp "${ARCHIVE_FOLDER}/${BASE_NAME}.app/iTunesArtwork" "${ARCHIVE_FOLDER}" (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload) else for app in "${ARCHIVE_FOLDER}/*.app"; do cp -rp "${app}" "${PAYLOAD_FOLDER}" cp -rp "${app}/iTunesArtwork" "${ARCHIVE_FOLDER}" done (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload) fi rm -rf "${ARCHIVE_FOLDER}/iTunesArtwork" "${PAYLOAD_FOLDER}" open "${ARCHIVE_FOLDER}" } function archive_dsym_iphoneos() { case ${CONFIGURATION} in *Distribution*) : ;; *) return ;; esac ARCHIVE_ROOT="${HOME}/projects/dSYMArchive/${PROJECT_NAME}" ARCHIVE_DATE=`date +"%Y%m%d-%H%M%S"` echo "Archiving ${CONFIGURATION} in ${ARCHIVE_ROOT} at ${ARCHIVE_DATE}" >> /tmp/archive_dsym.out ARCHIVE_CONFIG_FOLDER="${ARCHIVE_ROOT}/${CONFIGURATION}" mkdir -p -v "${ARCHIVE_CONFIG_FOLDER}" >> /tmp/archive_dsym.out ARCHIVE_FOLDER="${ARCHIVE_CONFIG_FOLDER}/${ARCHIVE_DATE}" echo "Copying ${DWARF_DSYM_FOLDER_PATH} to ${ARCHIVE_FOLDER}" >> /tmp/archive_dsym.out cp -Rp ${DWARF_DSYM_FOLDER_PATH} ${ARCHIVE_FOLDER} >> /tmp/archive_dsym.out case ${CONFIGURATION} in *[Bb]eta*) prepare_adhoc_package "${1}" ;; *) return ;; esac } case @${SDK_NAME} in @iphoneos*) archive_dsym_iphoneos "${1}" ;; @*) : ;; @) echo "Not running under Xcode." ;; esac
  • 22. My script… #!/bin/bash function prepare_adhoc_package() { if [ ${#} -eq 1 ]; then BASE_NAME="${1}" else BASE_NAME="${PROJECT}" fi PAYLOAD_FOLDER="${ARCHIVE_FOLDER}/Payload" mkdir -p "${PAYLOAD_FOLDER}" if [ -d "${ARCHIVE_FOLDER}/${BASE_NAME}.app" ]; then cp -rp "${ARCHIVE_FOLDER}/${BASE_NAME}.app" "${PAYLOAD_FOLDER}" cp "${ARCHIVE_FOLDER}/${BASE_NAME}.app/iTunesArtwork" "${ARCHIVE_FOLDER}" (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload) else for app in "${ARCHIVE_FOLDER}/*.app"; do cp -rp "${app}" "${PAYLOAD_FOLDER}" You can read that, can’t you? cp -rp "${app}/iTunesArtwork" "${ARCHIVE_FOLDER}" done (cd "${ARCHIVE_FOLDER}"; zip -qr "${BASE_NAME}.ipa" iTunesArtwork Payload) fi rm -rf "${ARCHIVE_FOLDER}/iTunesArtwork" "${PAYLOAD_FOLDER}" open "${ARCHIVE_FOLDER}" } function archive_dsym_iphoneos() { case ${CONFIGURATION} in *Distribution*) : ;; *) return ;; esac ARCHIVE_ROOT="${HOME}/projects/dSYMArchive/${PROJECT_NAME}" ARCHIVE_DATE=`date +"%Y%m%d-%H%M%S"` echo "Archiving ${CONFIGURATION} in ${ARCHIVE_ROOT} at ${ARCHIVE_DATE}" >> /tmp/archive_dsym.out ARCHIVE_CONFIG_FOLDER="${ARCHIVE_ROOT}/${CONFIGURATION}" mkdir -p -v "${ARCHIVE_CONFIG_FOLDER}" >> /tmp/archive_dsym.out ARCHIVE_FOLDER="${ARCHIVE_CONFIG_FOLDER}/${ARCHIVE_DATE}" echo "Copying ${DWARF_DSYM_FOLDER_PATH} to ${ARCHIVE_FOLDER}" >> /tmp/archive_dsym.out cp -Rp ${DWARF_DSYM_FOLDER_PATH} ${ARCHIVE_FOLDER} >> /tmp/archive_dsym.out case ${CONFIGURATION} in *[Bb]eta*) prepare_adhoc_package "${1}" ;; *) return ;; esac } case @${SDK_NAME} in @iphoneos*) archive_dsym_iphoneos "${1}" ;; @*) : ;; @) echo "Not running under Xcode." ;; esac
  • 23. dsym-archiver http://github.com/halostatue/ dsym-archiver Main features: Build adhoc packages. Archives dSYM bundles.
  • 24. One gotcha… Should be done on every releasable build. Needs to run after code signing for an ad-hoc package to be usable. Create a wrapper project; make the original iPhone target a dependent project. Add a custom build step that runs dsym-archiver at the end.
  • 25. 4. git Build Stamps Good idea to have build stamps in CFBundleVersion for error reporting. svn uses repository revision number Updated on every check- in. git doesn’t have a repository revision number.
  • 26. CIMGF to the Rescue? Marcus Zarra wrote a Perl script to use the short git commit sharef in April 2008. Uses regex replacement. Released before the iPhone SDK. I prefer Ruby.
  • 27. My Rewrite Rewrote the basic script to use Ruby with RubyCocoa. I’m too lazy to compile MacRuby. Info.plist loaded with dictionaryWithContentsOfFile. Formalized a version class.
  • 28. iTunes Connect‽ The git SHA reference has problems on iPhone projects: Non-contiguous, confuses iTunes old/new for ad hoc builds. iTunes Connect wants only incrementing dotted integers.
  • 29. git Tags git doesn’t have incrementing revisions We can fake it with tags on the commits. Downside is that repeated builds on the same commit means a lot of tags.
  • 30. build-number.rb (1/2) #!/usr/bin/ruby require 'osx/cocoa' … # Full implementation at http://github.com/halostatue/xcode-git-version/ class ProgramVersion attr_accessor :major, :minor, :patch, :build, :ref, :ref_type def <=>(other); …; end def self.parse_version(version_string); …; end def short_version; …; end def long_version; …; end def increment_build!; …; end end
  • 31. build-number.rb (2/2) # Load the plist info_plist = OSX::NSDictionary.dictionaryWithContentsOfFile(path).mutableCopy # Get the current HEAD current_head = %x(git describe --always).chomp # Get the list of build tags and turn them into version numbers. build_tags = `git tag -l build-* --contains #{current_head}`.chomp.split($/) build_tags.map! { |tag| ProgramVersion.parse_version(tag.sub(/^build-/, '')) } # Get the CFBundleVersion from the plist. old_version = ProgramVersion.parse_version(info_plist['CFBundleVersion']) # Add the old version to the list of build tags. build_tags << old_version.dup # Get the largest version we know about for this head. new_version = build_tags.max # Increment the build number new_version.increment_build! puts "Version modified: #{old_version} -> #{new_version}" # Set the long version (M.m.p.b) info_plist['CFBundleVersion'] = new_version.long_version # Set the short version (M.m or M.m.p). info_plist['CFBundleShortVersionString'] = new_version.short_version # Write the file. info_plist.writeToFile_atomically(path, 1) # Tag the version `git tag build-#{new_version.long_version}`

Editor's Notes

  1. This past October and November, I developed my first production iPhone app. I developed it on contract for a non-developer designer who wanted to use his own developer account. This obviously presents some challenges. It&amp;#x2019;s also only my second Objective-C program. So, what can I learn while I&amp;#x2019;m doing this?
  2. http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html
  3. http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html