Thursday, June 2, 2011

iOS CALayers

After spending hours using Instruments to find a memory issue where real memory usage was hovering around 170-180MB on an app with many layers I finally was able to pinpoint the cause of the high memory utilization.  The issue was with the size of the CALayer frame's.  The application had about 40 large layers and each was the size of about two-thirds of the screen.  When reducing the size of the frames down to the minimum rectangle needed to display the content at that time, memory usage went down to 40-60MB.  In short, don't make layer frame's larger than absolutely needed. 

Monday, April 11, 2011

Compiling Static Framework for Use in Simulator and on iOS Device

After creating a static framework as described in the previous post it appears that the framework bundle that is viewable in the project pane of Xcode is only for the iphoneos version and not the iphonesimulator version even if the target is specified as the simulator.  Xcode still creates the iphonesimulator version when the simulator target is selected, however it is not viewable in Xcode.  In order to use the simulator version you will need to go to the filesystem.  Go to the same directory as the iphoneos version's parent directory and a directory for the simulator will also be there.  For example, the iphoneos version will be named something like Debug-iphoneos and the simulator will be named something like Debug-iphonesimulator.  Under those directories will be a directory containing your framework named <your product name>.framework, drag and drop that directory into your frameworks group in Xcode to use it.

Friday, April 8, 2011

Creating a Static Framework in Xcode 4

For iOS there is no template in Xcode to build a framework for iOS.  In order to get around this, you can create a Cocoa Static Library project and modify it so that you can develop and compile a static iOS Framework.  Using the post at "http://www.cocoanetics.com/2010/05/making-your-own-iphone-frameworks-in-xcode/" as a basis for writing this tutorial I was able to get it working and used it in a another project by following the steps outlined in this post.  This is the first library I've ever tried to create outside of Java so please feel free to chime in with any comments or corrections.

Create the Xcode project for the framework(skip if you already have an existing project):
- Open Xcode and create a new project (File->New->Project)
- Under the "Framework & Library" section under iOS select "Cocoa Touch Static Library".
- Under "Product Name" enter the name you want to call your framework.
- Click "Next" and "Create".

Add a Bundle target:
- Click the "Add Target" button at the bottom of the Xcode window.
- Select the "Bundle" template under Mac OS X.
- Give it the name of your framework.
- Click "Finish".

Set the Build Settings:
- Change the following properties to the values indicated below...
- Base SDK (replace the value with the appropriate iOS SDK version for your framework).
- Build Active Architecture Only = No
- Architecture and Valid Architectures = $(ARCHS_STANDARD_32_BIT)
- Mac OS X Deployment Target = Compiler Default
- Targeted Device Family (choose the appropriate iOS device i.e. iPhone/iPad)
- Dead Code Stripping = No
- Link With Standard Libraries = No
- Mach-O Type = Relocatable Object File
- Wrapper Extension = framework
- Generate Debug Symbols = No

Modify Info.plist file:
- Under your frameworks "Supporting Files" folder should be a file named <framework>-Info.plist.  Modify the "Bundle OS Type code to "FMWK"

Modify the Prefix.pch file:
- The generated Prefix.pch file under "Supporting Files" contains a reference to the Cocoa framework header file.  Remove that reference from the file.

Remove unnecessary frameworks from your project:
- Under the "Frameworks" folder delete frameworks such as Cocoa.framework and AppKit.framework that your project does not rely on.

Add frameworks required by your framework:
- If the project was created from a Cocoa template, the Cocoa framework is setup by default and not UIKIt which is required for iOS projects.
- Drag and drop any required frameworks such as UIKit under the "Frameworks" folder in your project pane.  In the dialog box that comes up uncheck "Copy items into destination group's folder (if needed)".
- UIKit should be in a folder similar to the following - /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/UIKit.framework

Add and develop your framework code files.

Add a new build phase for "Copy Headers":
- Select your framework target.
- Click the "Build Phases" tab.
- Click the "Add Build Phase" button and select "Add Copy Headers" from the pull-down menu.
- Under "Compile Sources" your source files should already be there, if not add them.
- Under "Link Binary With Libraries" remove any frameworks that might be present.
- Under "Copy Headers" add your .h  files to the section with the appropriate scope.  They can be "Public", "Private", or "Project".  You can drag them from the "Project" section to the "Public" or "Private" sections or from the project file pane.

Create Target for Universal Framework
-------------------------------------------------
- Create an "Aggregate" target named "<Framework Name> Universal Framework".
- Update the "Build Settings" by changing "SDKROOT" to "iphoneos".
- Click "Add Build Phase" and then "Add Run Script".  Copy the script below into the script area in "Run Script".  Be sure to update the "FMK_NAME" property to the name of your framework.

# Sets the target folders and the final framework product.
FMK_NAME=MyFramework
FMK_VERSION=A

# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework

# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework

# Building both architectures.
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator

# Cleaning the oldest.
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi

# Creates and renews the final product folder.
mkdir -p "${INSTALL_DIR}"
mkdir -p "${INSTALL_DIR}/Versions"
mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}"
mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources"
mkdir -p "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers"

# Creates the internal links.
# It MUST uses relative path, otherwise will not work when the folder is copied/moved.
ln -s "${FMK_VERSION}" "${INSTALL_DIR}/Versions/Current"
ln -s "Versions/Current/Headers" "${INSTALL_DIR}/Headers"
ln -s "Versions/Current/Resources" "${INSTALL_DIR}/Resources"
ln -s "Versions/Current/${FMK_NAME}" "${INSTALL_DIR}/${FMK_NAME}"

# Copies the headers and resources files to the final product folder.
cp -R "${DEVICE_DIR}/Headers/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Headers/"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/"

# Removes the binary and header from the resources folder.
rm -r "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/Headers" "${INSTALL_DIR}/Versions/${FMK_VERSION}/Resources/${FMK_NAME}"

# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/Versions/${FMK_VERSION}/${FMK_NAME}"

rm -r "${WRK_DIR}"

Run the "MyApp Universal Framework" target to build the framework.  The framework bundle should now be under the "Products" folder of your project.  To use it in another project simply drag and drop it from your framework project to a folder in your project.

Hope this was helpful!

Unable to Deploy App to iOS Device

While testing an application on the iPad 2 I experienced several crashes.  All of a sudden I was no longer able to deploy to the device and received the following error in the debugger console within Xcode:

Couldn't register COMPANY.MyApp with the bootstrap server. Error: unknown error code.
This generally means that another instance of this process was already running or is hung in the debugger.warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.1 (8G4)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
(gdb)

Tried restarting the machine running Xcode with no success.  Then tried restarting the iPad which seemed to do the trick.  Pretty standard procedure to try to fix issues that seem to have no apparent reason for occurring.
 

Wednesday, April 6, 2011

Updating Cell Data in UITableView

Today I created a subclass of UITableView which would display a list of RSS feed items.  After creating a custom parser with the necessary callbacks to inform the UITableView subclass after parsing each item and when the feed was finished being parsed, the table needed to be updated to display the RSS items that were pulled from the XML feed.  I struggled with this a bit and tried calling setNeedsDisplay at first thinking that it would cause the view to repaint along with the new data, but that didn't work.  So I hopped onto Google and found that when the underlying data for the table changes a call to the reloadData method on the UITableView instance is required.  After doing this everything worked as expected.  Of course afterwards this all made perfect sense, but these kinds of moments are sure to occur frequently as I learn the iOS frameworks.

Tuesday, March 22, 2011

Learning iOS Development

For those looking to learn developing iOS I'd recommend the following material to help you get started.

If you are not familiar with Objective-C a good book to get you started is "Learning Objective-C 2.0" published by Addison Wesley.

To get familiar with the architecture and ideas behind iOS development definitely read through application programming guide at http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007072.

An easy to follow and understand first book on iOS specific development is "Beginning iPhone 4 Development - Exploring the iOS SDK" published by Apress.  This book had very complete examples that worked which helped take the frustration out of trying to figure things out whereas some of the other iOS books leave you scratching your head if you don't know what you're doing.

After the above you can go in many directions depending on the type of application you are interested in writing, but those should get you started on the right path.

Update 4/6/2011 - The above books get you familiar with the concepts without going too far into the weeds that you just get lost, but it also means that you sometimes feel left wondering how things really work or connect.  A decent book for learning how things fit together at the code level without Interface Builder is "Advanced iOS 4 Programming" published by Wiley.  Interface builder is not used to encourage better understanding of how the pieces connect which also allows you to code more advanced applications that are not possible using interface builder alone.

WebSphere 7 JSP Java Version Quirk

A couple weeks ago I was working with a client to deploy some web application enhancements to their WebSphere 7 Express server and ran into an unexpected issue.  I had been developing and testing on WebSphere 7 Developer edition which is a full version of WebSphere, but with unlimited use for development.  After numerous deploys and no changes to the default configuration of the server instance I had expected a smooth deployment to WebSphere 7 Express on the client's server.  However, as it turns out the WebSphere 7 Express version defaults to version 13 of Java which is Java 1.3 even though the rest of the server is running on Java 1.6.  In order to change the version of Java for JSP compilation one must choose the detailed deploy and pre-compile JSP option to set the version to a later version otherwise any JSP's developed using features specific to versions above 1.3 will fail to compile and users will see error messages upon page load attempts.  This is just another of many WebSphere 7 quirks I've run into ever since my first experience with WebSphere on version 5.