22 Feb 2015
Lots more tweaks of the UI, in particular making a better Settings page.
It’s a pretty straightforward static table view, but there were a few things I learnt/remembered along the way that are worth sharing here.
Selectors in Swift
Installing the app on my iPad exposed a bug I’d introduced in a selector - just shows you can’t always believe everything you read on StackOverflow!
Setting up the handler for incoming notifications, you need to add a colon to the end of the method name in the selector e.g.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateKVStoreItems:", name: NSUbiquitousKeyValueStoreDidChangeExternallyNotification, object: store)
Also on the function being selected, you need to annotate the function with @objc
so it can correctly interact with the Objective-C library code calling it
Custom data entry for text
I wanted a simple way of entering dates, as I stated in an earlier post simply adding a DatePicker to the page is pretty ugly and takes up a lot of screen.
The solution is to show the dates in a textbox, but then setting the inputView of the text box to be a DatePicker.
// Setup the date pickers as editors for text fields
self.textStart.inputView = self.startDatePicker
Obviously you have to take care in calling self.view.endEditing(true)
as appropriate when other elements are selected, but it’s a neat solution that works well.
Setting the build version automatically
I can’t remember where I found this originally, but I’ve used it in projects before. Basically I want to set the build number automatically based on the Github revision number, so I can call it in the code.
The trick is to add this script as part of the “Run Script” you can set in a target’s Build Phases
git=`sh /etc/profile; which git`
appBuild=`$git rev-list --all |wc -l`
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $appBuild" "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
echo "Updated ${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
You can then use this to add a version number to your display:
// Add version number
let infoDictionary = NSBundle.mainBundle();
let version = infoDictionary.objectForInfoDictionaryKey("CFBundleShortVersionString") as! String;
let build = infoDictionary.objectForInfoDictionaryKey("CFBundleVersion") as! String;
self.labelVersion.text = String(format: "v%@.%@", version, build);
Having a version number in the settings can be invaluable when trying to debug customer issues.
Next steps
Add some quality and animations to the counter control
Previous posts in the series
The code for the project is also available on GitHub
22 Feb 2015
It’s really nice to see Twitter starting to open up to developers again after a long period of wanting to keep everything closed.
The Fabric mobile tools look very interesting and I may test them out soon,
but right now I thought I’d have a go at adding Twitter Cards support to my various blogs.
In theory it’s as simple as this (according to their web site):
- Choose a card type you want to implement.
- Add the pertinent meta tags to your page.
- Run your URL against the validator tool to be approved.
- After approval, tweet the URL and see the Card appear below your tweet.
- Use Twitter Card analytics to measure your results.
This is the first post I’ll tweet when the new meta-tags have been added, so the post may take a couple of goes to get working. I’ve been approved (apply via the validation tool) and it all looks good, so fingers crossed!
Update - It worked!
Not quite as I expected - on the Twitter web site there was a “View Summary” link rather than it being automatically inline, but it looks quite good:
In the iOS app you get a little card icon next to the time posted (which I’ve not noticed before but is actually on a few tweets now I see it), and you get the full experience if you click on the tweet. Nice :)
19 Feb 2015
I wanted a nice graphical way of showing the progress of the days counted, so I had to make a custom control.
Luckily a couple of days ago I found a great article on how to do almost exactly what I needed.
After a few tweaks from the article code to expose some hard-coded values as properties, it didn’t take long to get something up and running. I’m not going to repeat the article details here, as it’s very clear.
The easiest way of sharing code between the main app and the today widget is to put the shared code in a separate library, which can then be linked to by both targets.
The main view of the app is now much better looking…
I also wanted to add the control to the today widget, which looks a lot better now too …
Next steps
Improve the UI of the settings view, and overall navigation between the views
Previous posts in the series
The code for the project is also available on GitHub
18 Feb 2015
Making a widget to show on the Today notification screen is pretty straightforward, and as I’d done it before
it was pretty quick to do this.
However there are a few steps that must be done correctly for this to work, so it’s probably useful for me to document them here so I remember quicker next time.
Putting shared code in an embedded library
The easiest way of sharing code between the main app and the today widget is to put the shared code in a separate library, which can then be linked to by both targets.
Adding a new target to your project via the “Editor->Add Target…” is the simplest way, and you need to select an “iOS/Framework & Library/Cocoa Touch Framework” template. I tried choosing the static library type, but that only offered me an Objective-C project type which didn’t seem correct even though the data code I’m sharing uses the Foundation code and is non-UI.
After adding the new target, it’s then easy to move the existing files - for me the data model code that’ll be shared in both the app and the widget - into the new target by making sure the files are references in the “Compile Sources” section on the “Build Phases” tab of the target properties (and obviously removing from the original target)
Finally, you’ll need to check the “Link Binary with Libraries” sections for all the targets reference the new library as appropriate, and then the Swift code has the appropriate import daysleftlibrary
code to reference the library code
Setting the app group for the user settings
To allow two different targets access to the same user settings, they need to reference the same App Group where the preferences are hold.
Once that’s done, you also need to reference the group name as the suiteName parameter in the constructor of your NSDefaults object
By default, the title of the widget in the Today screen will take the target name, which for me was the ugly “dayslefttoday”. To be able to override this you can set the “Bundle display name” target property to the required string
You can see the results of this stunning widget below:
Next steps
Undecided yet whether to have a play with the WatchKit, or with some animations in the main app. Stay tuned :)
Previous posts in the series
The code for the project is also available on GitHub
18 Feb 2015
Now we have the data model code ready, we can start building out the UI for the app.
Basic UI
Working in storyboards, there is obviously very little difference doing this with Swift ViewController code rather than
Objective-C, especially with such a simple UI.
As you can hopefully see from the screenshot of the storyboard, there are just 2 views in the app - one to show the number of days left, and a settings view where we can edit the values in the data model.
Later on we’ll come back to this to make the views look a bit more polished, but I just wanted get the app up and running first before tackling some more slightly interesting problems.
A few things learnt
There’s not a lot to say about the Swift code here as it’s pretty simple. However there were a few things that puzzled me a little during development that I thought I’d capture here.
I didn’t know the DatePicker control has a fixed height, which made it very confusing at first as my initial attempts as restricting the height using AutoLayout constraints just didn’t work. There are ways you can change this using frame size manipulation but I wasn’t happy with doing that in my first pass. I’ll revisit the layout of the settings page later.
Also I’d forgotten how to handle hiding the keyboard when the focus goes off a text field (or when the user presses return on the keyboard). As I’m sure you know the secret is to fire a self.view.endEditing(true)
in a touchesBegan
event handler, and also implement the UITextFieldDelegate and add the endEditing call to a func textFieldShouldReturn(textField: UITextField) -> Bool
method as well
Next steps
Add a widget to Today notification screen, which will involve a little refactoring of the code to make common elements shareable
Previous posts in the series
The code for the project is also available on GitHub