Advertising

Figuring out software version of a passcode-disabled iPhone

If you have an iPhone that's passcode-locked, especially with message that iPhone needs to be connected to iTunes, you may want to try to unlock it by jailbreaking, installing SSH and messing with the filesystem. (tcprelay.py from usbmuxd is very helpful for SSHing if the iPhone doesn't connect to a known wireless network.) There's this tutorial on installing SSH on a disabled iPod, which doesn't use tcprelay.py and is targeted for Windows users, but it should nonetheless give you the starting info.

Before you start you may want to discover which software version is installed on the iPhone.

Current redsn0w, 0.9.11b4, displays the device name, ECID, and software build when you use the "Fetch SHSH" functionality. There's also "Identify" functionality which should do the same thing, but you won't be collecting SHSH blob (it's a good idea if you want to downgrade in the future.) Build name is a string similar to iPhone2,1_5.0.1_9A405 and can be used to obtain the correct .IPSW for your device, enabling you to jailbreak the device and to install contents of "SSH Bundle.tgz". Most important thing is - fetching SHSH blobs is a non-destructive operation.

You're welcome.

(All files mentioned are googleable.)


rest of the post
About me

Overriding HTTP user agent for calls to -initWithContentsOfURL:

Perhaps you need to override the HTTP user agent whenever you call -initWithContentsOfURL: from classes such as NSString, NSDictionary or NSArray, or one of this method's convenience wrappers such as +stringWithContentsOfURL:, +dictionaryWithContentsOfURL: or +arrayWithContentsOfURL:. So let's consider how this can be accomplished under iOS.

From what I can see, there is no easy and "clean" way apart from adding a category on the classes where you need to support this and writing your own implementation of -initWithContentsOfURL: and convenience functions (with a slightly different name, of course). These implementations would use NSURLConnection's +sendSynchronousRequest:returningResponse:error:. Of course, as with -initWithContentsOfURL: you'd use this replacement method in a background thread to maintain UI responsiveness.

You'd have to write a reimplementation of -initWithContentsOfURL: because the first place you can change this is NSURLRequest, or more specifically, its mutable variant NSMutableURLRequest, using the -setValue:forHTTPHeaderField:. But, if you have tons of code, you probably can't easily change it to use the new method.

So I dug in and, with a few smart tricks (such as feeding a broken non-NSURL as a NSURL to figure out which methods get called, then implementing them as necessary), I figured out which of several ways for fetching web content is actually used in NSString's implementation of -initWithContentsOfURL:. These could have been NSURLConnection or some low level messing with CFNetwork.

It turned out not to matter since NSURLRequest is generated out of the NSURL passed to the method. Customizing the user agent turned out to be just a matter of taking all NSURLRequests, forcing them to become mutable copies in form of instances of NSMutableURLRequest during the initializer and setting the user agent at that time. Specific initializer appearing in iOS implementation used in iOS 5 Simulator that ships with Xcode 4.2.1 appears to be -initWithURL:cachePolicy:timeoutInterval:.

It's an enormous hack, but I decided to simply swizzle this method out. Swizzling NSURLConnection's class method +sendSynchronousRequest:returningResponse:error: did not appear to work - the original method still got called despite my best efforts to figure out what went wrong with swizzling, so I gave up on it. If you can see a mistake in my class swizzling code, please tell me about it in the comments section below.

I definitely have no idea whether or not your app will be rejected for this, but from what I know, method swizzling is not illegal.

//  NSURLRequest+UserAgentFix.m

#define YOUR_USER_AGENT @"Your User Agent"
#import "NSURLRequest+UserAgentFix.h"
#import "NSObject+ISSwizzling.h"
@implementation NSURLRequest (UserAgentFix)
+(void)load
{
    [self swizzleMethod:@selector(initWithURL:cachePolicy:timeoutInterval:)
             withMethod:@selector(initWithURL2:cachePolicy:timeoutInterval:)];
}
-(id)initWithURL2:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
{
    self = [self initWithURL2:URL cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
    
    if(!self)
        return nil;
    
    if([self class] == [NSURLRequest class])
        self = [self mutableCopy];
    
    if([self class] == [NSMutableURLRequest class])
    {
        NSMutableURLRequest * req = self;
        [req setValue:YOUR_USER_AGENT forHTTPHeaderField:@"User-Agent"];
    }
    
    return self;
}
@end

// NSURLRequest+UserAgentFix.h
#import 

@interface NSURLRequest (UserAgentFix)

@end

// NSObject+ISSwizzling.h
#import 

@interface NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector;
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector;

@end

// NSObject+ISSwizzling.m
#import 
#import "NSObject+ISSwizzling.h"

@implementation NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getInstanceMethod(self, origSelector);
    Method newMethod = class_getInstanceMethod(self, newSelector);
    
    if (origMethod && newMethod) {
        if (class_addMethod(self, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(self, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getClassMethod(self, origSelector);
    Method newMethod = class_getClassMethod(self, newSelector);
    
    Class class = object_getClass((id)self);

    if (origMethod && newMethod) {
        if (class_addMethod(class, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(class, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}

@end

Tested on iOS 5 Simulator with NSString's +stringWithContentsOfURL:.


rest of the post

Compiling Lua for iOS

To compile Lua for use on iPhone and iPad, I've grabbed the original source code for 5.1.4, and I have grabbed build_for_iphoneos.sh shell script by Christopher Stawarz. Both were slightly modified since Lua does not use autoconf, and build_for_iphoneos.sh is slightly dated. Read more to see what did I do.

You can read the full description with full copies of the files that need to be changed, or you can review diffs on the bottom of the post.

Full description

In "build_for_iphoneos.sh" I had to remove use of configure. I added a call to "make". I've exported "prefix" so I can use it in modified Lua's Makefile. Additionally, the script presumes older iOS SDKs will be shipped in newer Xcode releases, which isn't the case for quite a while now; instead one needs to build with the latest SDK and specify that an older iPhone is a target.

Here's the modified script:

#!/bin/bash

################################################################################
#
# Copyright (c) 2008-2009 Christopher J. Stawarz
# Modifications (c) 2011 Ivan Vucica
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
################################################################################



# Disallow undefined variables
set -u


default_gcc_version=4.2
default_iphoneos_version=3.1.2
default_macos_version=10.5

current_iphone_sdk=4.2

GCC_VERSION="${GCC_VERSION:-$default_gcc_version}"
export IPHONEOS_SDK="${IPHONEOS_DEPLOYMENT_TARGET:-$current_iphone_sdk}"
export IPHONEOS_DEPLOYMENT_TARGET="${IPHONEOS_DEPLOYMENT_TARGET:-$default_iphoneos_version}"
export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-$default_macos_version}"


usage ()
{
    cat >&2 << EOF
Usage: ${0##*/} [-ht] [-p prefix] target [configure_args]
	-h	Print help message
	-p	Installation prefix (default: \$HOME/Developer/Platforms/...)
	-t	Use 16-bit Thumb instruction set (instead of 32-bit ARM)

The target must be "device" or "simulator".  Any additional arguments
are passed to configure.

The following environment variables affect the build process:

	GCC_VERSION			(default: $default_gcc_version)
	IPHONEOS_DEPLOYMENT_TARGET	(default: $default_iphoneos_version)
	MACOSX_DEPLOYMENT_TARGET	(default: $default_macos_version)

EOF
}


while getopts ":hp:t" opt; do
    case $opt in
	h  ) usage ; exit 0 ;;
	p  ) prefix="$OPTARG" ;;
	t  ) thumb_opt=thumb ;;
	\? ) usage ; exit 2 ;;
    esac
done
shift $(( $OPTIND - 1 ))

if (( $# < 1 )); then
    usage
    exit 2
fi

target=$1
shift

case $target in

    device )
	arch=armv6
	platform=iPhoneOS
	extra_cflags="-m${thumb_opt:-no-thumb} -mthumb-interwork"
	;;

    simulator )
	arch=i386
	platform=iPhoneSimulator
	extra_cflags="-D__IPHONE_OS_VERSION_MIN_REQUIRED=${IPHONEOS_DEPLOYMENT_TARGET%%.*}0000"
	;;

    * )
	usage
	exit 2

esac


platform_dir="/Developer/Platforms/${platform}.platform/Developer"
platform_bin_dir="${platform_dir}/usr/bin"
platform_sdk_dir="${platform_dir}/SDKs/${platform}${IPHONEOS_SDK}.sdk"
prefix="${prefix:-${HOME}${platform_sdk_dir}}"

export CC="${platform_bin_dir}/gcc-${GCC_VERSION}"
export CFLAGS="-arch ${arch} -pipe -Os -gdwarf-2 -isysroot ${platform_sdk_dir} ${extra_cflags}"
export LDFLAGS="-arch ${arch} -isysroot ${platform_sdk_dir}"
export CXX="${platform_bin_dir}/g++-${GCC_VERSION}"
export CXXFLAGS="${CFLAGS}"
export CPP="/Developer/usr/bin/cpp-${GCC_VERSION}"
export CXXCPP="${CPP}"


#./configure \
#    --prefix="${prefix}" \
#    --host="${arch}-apple-darwin" \
#    --disable-shared \
#    --enable-static \
#    "$@" || exit
export prefix
make "$@" || exit
make install "$@" || exit

cat >&2 << EOF

Build succeeded!  Files were installed in

  $prefix

EOF

I've called this modified script "build_for_iphoneos_noconfigure.sh", and just like the original one, I've placed it in my path for easier access at a later time.

Next, we need to make slight adaptations to the Makefile in "src/" subdirectory of lua-5.1.4. Primarily, we need to introduce use of LDFLAGS in addition to MYLDFLAGS in a few spots, since they're set by "build_for_iphoneos_noconfigure.sh". We need to remove setting of CC and CFLAGS, since they will also be set by the shell script.

# makefile for building Lua
# see ../INSTALL for installation instructions
# see ../Makefile and luaconf.h for further customization

# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================

# Your platform. See PLATS for possible values.
PLAT= none

#CC= gcc
#CFLAGS= -O2 -Wall $(MYCFLAGS)
AR= ar rcu
RANLIB= ranlib
RM= rm -f
LIBS= -lm $(MYLIBS)

MYCFLAGS=
MYLDFLAGS=
MYLIBS=

# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========

PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris

LUA_A=	liblua.a
CORE_O=	lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
	lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o  \
	lundump.o lvm.o lzio.o
LIB_O=	lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
	lstrlib.o loadlib.o linit.o

LUA_T=	lua
LUA_O=	lua.o

LUAC_T=	luac
LUAC_O=	luac.o print.o

ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)

default: $(PLAT)

all:	$(ALL_T)

o:	$(ALL_O)

a:	$(ALL_A)

$(LUA_A): $(CORE_O) $(LIB_O)
	$(AR) $@ $?
	$(RANLIB) $@

$(LUA_T): $(LUA_O) $(LUA_A)
	$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) $(LDFLAGS)

$(LUAC_T): $(LUAC_O) $(LUA_A)
	$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) $(LDFLAGS)

clean:
	$(RM) $(ALL_T) $(ALL_O)

depend:
	@$(CC) $(CFLAGS) -MM l*.c print.c

echo:
	@echo "PLAT = $(PLAT)"
	@echo "CC = $(CC)"
	@echo "CFLAGS = $(CFLAGS)"
	@echo "AR = $(AR)"
	@echo "RANLIB = $(RANLIB)"
	@echo "RM = $(RM)"
	@echo "MYCFLAGS = $(MYCFLAGS)"
	@echo "MYLDFLAGS = $(MYLDFLAGS)"
	@echo "MYLIBS = $(MYLIBS)"

# convenience targets for popular platforms

none:
	@echo "Please choose a platform:"
	@echo "   $(PLATS)"

aix:
	$(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall"

ansi:
	$(MAKE) all MYCFLAGS=-DLUA_ANSI

bsd:
	$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E"

freebsd:
	$(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline"

generic:
	$(MAKE) all MYCFLAGS=

linux:
	$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"

macosx:
	$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline"
# use this on Mac OS X 10.3-
#	$(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX

mingw:
	$(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \
	"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
	"MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe
	$(MAKE) "LUAC_T=luac.exe" luac.exe

posix:
	$(MAKE) all MYCFLAGS=-DLUA_USE_POSIX

solaris:
	$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl"

# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) default o a clean depend echo none

# DO NOT DELETE

lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \
  lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \
  lundump.h lvm.h
lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h
lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h
lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \
  ltable.h
ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h
ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \
  llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
  lfunc.h lstring.h lgc.h ltable.h lvm.h
ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \
  ltable.h lundump.h lvm.h
ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \
  lzio.h lmem.h lundump.h
lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \
  lstate.h ltm.h lzio.h
lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
  lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h
liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h
llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \
  lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h
lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h
lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
  ltm.h lzio.h lmem.h ldo.h
loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h
lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \
  ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h
lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h
loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h
lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
  lfunc.h lstring.h lgc.h ltable.h
lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
  ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h
lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \
  ltm.h lzio.h lstring.h lgc.h
lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h
ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
  ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h
ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h
ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \
  lmem.h lstring.h lgc.h ltable.h
lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h
luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \
  lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \
  lundump.h
lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \
  llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h
lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h
lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
  lzio.h
print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \
  ltm.h lzio.h lmem.h lopcodes.h lundump.h

# (end of Makefile)

Finally, you need to introduce a slight change to the Makefile in root of Lua's distribution. INSTALL_TOP needs to be a replica of "prefix" environment variable since it's set by the shell script.

Here's just the excerpt of the top of the Makefile. I think you'll easily notice what's

# makefile for installing Lua
# see INSTALL for installation instructions
# see src/Makefile and src/luaconf.h for further customization

# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================

# Your platform. See PLATS for possible values.
PLAT= none

# Where to install. The installation starts in the src and doc directories,
# so take care if INSTALL_TOP is not an absolute path.
INSTALL_TOP= ${prefix}
INSTALL_BIN= $(INSTALL_TOP)/bin
INSTALL_INC= $(INSTALL_TOP)/include
INSTALL_LIB= $(INSTALL_TOP)/lib
INSTALL_MAN= $(INSTALL_TOP)/man/man1

###### -- continues below --
 

That's it. Now you can compile Lua for platform "generic" (trying to compile for "macosx" pulls in readline which isn't available on iPhone):

build_for_iphoneos_noconfigure.sh device generic

Output should end with:

Build succeeded!  Files were installed in

  /Users/ivucica/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.2.sdk

Excerpt from documentation for "build_for_iphoneos.sh", which explains how to set up your Xcode project:

By default, the script will install files in a subdirectory of $HOME/Developer/Platforms that mirrors the layout of /Developer/Platforms. For example, the installation prefix for a device build defaults to $HOME/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.1.sdk. The advantage of this is that you can make Xcode aware of the installed files for both the simulator and the device by adding the following under "All Configurations" in your project settings:

Header Search Paths: $(HOME)/$(SDK_DIR)/include Library Search Paths: $(HOME)/$(SDK_DIR)/lib

Of course, you can move these things into a better place, or you can attempt to design an SDK, similar to what I've done when adding iPhone support to libapril and friends.

Just the diffs

Using Mercurial I quickly made the following diff for lua-5.1.4, if you're interested only in diff on Makefiles:

diff -r 130652fce1b4 Makefile
--- a/Makefile  Fri Mar 04 14:13:36 2011 +0100
+++ b/Makefile  Fri Mar 04 14:13:56 2011 +0100
@@ -9,7 +9,7 @@
 
 # Where to install. The installation starts in the src and doc directories,
 # so take care if INSTALL_TOP is not an absolute path.
-INSTALL_TOP= /usr/local
+INSTALL_TOP= ${prefix}
 INSTALL_BIN= $(INSTALL_TOP)/bin
 INSTALL_INC= $(INSTALL_TOP)/include
 INSTALL_LIB= $(INSTALL_TOP)/lib
diff -r 130652fce1b4 src/Makefile
--- a/src/Makefile      Fri Mar 04 14:13:36 2011 +0100
+++ b/src/Makefile      Fri Mar 04 14:13:56 2011 +0100
@@ -7,8 +7,8 @@
 # Your platform. See PLATS for possible values.
 PLAT= none
 
-CC= gcc
-CFLAGS= -O2 -Wall $(MYCFLAGS)
+#CC= gcc
+#CFLAGS= -O2 -Wall $(MYCFLAGS)
 AR= ar rcu
 RANLIB= ranlib
 RM= rm -f
@@ -52,10 +52,10 @@
        $(RANLIB) $@
 
 $(LUA_T): $(LUA_O) $(LUA_A)
-       $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
+       $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) $(LDFLAGS)
 
 $(LUAC_T): $(LUAC_O) $(LUA_A)
-       $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
+       $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) $(LDFLAGS)
 
 clean:
        $(RM) $(ALL_T) $(ALL_O)

And diff for the build_for_iphoneos.sh:

--- build_for_iphoneos.sh       2011-03-04 12:38:06.000000000 +0100
+++ build_for_iphoneos_noconfigure.sh   2011-03-04 13:48:05.000000000 +0100
@@ -36,7 +36,10 @@
 default_iphoneos_version=3.1.2
 default_macos_version=10.5
 
+current_iphone_sdk=4.2
+
 GCC_VERSION="${GCC_VERSION:-$default_gcc_version}"
+export IPHONEOS_SDK="${IPHONEOS_DEPLOYMENT_TARGET:-$current_iphone_sdk}"
 export IPHONEOS_DEPLOYMENT_TARGET="${IPHONEOS_DEPLOYMENT_TARGET:-$default_iphoneos_version}"
 export MACOSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-$default_macos_version}"
 
@@ -103,7 +106,7 @@
 
 platform_dir="/Developer/Platforms/${platform}.platform/Developer"
 platform_bin_dir="${platform_dir}/usr/bin"
-platform_sdk_dir="${platform_dir}/SDKs/${platform}${IPHONEOS_DEPLOYMENT_TARGET}.sdk"
+platform_sdk_dir="${platform_dir}/SDKs/${platform}${IPHONEOS_SDK}.sdk"
 prefix="${prefix:-${HOME}${platform_sdk_dir}}"
 
 export CC="${platform_bin_dir}/gcc-${GCC_VERSION}"
@@ -115,14 +118,15 @@
 export CXXCPP="${CPP}"
 
 
-./configure \
-    --prefix="${prefix}" \
-    --host="${arch}-apple-darwin" \
-    --disable-shared \
-    --enable-static \
-    "$@" || exit
-
-make install || exit
+#./configure \
+#    --prefix="${prefix}" \
+#    --host="${arch}-apple-darwin" \
+#    --disable-shared \
+#    --enable-static \
+#    "$@" || exit
+export prefix
+make "$@" || exit
+make install "$@" || exit
 
 cat >&2 << EOF
 


rest of the post

Correct check for Game Center availability

Apple's example code for Game Center does something strange -- in fact, we can simply go out and call those things bugs. Keep reading to see what I'm talking about. Here's the code.

        // check for presence of GKLocalPlayer API
        Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
        
        // check if the device is running iOS 4.1 or later
        NSString *reqSysVer = @"4.1";
        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
        BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);

        return (gcClass && osVersionSupported);

Well, that's incorrect and doesn't count on the fact that Game Center is unavailable on iPhone 3G despite iOS 4.1 and 4.2 existing there. If your goal wasn't to avoid crashes caused by Game Center classes not existing, but to check if Game Center really is available, you need to check if you are running on iPhone 3G. Update April 6th 2011: I have missed a few lines in the documentation that say you will receive GKErrorNotSupported when authenticating local player, if Game Center is not available. However, a library I'm working on needs this sort of check to return availability prior to authenticating player, to determine best achievement engine. You may need this as well, so I'm keeping this up. An excellent way to do this was posted on iOS Developer Tips and uses sysctlbyname() C function found in BSD operating systems such as OS X or iOS to query the kernel for value of hw.machine string.

The above method proposes adding a category to UIDevice that adds -(NSString*)machine. While this would be a most excellent way to do this, it is not an appropriate way to handle stuff in a C++ library; it should not add categories or anything like that unless it is absolutely necessary, so I just patched the function that checks availability of Game Center directly.

Compared to the iOS Developer Tips' solution, I also added a check whether or not sysctlbyname() fails. This should prevent any problems in case Apple decides to remove support for hw.machine (although I see no reason for them doing that).

We don't need to check for iPhone, iPod Touch (no iOS4) or iPod Touch 2G (Game Center available).

Let's take a look at the code:

    bool GameCenterAchievementsService::isGameCenterAvailable()
    {
        // check for presence of GKLocalPlayer API
        Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
        
        // check if the device is running iOS 4.1 or later
        NSString *reqSysVer = @"4.1";
        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
        BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
        
        // device must not be an iphone 3g
        bool validDevice = true;
        {
            size_t size;
            if(sysctlbyname("hw.machine", NULL, &size, NULL, 0)!=-1)
            {
                char*name = (char*)malloc(size);
                if(sysctlbyname("hw.machine", name, &size, NULL, 0)!=-1)
                {
                    if (!strcmp(name, "iPhone1,1") || !strcmp(name, "iPhone1,2") || !strcmp(name, "iPod1,1")) 
                    {
                        validDevice = false;
                    }
                }
                free(name);
            }
        }
        
        return (gcClass && osVersionSupported && validDevice);
    }

Note that this is Objective-C++, but is trivial to patch for pure Objective-C. Report bugs in comments section below!


rest of the post

GKTapper - Apple's buggy example

I've caught a few mistakes in GKTapper. Read on to see how the sample performs invalid caching of achievements and to see how it shows incorrect string as leaderboard description in one place. Invalid caching of achievements

Achievements in -submitAchievement:percentComplete: will never be cached. Also, the comment makes little sense; a sentence appears to be missing.

- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
	//GameCenter check for duplicate achievements when the achievement is submitted, but if you only want to report
	// new achievements to the user, then you need to check if it's been earned
	// before you submit.  Otherwise you'll end up with a race condition between loadAchievementsWithCompletionHandler
	// and reportAchievementWithCompletionHandler.  To avoid this, we fetch the current achievement list once,
	// then cache it and keep it updated with any new achievements.
	if(self.earnedAchievementCache == NULL)
	{
		[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
		{
			if(error == NULL)
			{
				NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
				for (GKAchievement* score in tempCache)
				{
					[tempCache setObject: score forKey: score.identifier];
				}
				self.earnedAchievementCache= tempCache;
				[self submitAchievement: identifier percentComplete: percentComplete];
			}
			else
			{
				//Something broke loading the achievement list.  Error out, and we'll try again the next time achievements submit.
				[self callDelegateOnMainThread: @selector(achievementSubmitted:error:) withArg: NULL error: error];
			}

		}];
	}
	else
}

Where's the bug? Code iterates over the just-created, empty dictionary "tempCache". Corrected:

				for (GKAchievement* score in scores) // this used to read tempCache

Leaderboard description invalid

Code in method mappedPlayerIDToPlayer:error: contains a small bug:

		self.leaderboardHighScoreDescription= @"GameCenter Scores Unavailable";
		self.leaderboardHighScoreDescription=  @"-";

It should probably read:

		self.leaderboardHighScoreDescription= @"GameCenter Scores Unavailable";
		self.leaderboardHighScoreString=  @"-";


rest of the post

Getting started with Objective-C?

If you want to get started with Objective-C, and you have some background in C/C++, read this neat post I randomly stumbled upon: "Why Objective-C is cool". It's a pretty nice description and hopefully removes a lot of WTFs from a newbie.

Then, read CocoaLab's free e-book "Become an Xcoder".

Finally, this is the longest route, but the one I went with: watch all 18 hours + extras of CS193P iPhone Application Development 2009 (and then go watch some of the 2010 lessons, too). It takes a long time to watch, but it's worth it.


rest of the post