16/08/2015
by Stephan Partzsch
0 comments

Continuous Integration for iOS – Code metrics

The last part of the “Continuous Integration for iOS” series covers the configuration of code metrics. Running metrics are a great tool to analyse the quality of the code on continuous level. And this helps to keep the project clean, maintainable and working. This post covers not all of the available metrics, but at the end I linked a few more static analysis tools that can be integrated in the workflow. It’s up to you how much data you want to collect and what you really need. Please always consider that every metric will take its time. Maybe run the most relevant metrics as a new commit is detected and all metrics only once a day at night.

Let’s see how to configure code coverage, lines of code counting, a warnings detector and code duplication.

 

Add code coverage

  1. Install Cobertura-Plugin to show cobertura code coverage reports.
  2. Install gcovr with Homebrew to generate cobertura reports.
    $ brew install gcovr
  3. Generate the code coverage files
    xctool 	-project ${PROJECT_NAME}.xcodeproj \
    	-scheme JenkinsTest \
            -sdk iphonesimulator \
            -configuration Debug \
            -derivedDataPath $BUILD_DIRECTORY\
            test \
           	GCC_GENERATE_DEBUGGING_SYMBOLS=YES \
      	GCC_GENERATE_TEST_COVERAGE_FILES=YES \
      	GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES
    
  4. Generate the code coverage report.
    BUILD_DIRECTORY=~/Desktop/Jenkins_Build
    COVERAGE_DIRECTORY=$BUILD_DIRECTORY/Build/Intermediates/JenkinsTest.build/Debug-iphonesimulator/JenkinsTest.build/Objects-normal/i386
    
    cd $COVERAGE_DIRECTORY
    gcovr —object-directory . —exclude ‘.*Tests.*’ —xml > ${WORKSPACE}/reports/ios-code-coverage.xml
    
  5. Add post-build action, choose Publish Cobertura Coverage Report and add **/reports/ios-code-coverage.xml.
Code Coverage Report

Code Coverage Report

 

Add LOC (lines of code) counting

  1. Install SLOCCount Plugin.
  2. Install cloc with Homebrew.
    $brew install cloc
  3. Add Build-Step and choose Execute shell.
    cloc $PROJECT_NAME —by-file —skip-uniqueness —xml —out=${WORKSPACE}/reports/cloc.xml
  4. Add post-build action, choose Publish SLOCCount analysis results and add **/reports/cloc.xml.
Lines of code

Lines of code

 

Add warnings detector

  1. Install warnings detector.
  2. Add post-build action, choose Scan for compiler warnings and use the “Clang (LLVM based)” parser.
Warnings in files

Warnings in files

Warning categories

Warning categories

 

Add CPD (code duplication detection)

  1. Download PMD in version 4.2.5 — the version is important, version 5.1. did not work for me.
  2. Install Objective-C language definition package for PMD.
  3. Install DRY plugin.
  4. Generate CPD output.
    java -Xmx512m \
    	-classpath /usr/local/Cellar/pmd/4.2.5/lib/pmd-4.2.5.jar:/usr/local/Cellar/pmd/ObjCLanguage-0.0.7-SNAPSHOT.jar net.sourceforge.pmd.cpd.CPD \
    	-minimum-tokens 1 \
    	-files JenkinsTest \
    	-language ObjectiveC \
    	-encoding UTF-8 \
    	-format net.sourceforge.pmd.cpd.XMLRenderer > ${WORKSPACE}/reports/duplicated-code.xml
    
  5. Add post-build action, choose Publish duplicate code analysis results and add **/reports/duplicated-code.xml.

 

After all the setup and configuration hopefully everything runs great and this post was somehow helpful. Especially the metrics stuff needs a lot of fine tuning to be really meaningful. But very often the graph alone gives an idea in which direction the code/project is developing.

In the end here are some paths, links and resources that may help the one or the other.

 


 

Paths

  • Jenkins WAR file: /usr/local/jenkins or /usr/local/Cellar//jenkins/libexec
  • Jenkins home directory: ~/.jenkins

 

Links & Resources

07/08/2015
by Stephan Partzsch
0 comments

Continuous Integration for iOS – Create a build job

Now that the Jenkins server is running as a Launch Agent, it’s time to create a build job for building and testing an app.

  1. The first step is to provide Jenkins the necessary certificates and provisioning profiles. Add them as always to the keychain of the current user.
    Attention! If the Jenkins asks for access to the Keychain, press Always allow in the appearing popup. Otherwise the build process will stop. Alternatively you can unlock the keychain via script (this exposes your password – not recommended!)Additionally I had to login to the Apple developer account in Xcode, otherwise the provisioning profiles won’t be recognised.

     $ security unlock-keychain -p <MAC_USER_PASSWORD> (<KEY_CHAIN_PATH>)
  2. Choose New Item from the left side of the Jenkins dashboard.
  3. Choose Build a free-style software project, give it a speakable name and click OK.
  4. Add a description (html is supported) that explains what the job is doing, when it is triggered and which steps are executed.
  5. Select Discard Old Builds and set Max # of builds to keep to 10.
  6. Configure Source Code Management (the repository url, credentials and the branch to build).
  7. Configure Build-Trigger (e.g. poll SCM every 2 minutes: ‘H/2 * * * *’)
  8. Choose Add build step. If you want to use the Xcode plugin, choose Xcode and fill out the parameter inputs. Or choose Execute shell if you want to follow along and use the script approach.
    If you want to leverage the full power of the script approach, have a look at this build system.

 


 

Script code for building the app

  • Organise Output
    BUILD_DIRECTORY=~/Desktop/Jenkins_Build
    ARTEFACTS_DIRECTORY=~/Desktop/Jenkins_Artefacts/${BUILD_TAG}
  • Clean build directory before building
    rm -rv $BUILD_DIRECTORY
    mkdir -p $BUILD_DIRECTORY
  • Set Xcode version (if a different version is required)
    export DEVELOPER_DIR=/Applications/$XCODE_APP_NAME.app/Contents/Developer
  • Print current Xcode version
    echo “Xcode version: “ `xcodebuild -version`
    echo “Xcode path: “ `xcode-select -print-path`
  • Build the app
    xctool 	-project ${ProjectName} \
    	-scheme JenkinsTest \
            -sdk iphoneos \
           	-configuration Release \
            CONFIGURATION_BUILD_DIR=$BUILD_DIRECTORY \
            clean \
            build
    
  • Create IPA / Signing the app (Specify a special certificate and provisioning profile if you want to create ipa files for different use cases)
    xcrun	-sdk iphoneos \
    	PackageApplication \
            -v $BUILD_DIRECTORY/JenkinsTest.app \
            -o $BUILD_DIRECTORY/JenkinsTest.ipa \
    	#-sign “iPhone Distribution” \
    	#-embed PATH/Example_Ad_Hoc.mobileprovision
    
  • Zip dsym for distribution
    zip -r $BUILD_DIRECTORY/$PROJECT_NAME.dSYM.zip BUILD_DIRECTORY/*.app.dSYM
  • Save artefacts
    mkdir -p $ARTEFACTS_DIRECTORY
    mv $BUILD_DIRECTORY/$PROJECT_NAME.dSYM.zip $ARTEFACTS_DIRECTORY/
    mv $BUILD_DIRECTORY/JenkinsTest.ipa $ARTEFACTS_DIRECTORY/
    

 

Script code for testing the app

  • Build tests and write test report. (If you want to run tests against multiple SDKs, have a look at the xctool documentation)
    xctool	-workspace SampleProject.xcworkspace \
           	-scheme ExampleApp \
           	-reporter plain \
           	-reporter junit:test-reports/report.xml \
           	test \
           	-freshSimulator \
           	-freshInstall
    

 

Publish test results (JUnit-compatible output)

  1. Choose Add post-build action and then choose Publish JUnit test result report.
  2. Add test-report/*.xml into the field Test report XMLs.
  3. After some builds, when enough data was collected, a graph will be shown in the status view of the build job.
Test results

Test results

 

?  The last post in this series shows how to get insights in code quality with code metrics…

04/08/2015
by Stephan Partzsch
0 comments

Continuous Integration for iOS – Install and configure Jenkins

Now that the user account is ready for use, the next step is to install and configure Jenkins server itself. Let’s start right away…

Install Jenkins server

  1. The best way to install Jenkins is via Homebrew. It is less problematic compared to the installation with the installer or the war-file. If you’re still interested in these ways of installing Jenkins, please see this nice blog post.
    $ brew install jenkins
  2. Install and set Java (at least version 1.7). Please follow these instructionsIf this does not work, try these steps:
    • Check the current java version
      $ java -version
    • Check current Java version link (typically this returns ‘java -> /System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java’)
      $ ls -la /usr/bin | grep java
    • Remove current Java version link
      $ sudo rm /usr/bin/java
    • Create new Java version link
      $ sudo ln -s /Library/Internet\ Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java /usr/bin
  3. Configure Jenkins as Launch Agent not as Launch Daemon. A few words about this, because it is important. There are some differences. Most important, Jenkins needs to be run as a Launch Agent to execute tests. Otherwise it has no access to the window manager and therefore can not launch and interact with the iOS Simulator. The other major difference is, that a Launch Agent is started after the user has logged in (because it runs on behalf of the user), while the Launch Daemon is started at system start.
    For more information about the differences between Launch Agent and Launch Daemon see these blog posts [1][2].
    If you see the following error, then Jenkins is configured as a Launch Daemon (described here and there):

    [DEBUG] Session could not be started: Error 
    Domain=DTiPhoneSimulatorErrorDomain Code=2 
    UserInfo=0x100270b90 “Simulator session timed out.”

    To configure Jenkins as a Launch Agent, do the following:

    • Create the directory for Launch Agents if it does not exist.
      $ mkdir -p ~/Library/LaunchAgents
    • Copy Jenkins’ plist to the Launch Agents directory.
      $ cp /usr/local/opt/jenkins/*.plist ~/Library/LaunchAgents

    Now Jenkins gets started right after the user has logged into the system. If you want more automation, you can use the automatic login, (‘System Preferences → Users & Groups → Login Options → Automatic login’). But this reduces security because the system is not password protected anymore. I am ok with entering a password if I work directly on the machine once in a while. And usually the Jenkins does not reboot that often.

  4. Start and stop the Jenkins manually with these commands:
    Start:
    $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
    
    Stop:
    $ launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist
  5. The web interface/dashboard of the Jenkins server should be reachable at http://localhost:8080.

 


 

Configure the Jenkins server

  1. Configure the server parameters, mainly to grant more memory to Jenkins. Edit the default plist file at ~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist. My plist looks like this:
    <?xml version=“1.0” encoding=“UTF-8”?>
    		<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
    		<plist version=“1.0”>
    			<dict>
    				<key>Label</key>
    				<string>homebrew.mxcl.jenkins</string>
    				<key>ProgramArguments</key>
    				<array>
    					<string>/usr/bin/java</string>
    					<string>-d64</string>
    					<string>-Xms1024m</string>
    					<string>-Xmx2048m</string>
    					<!-- Use Concurrent GC-->
    					<string>-XX:+UseConcMarkSweepGC</string>
    					<string>-XX:+CMSClassUnloadingEnabled</string>
    					<string>-XX:MaxPermSize=256m</string>
    					<string>-Dmail.smtp.starttls.enable=true</string>
    					<string>-jar</string>
    					<string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
    					<string>--httpListenAddress=127.0.0.1</string>
    					<string>--httpPort=8080</string>
    				</array>
    				<key>RunAtLoad</key>
    				<true/>
    			</dict>
    		</plist>
    

     

    JVM Virtual Memory

    Set the heap size minimum and maximum with -Xms and -Xmx to give Jenkins a bit more memory power and let Jenkins use a 64-bit data model, if available, with -d64.

    <string>-d64</string>
    <string>-Xms1024m</string>
    <string>-Xmx2048m</string>
    
    HTTP address and port

    Usually Jenkins runs on ‘127.0.0.1:8080’. These values have to go to the ‘jenkins.war’ file.
    If you face the problem that the Jenkins dashboard does not open, try to enable access from all IP addresses by setting ‘httpListenAddress=0.0.0.0.’.

    <string>-jar</string>
    <string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
    <string>--httpListenAddress=127.0.0.1</string>
    <string>--httpPort=8080</string>
    

    If you want to know more about the other configurations like HTTP proxy, garbage collection, environment variables, redirect stdout and stderr, etc., read the configuration part in this blog post.

  2. Install the Jenkins plugins you need for your workflow. Here are some of my favorites:
  3.  The last plugin helps to configure the iOS part of the build process. You make some definitions and the plugin executes the rest of the build process. If you don’t want to have your build configuration stored on Jenkins only and want to build your apps locally on your machine the same way, use build scripts. Commit these scripts to your VCS and you have an executable build system everywhere where the project is checked out.
    Xcode plugin settings

    Xcode plugin settings

    To default build tool is xcodebuild, which is part of the command line tools. But its output and flexibility regarding test execution could be better. And xctool does this job better. The configuration is almost the same.

    $ brew install xctool
  4. Manage the Jenkins. From the left side of the dashboard select Manage Jenkins.
    Configure System
    • Extend PATH variable, otherwise you have to to specify full qualified paths for commands. Under Global properties select Environment variables and add a variable named “PATH” with the value /usr/local/bin:$PATH.
    • Configure Git
    • Configure Jenkins Location
    • Configure Git plugin
    • Configure Xcode Builder (= Xcode plugin) optionally
    • Configure E-Mail setup (to notify users about a broken build)
    • Configure other appropriate settings according your taste
    Configure system

    Configure system

    Configure Global Security
    • Define how users are created and how they authenticate, under Access Control. The easiest way is to let Jenkins manage usernames and passwords by choosing Jenkins’ own user database under Security Realm.
    Configure global security

    Configure global security

    Manage credentials
    • Set credentials which will be used by build jobs and Jenkins itself (i.e. for access to integrated systems like GitHub or Stash, …).
    • Set a SSH private key and give it a speakable name.
  5. Share all schemes that should be used in Jenkins.
    • Open project in Xcode
    • ‘Product ➝ Schemes ➝ Manage Schemes…’
    • Select the Shared checkbox for the required scheme

    Share a scheme

    Share a scheme

 

?  How to setup a build job that runs on the installed Jenkins server is the topic of the next post

22/06/2015
by Stephan Partzsch
0 comments

Continuous Integration for iOS – OS X user account

Continuous integration for iOS is an important part of a professional software development workflow and a helper to automate the build and test process.
Besides automatic builds and tests, Jenkins gives the opportunity to run code metrics, to distribute build artefacts to servers or to run every other task which is required for the job.
Once setup, a developer can focus on writing code and tests. Jenkins does the rest of the job. And this always the same way and while the developer is coding.
At the end you get stable build results and an overview about the quality of your code and where it can be improved.
Jenkins Dashboard

There are many blog posts out there, some good, some bad and some wrong. This series here is my summary of infos that I found in various blog posts and tried out in many exploration times. The next four posts contain what I have done to setup a Jenkins on OS X Yosemite which is able to build, test and analyse my projects.

The setup process can be clustered in the following parts:

  1. Create and/or configure the user account on OS X.
  2. Install and configure the Jenkins server.
  3. Create and configure one or more build jobs.
  4. Configure one or more build jobs to run code metrics.

*Ok, then let’s rock! :)*


1. Create and configure the OS X user account

Some of the following steps belong to my personal preferences and are not necessarily required, however very helpful. They are marked with a little (*).

  1. Create a new user account with admin rights for OS X.
  2. (*) Basically configure the system (faster mouse and keyboard, always visible scrollbars, nice background) and install the tools that make you faster (i.e. a visual git client).
    Configure Keyboard in OS XConfigure Mouse in OS X
  3. (*) Install zsh.
    $ curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh
  4. (*) Install iTerm2.
  5. (*) Configure zsh and iTerm2 to your needs to speed up usage. (the command line will be used very often)
  6. Install Homebrew and follow the instructions.
    $ ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
  7. Install wget via Homebrew.
    $ brew install wget
  8. (*) Install Ruby via RVM or rbenv to get a controlled Ruby environment. The following example generates a `.ruby-version` file in the project repository (should be committed) which sets the local Ruby version for the project to 1.9.3-p327.:
    $ rbenv local 1.9.3-p327
  9. Install Xcode and command line tools. [How to]
  10. Grant Jenkins access to the required source code. Typically this means to generate one or more SSH keys and register them at a hosting service (e.g. Github, Bitbucket, …) For how to generate a SSH key see the awesome Github help page.

 

?  The next post will be about the installation and configuration of the Jenkins server itself…

16/04/2015
by Stephan Partzsch
0 comments

How to ignore changes in tracked files with Git

We all know the .gitignore file as a place to exclude files from the version control system, unless they are not tracked. But once a file is committed, it won’t be affected by the .gitignore system.
Sometimes files get used on a general basis or by a CI server. Therefore they have to exist in the repository, but local and individual changes (for testing purposes) should not be committed. To ignore changes in tracked files the .gitignore system is not suitable.

Fortunately Git has a way to solve this issue. git update-index. But there are two options for this command --assume-unchanged and --skip-worktree. Long time I was aware only of the first, but then I realised that the second option is in most cases a more appropriate solution for the mentioned problem. Therefore I will try to shed some light on each of these ways and the difference between them.

assume-unchanged

You can set a bit for a file to mark it as “unchanged” so that Git will ignore it. This feature was planned as an option to optimise inefficient filesystems, because it makes Git to omit any checking and assume that the file hasn’t changed in the working tree (see git update-index). The problem is that this bit is not safe and will be overwritten in some cases. E.g. if a modified version of the file is pulled from the remote repository or a git reset --hard will be performed.

Set the bit and ignore an already tracked file:

git update-index --assume-unchanged <file>

Unset the bit and reenable tracking:

git update-index --no-assume-unchanged <file>

Instead of fixed file paths, you can use wildcards to ignore all files in a directory with <directory/*>.

List affected files:

git ls-files -v | grep ^[a-z] <path>

git ls-files shows the file status represented by a letter. If a lowercase letter is used, the file is marked as “assume unchanged”. Skip <path> to show the status of all files in the repository. (see git ls-files).

skip-worktree

If this bit is set for a file, Git runs all the normal check routines. Therefore Git knows if a file has been modified but ignores it and takes the version from the index instead of the one from the working tree. This way modifications in the working tree get ignored. The good thing is, that this bit is safe and does not get overwritten. However problems may occur if the file has changed in the local as well as in the remote repository. Stashing will not work and you have to manually reset the --skip-worktree bit. But this seems to be pretty ok.

Set the bit and ignore an already tracked file:

git update-index --skip-worktree <file>

Unset the bit and reenable tracking:

git update-index --no-skip-worktree <file>

List affected files:

git ls-files -v | grep ^[sS]

If the file is marked as “skip-worktree” git ls-files shows a “S”. If it is a lowercase “s”, the bit for “assume-unchanged” is set to. But the “skip-worktree” bit rules.

Differences

A post in the FallenGameR’s blog contains a good summary of the differences and the use case for both options. I will wrap up the essential differences here. Please see the mentioned blogpost for more details.

Operation --assume-unchanged --skip-worktree
File changed in local and remote repository.
git pull
Git wouldn’t overwrite local file, but output conflicts. Git wouldn’t overwrite local file, but output conflicts.
File changed in local and remote repository. git stash & git pull Discards all local changes, no option to restore them. Stash wouldn’t work. Manually unset “skip-worktree” bit first.
File changed only in remote repository. git pull File is updated, “assume-unchanged” bit is unset. File is updated, “skip-worktree” bit is preserved.
File changed only in local repository. git reset --hard File is reverted, “assume-unchanged” bit is unset. File is not reverted, “skip-worktree” bit is preserved.

Conclusion

After all the “skip-corktree” is the better fit for the mentioned problem and indeed it does a really good job in practise.
But no matter which option you choose there is one point to consider: The chosen bit will not be propagated by git push. This means that every user has to run the required commands individually.
The best you can do is to write a small script that contains these commands (and maybe some other settings). Then everybody can run this script after the first checkout of the repository.
There is a detailed description about this point on stackoverflow.

Here are the commands for a small demonstration of git update-index --skip-worktree:

// Create git repository
$ mkdir git-test
$ cd git-test
$ git init
Initialized empty Git repository in …

// Add a file in a new commit
$ echo “Hello World” > file01.txt
$ git add file01.txt
$ git commit -m “Adds a new file.”
[master (root-commit) a6053cf] Adds a new file.
 1 file changed, 1 insertion(+)
 create mode 100644 file01.txt

// Make some changes to the file
$ echo “here are some changes” >> file01.txt
$ git status
On branch master
Changes not staged for commit:
  (use “git add <file>…” to update what will be committed)
  (use “git checkout — <file>…” to discard changes in working directory)

	modified:   file01.txt

no changes added to commit (use “git add” and/or “git commit -a”)

// Ignore the tracked file
$ git update-index --skip-worktree file01.txt
$ git status
On branch master
nothing to commit, working directory clean

// Un-Ignore the tracked file again
$ git update-index --skip-worktree file01.txt
$ git status
On branch master
Changes not staged for commit:
  (use “git add <file>…” to update what will be committed)
  (use “git checkout — <file>…” to discard changes in working directory)

	modified:   file01.txt

no changes added to commit (use “git add” and/or “git commit -a”)

14/11/2013
by Stephan Partzsch
2 Comments

Better performance for labels with outline font in UIKit

For games performance is key. But also style and visual appearance are important, especially in iOS games. And sometimes your local artist comes with a great idea: He wants to have outlines around the labels. “Great!”, you might think, “This won’t be a hard job!” — You are right. To achieve an outline around a label in iOS, you can use one of the many approaches out there.
The bandwidth reaches from utilising the shadow properties of UILabel (UILabel with Stroke, THLabel), which works for iOS 4.0+, till the attributed text, which was introduced in iOS 6.0 (UILabels in iOS 6).
The last is the most efficient solution in terms of performance. But each of these approaches reduces the performance when the labels get drawn. You can feel this in a game or application, if you scroll a long list, that contains many labels with an outline. The feeling is even worse, if labels get updated really fast.

This was not acceptable, especially not in our game, where we want to have the best performance that is possible. Therefore I searched around a lot and profiled every of the before mentioned approaches. The result was not satisfying. None of them was performant enough. So, what else?
One of the last options was to create 2 labels on top of each other. This seemed to be a good solution, but in fact it looked strange. This was because the second label with a slightly bigger font size was scaled from the anchor point of the UILabel. In this example below it was at the center:

Two labels on top of each other

Two labels on top of each other

Again, no useful solution. Hmmm…
Fortunately an other game team at Wooga was researching too and Kamil Chmurzynski and Lucas Bellett came up with a great solution of the 2-label-approach. The idea is to have a dedicated outline font instead of using only a bigger version of the normal font. They use a tool named ‘Font Forge’ to create proper outline fonts. The tool itself is really powerful and you can do advanced stuff, if you have enough knowledge in font science.
I was curious and started to create an outline font for our project. After some traps and mistakes and discussions with Kamil and Lucas, I wrote down all the necessary steps and built a simple prototype. And the result looks like this:

Label with outline

Label with outline

Spectacular, this works great! In one specific situation, which was my test case, I measured that this approach is 5.5 times faster then the one we had before (and this was already the fastest of the previously mentioned approaches).
And this is what you have to do for it:

How to create the outline font with Font Forge:

  1. Install Font Forge — http://fontforge.github.io/en-US/downloads/mac
  2. Load the base font into font forge — File/Open
  3. Change the em size of the base font to a factor 2 number — Element/Font Info…/General

    Font information

  4. Export font via — File/Generate font
  5. Load the modified base font — File/Open
  6. Select all characters — CMD+A
  7. Change the weight of the characters — Element/Style/Change Weight
    • Change the value for Embolden by to something significant (50 ➞ 150), according to how strong you want the outline to be
    • Change the mode to CJK

    Font characteristics

  8. Change the name of the modified font — Element/Font info/PS names
    • This step is important since you don’t want to show up duplicate fonts.
    • IMPORTANT: Make sure that the UID has changed as well!!! (If an already renamed font remains open and you change the name of the font again, Font Forge won’t ask for a UID change)

    Font name

  9. Export font via — File/Generate font
  10. Be aware that validation alerts may occur when saving or modifying the characters or font. Usually this is due to a non-perfect base font where some of the vector shapes are broken (means, they have not closed paths)

How to use both fonts in code

I created an ‘OutlinedLabel’ that inherits from UILabel. It contains some stuff for a convenient use but the essence is in the overridden drawTextInRect method.

- (void)drawTextInRect:(CGRect)rect 
{
    self.font = _outlineFont;
    self.textColor = _outlineColor;
    [super drawTextInRect:rect];

    self.font = _fillFont;
    self.textColor = _fillColor;
    [super drawTextInRect:rect];
}

Here the label text gets drawn two times. The first time is the outline with the new created outline font and an appropriate color. The second time the fill is drawn with the adjusted base font and a fill color. There is no need for managing 2 different labels.
A small test project that shows the outlined label in action, you can find on github (Outlined UILabel).

Drawbacks

Cool, the only thing to do is creating a special outline font, use it and this solves all of our problems? No! This approach works great for numbers. And it also works for characters that are included in your base font file.
BUT, if you try to use special characters, e.g. if your application/game is localised in countries with a different character set, the font substitution of iOS jumps in and replaces your font with a system default font (iOS fonts / Discussion about Chinese Characters). This means that the outline font is substituted too and therefore the outline won’t be visible. The outline will have the same size and dimensions and is drawn underneath the filling, which looks like this:

Label with Japanese text

Label with Japanese text

To avoid this you can choose a font that contains all required characters. If you need Arabic, Japanese, Chinese, Korean, Latin, … characters, your font file size will be increased a lot. And with a bigger base font and a bigger outline font (which is usually even bigger then the base font) the bundle size of the application/game gets increased tremendously.

Conclusion

In the end it is clear, that there is a performant way to get labels with outlines. But I ask myself, if and where this approach is really useful.
Outlines work great for numbers anyways. For normal text you see the outline only if there is no font substitution happening. Therefore you have to take care, that the text stays readable even without the outlines. These leads me to the question why to use outlines for normal text at all? And if you use no outlines for normal text, it may be even more consistent to avoid outlines for numbers as well.
With these questions in mind, the described approach can be only used without any drawback, if the application/game has all required characters in the custom font. This means, if no font substitution will happen.
But after all these questions and thoughts, it is a matter of the design of the application/game, if outlines are needed and in which way they get realised.

03/06/2013
by Stephan Partzsch
0 comments

Using Git Tower with git hooks and ruby gems (Mountain Lion)

Nowadays every programmer uses git as the preferred version control system or at least is familiar with it. Some use the command line interface, others favor visual git clients. My favorite is the powerful Tower. It is easy to use, informations are quicker to find and it offers most of the features you will need to work with git.
But the app has one major drawback. If you have a development workflow that utilizes git hooks to automate certain things, it could be that Tower shows you some errors.
If one of the git hooks is using ruby and requires some special gems, Tower does not know them, if you have them installed in your custom ruby environment. Mostly ruby is managed with either rvm (ruby version manager) or rbenv.
Tower ignores these versions and makes use of the system default ruby version, which is installed with your Mac OSX. On OSX Mountain Lion this ruby version is located here:

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin

Install all the gems you need for this ruby version and Tower will work as expected.