You are currently browsing the category archive for the ‘iOS’ category.
Water by the Bucket!
As a companion to my NotchFlow App I have just released the free BucketFlow App. Where as NotchFlow measure water flowing over a V-notch weir, BucketFlow measures water flow by timing how long it takes to fill a fixed sized container.
In addition to calculating the flow, BucketFlow also estimates the population that could be supported by a given flow-rate using one of five predefined per capita water usage profiles.
However if that is not what you want, BucketFlow can perform its calculations three different ways:
- Flow and population calculated from a given fill-time
- Fill-time and population calculated from a given flow
- Fill-time and flow calculated from a given population
Additional information about the App can be found on the BucketFlow website
Picture it
Here are some screen shots of the BucketFlow App:
![]() Calculations |
![]() Preferences |
![]() Information |
The Calculations screen shows the flow (238 l/hr) and population (57) being calculated when a container (1l) fills in a given time (15s). But the calculation screen also allows the other two calculation types to be easily selected.
The Preferences screen shows the three main configurable preferences:
- The units system: Metric, Imperial and US
- The per capita daily water consumption profile
- The size of the container being filled
The Information screen shows the wide range of information and design notes built into BucketFlow.
Source of my inspiration
The idea for this App came about while attending the EWB USA South East conference last year. As part of a session on mixing concrete, the flow rate of a garden hose was being estimate by timing the filling of a bucket with a stop watch and then doing a manual calculation. When I saw this a light bulb went off in my head as I saw how I could adapt my NotchFlow App to do the timing and calculation in one. It’s amazing how a little practical experience can stimulate the mind – and in hind-sight I think that I should have written BucketFlow before NotchFlow.
Adios
I hope people find this App useful, and I look forward to releasing my next App!
Ciao, Peter
New! Improved! Or how it should be done in the first place!
Previously I wrote an article about integrating Git and Xcode: Displaying Git details in XCode 4.2 projects. That was a good approach but after writing it I realized that I had only completed half the task I should have, and this article addresses how I should have done it.
What I was trying to do in that previous article was to automatically include versioning information from the Git repository into the iOS application. In doing so I could then explicitly match any version of the App in the wild to the actual code used to produce that App – which would aid greatly in diagnosing any reported bugs. The script I provided extracted the last commit date and hash as well as the last tag inserted into the repository. It then generated an include file that imported the data as Objective-C string literals that could be used directly by the code.
This process worked well, but it wasn’t until I went to upgrade an App in the App store that I saw what was missing from what I did. The problem was that even though I was including the version information in the code itself, the version information that Apple wanted needed to be included in Info.plist file for the App. So regardless of my smarts, I was still left with having to manually update the version and build information in XCode prior to submitting an Archive to Apple.
So this article addresses the deficiencies of the previous article and shows how to automatically inject information into the Info.plist file and achieve a “hands-off” approach to tagging your Apps with their build number. And while I am using Git to supply that data, this build number could come from any source you liked.
What needs to change
The version information that I ultimately want to automatically set are two values in the Info.plist file for the project. These are (with descriptions taken from Apple):
CFBundleVersion | Specifies the build version number of the bundle, which identifies an iteration (released or unreleased) of the bundle. This is a monotonically increased string, comprised of one or more period-separated integers. This key is not localizable. |
CFBundleShortVersionString | Specifies the release version number of the bundle, which identifies a released iteration of the app. The release version number is a string comprised of three period-separated integers. The first integer represents major revisions to the app, such as revisions that implement new features or major changes. The second integer denotes revisions that implement less prominent features. The third integer represents maintenance releases.
The value for this key differs from the value for “CFBundleVersion,” which identifies an iteration (released or unreleased) of the app. This key can be localized by including it in your InfoPlist.strings files. |
Within an XCode project, CFBundleShortVersionString and CFBundleVersion can be found in these locations:
Info.plist field | Summary Name | Info name |
---|---|---|
CFBundleShortVersionString | Version | Bundle version string; short |
CFBundleVersion | Build | Bundle version |
But first a word from your sponsor
In scanning various websites I have seen a lot of pages that want to achieve the same outcome as what I want. However A lot of them are based on executing /usr/libexec/PlistBuddy in order to directly insert data into the Info.plist file. This works, but to me it suffers from some deficiencies:
- Directly rewriting a file in the project is a brute force approach.
- If the project is under version control, then the act of building the project changes the version history.
- Because the value is changed “behind the scenes”, when looking at the project in Xcode it isn’t obvious that the values in question are being changed.
- It ignores the mechanism that Apple already provides for updating the file.
So with that said I am going to charge my solution with:
- Using the Apple supplied method for changing the Info.plist file
- Raising the visibility of the data that is being automatically changed
- Not changing the project’s version history when simply building the project
The true path
The key to doing things the Apple way is to note two options in a project’s build settings:
- Preprocess Info.plist File
- Info.plist Preprocessor prefix file
Setting the first one to “yes” turns on the processing of the Info.plist file and perform the substitutions of symbols for values, while the second setting designates the file that contains the required #defines that link the symbols with their values.
In my case (as per my previous article) I set the prefix file to be “gitDataAutoGenerated.h”, so that all I need to do is to generate this file prior to the Info.plist preprocessing during the building of the project. Within that file I created a #define called AUTOVERSION which I matched up with my auto created version number extracted from the Git repository for the project. Then in the the appropriate spots on the projects Summary or Info Page, I set the value of the CFBundleVersion and/or CFBundleShortVersionString to be “AUTOVERSION” (with no quotes) and then everything magically gets linked up when I build the project.
And as “gitDataAutoGenerated.h” is not a part of the project, it can be changed with impunity without affecting the project’s version control history.
First things first
As per my previous article, I had created a “run script” build phase within the project that hosted the script that generated the “gitDataAutoGenerated.h” file. But this approach totally failed when it came to modifying the Info.plist file. After inspection of the build output messages, it was obvious that even though I had set up the script to run prior to any compilation, the preprocessing of the Info.plist file was actually being run before the script. So it was time for plan “B”
Plan “B” is to create an additional target within the project that is dedicated to building the include file, and to make the App target dependent on the new script target. The process for doing this is:
- Add a new “Aggregate” target to the project called “VersionNumberScripts”
- Within the Build phases of this target, create a “Run script” phase and either copy over the the script from the equivalent phase from the App, or create the script from scratch. (If the script was copied, then the App’s run script phase can be deleted.)
- Within the build phases of the App, add the new VersionNumberScripts target as a Target Dependency
Once this is done the version number script will be run prior to the App target being built, thus ensuring that the correct information is ready for XCode to preprocess the Info.plist file in a timely manner.
Following the script
While the script from the previous article was fairly decent, I have updated it to be a little more robust by including default values for if/when the git commands fail to return any data. This new script is:
# build data file that is included in the source # so we can automatically report Git repo information # in the application echo "Building file" cd ${PROJECT_DIR}/${PROJECT_NAME} gitDataFile="gitDataAutoGenerated.h" echo "Get Information from system" # Date and time that we are running this build buildDate=`date "+%F %H:%M:%S"` # Current branch in use currentBranchTemp=`git rev-parse --abbrev-ref HEAD` if [ -n "$currentBranchTemp" ] then currentBranch=$currentBranchTemp else currentBranch="" fi # Last hash from the current branch lastCommitHashTemp=`git log --pretty=format:"%h" -1` if [ -n "$lastCommitHashTemp" ] then lastCommitHash=$lastCommitHashTemp else lastCommitHash="" fi # Date and time of the last commit on this branch lastCommitDateTemp=`git log --pretty=format:"%ad" --date=short -1` if [ -n "$" ] then lastCommitDate=$lastCommitDateTemp else lastCommitDate="" fi # Comment from the last commit on this branch lastCommitCommentTemp=`git log --pretty=format:"%s" -1` if [ -n "$" ] then lastCommitComment=$lastCommitCommentTemp else lastCommitComment="" fi # Last tag applied to this branch lastRepoTagTemp=`git describe --abbrev=0 --tags` if [ -n "$lastRepoTagTemp" ] then lastRepoTag=$lastRepoTagTemp else lastRepoTag="0.0.0" fi # Build the file with all the information in it echo "Create header file" echo -e "//-----------------------------------------" > $gitDataFile echo -e "// Auto generated file" >> $gitDataFile echo -e "// Created $buildDate" >> $gitDataFile echo -e "//-----------------------------------------" >> $gitDataFile echo -e "" >> $gitDataFile echo -e "#define BUILD_DATE @\"$buildDate\"" >> $gitDataFile echo -e "#define GIT_CURRENT_BRANCH @\"$currentBranch\"" >> $gitDataFile echo -e "#define GIT_LAST_COMMIT_HASH @\"$lastCommitHash\"" >> $gitDataFile echo -e "#define GIT_LAST_COMMIT_DATE @\"$lastCommitDate\"" >> $gitDataFile echo -e "#define GIT_LAST_COMMIT_COMMENT @\"$lastCommitComment\"" >> $gitDataFile echo -e "#define GIT_LAST_REPO_TAG @\"$lastRepoTag\"" >> $gitDataFile echo -e "#define AUTOVERSION $lastRepoTag" >> $gitDataFile # Force the system to process the plist file echo "touch plist" touch ${PROJECT_DIR}/Info.plist
Note that this script does depend on the target name of the App being the same as that of the overall project.
An example of this scripts output is:
//----------------------------------------- // Auto generated file // Created 2012-10-24 15:57:47 //----------------------------------------- #define BUILD_DATE @"2012-10-24 15:57:47" #define GIT_CURRENT_BRANCH @"versionNumber" #define GIT_LAST_COMMIT_HASH @"2a92057" #define GIT_LAST_COMMIT_DATE @"2012-10-24" #define GIT_LAST_COMMIT_COMMENT @"Fixed layout of About view controller" #define GIT_LAST_REPO_TAG @"1.1.0" #define AUTOVERSION 1.1.0
Tag! You’re It
So you now have this great script that will extract tags from the project’s Git repository and insert them in the App as needed. You successfully build the App and look at the result .. but .. but ..where’s the tags?!?!??! why are they missing?!?!? I know that I tagged the release in Git.
And that’s what happened to me when I was building this system. I had tagged the repository, I had built the release, but there were no tags to be seen. The issue turned out to be where I was building the App.
At the same time I was creating this new build process, I was also setting up an additional computer for development, and I was testing the new build system on this new computer. In order to transfer the project’s Git repository to the new computer, I created a local Git server (well really a user named git on my main dev computer), pushed the project from the original system to the git server, and then cloned the project onto the new computer. Simple! Easy! Allows me to work on multiple computers! All sorts of possibilities now start to emerge.
What I hadn’t realized was that if you push a repository to another location the tags don’t get pushed. You have explicitly push the tags in a separate command. See 2.6 Git Basics – Tagging for a complete explanation on tagging.
So in addition to doing something like this:
git push origin master
I also have to do
git push origin --tags
It kinda sucks splitting the functionality up like this, but at least I learnt something new about git.
Last things last
Well that’s a wrap on my new build system. Given how easy it was to set things up the right way I’m kicking myself for not doing this the first time around. Now I have this nailed down I’m in the process of updating my NotchFlow App to run under iOS 6 (and with the new 4″ retina screen resolution). So look forward to update on that App soon.
Finally I have to put in a plug for 89Paint in Richmond. They are sponsoring the local Gangplank group and I have been working out of their offices this week – it sure beats sitting at home by myself talking to the cat!
NOTE that this App has been updated to support iOS6 and the 4 inch retina screen
A new app is born
I have finally released (and debugged and re-released) the NotchFlow App on the iTunes store. The App is available globally, so if you don’t have access to the US store, please try your local one.
NotchFlow calculates the flow-rate of water passing over the top of a fully contracted V-Notch weir by simply measuring the height of the flow above the bottom of the weir. In addition to calculating the flow, NotchFlow also estimates the population that could be supported by a given flow-rate using one of five predefined per capita water usage profiles.
However if that is not what you want, NotchFlow can perform its calculations three different ways:
- Flow and population calculated from a given height
- Height and population calculated from a given flow
- Height and flow calculated from a given population
Additional information about the App can be found on the NotchFlow website
Pretty Pictures
Here are some screen shots of the NotchFlow App:
![]() Calculations |
![]() Preferences |
![]() Information |
![]() Weir Design |
The Calculations screen shows the flow and population being calculated from the given height, but also allows the other two calculation types to be easily selected.
The Preferences screen shows the three main configurable preferences:
- The units system: Metric, Imperial and US
- The per capita daily water consumption profile
- The actual notch angle used in the weir under consideration
The Information screen shows the wide range of information and design notes built into NotchFlow.
Finally the Weir Design screen shows part of the design notes available to help build the correct weir shape.
Stealing ideas
The basis for the calculation part of the App was “borrowed” from United States Department of the Interior, Bureau of Reclamation’s Water Measurement Manual. This publication describes the basic calculation needed to determine the flow-rate as well as providing notes on how the weir itself should be constructed. The NotchFlow App contains a summary of the design notes from that site with enough detail so that the proper weir can be built in the field.
The pre-defined per capita water profiles were adapted from the classifications defined in the WHO Guidelines for drinking-water quality. The WHO classifications aim to relate the level of public health risk for a person based on how much access they have to clean drinking water.
Adios
I hope people find this App useful, and I look forward to releasing my next App!
Ciao, Peter
NOTE that this code has been updated to support iOS6 and the 4 inch retina screen
The What And Why Of It All
This week I’m giving back some code that I developed as a learning exercise for moving an object along a bezier path under the iOS environment. This code also has its roots in the answers to some questions I posted on StackOverflow.com, hence part of my desire to give back a fully working solution. These questions being:
iOS CAKeyframeAnimation rotationMode on UIImageView: Solved
iOS CAKeyFrameAnimation Scaling Flickers at animation end
PathMove
The PathMove program allows the user to set up a predetermined set of bezier paths and then trigger a UIImageView object to move from start to end along that path. The program incorporates several options to do things like:
- Select the ending quadrant of the bezier path.
- Mix and match three different predefined bezier path segments for the starting and ending segments of the overall path.
- Allow the object to grow, shrink or remaiin the same size as it moves along the path.
- Rotate the object to match the tangent of the bezier curve as the object moves.
- Pre-Rotate the object by 90 degrees to accommodate how iOS calculates a tangent.
- Annotate the complete bezier path with the size and location of all of the path’s the control points
You can download the source code for PathMove from GitHub at https://github.com/JoalahDesigns/PathMove
I have released this code under a BSD-3 license, so you are free to use however you like as long as you acknowledge where it originated from. And if you do use any part of it please drop me a line to let me know!
Screen Shots
The program consists of two screens – a main screen where the path is drawn and the object moves, and a setup screen which contains all the options that the user can make. The following screen shots show the object moving along a path and the corresponding setup screen that created the path. The final screen shot shows how the bezier path can be annotated in order to show all the control points.
![]() PathMove Main screen |
![]() PathMove setup screen |
![]() PathMove Annotated Path |
Code Construction
The PathMove program is built up from the following classes:
- JdViewController Forms the display for the main screen and animates the object to move along the bezier path.
- JdGraphicView Draws the graph axis, bezier path and assorted boxes.
- JdSetupViewController Forms the display for the setup screen.
- JdConfiguration Passes configuration information between the two screens.
- JdBezierPoint Contains the definitions of a single bezier point in the path.
- JdBezierPath Maintains a list of JdBezierPoint definitions and constructs a smooth bezier path from options passed into it.
Notable Points
There are several interesting points about PathMove.
Rather than directly construct a UIBezierPath object, PathMove constructs an array of JdBezierPoint objects, each of which describes a location along the desired bezier path, including the main and control points. This allows you to do nifty things like drawing the locations of the control points on the display, rather than just the actual bezier path. Before I did this I had a hell of a time trying to visualize the bezier path and how changes to the control points affected it. Once I could see the control points on the screen it became so much simpler to say “yep .. just move that control point by X degree and extend it by a little bit more. Short of having a drawing package that allows dragging and dropping control points, this is one of my favorite features of PathMove.
The animations are built up from a combination of CAAffineTransforms and CAKeyframeAnimation animation. Figuring out this combination of transforms and animations gave me the biggest headache and I only really nailed it after I got answers to my StackOverflow questions.
Within the animations, the biggest issue I ran into was the “Rotate the object with the path” option. In the code this is enabled by setting the rotationMode property on the CAKeyframeAnimation object. In Apples documentation for this property CAKeyframeAnimation Class Reference: rotationMode they helpfully say:
Determines whether objects animating along the path rotate to match the path tangent.
You’d think that setting this would mean that the object in question keep its orientation relative to the tangent of the path. Well it does .. sort of. What Apple should have said is:
Determines whether objects animating along the path rotate their horizontal axis to match the path tangent, and that once animation starts the object will be automatically rotated so that its horizontal axis is tangential to the path.
Thus if you want an axis other than the objects horizontal axis to be tangential to the path, you have to pre-rotate the object by the angle between the desired and horizontal axis. This is the reasoning behind the “Pre-Rotate object” option in the Setup screen.
Th’ Th’ That’s all folks
Hope you enjoy this program.
Peter