25 Nov 2015
We found an “interesting” issue at work yesterday regarding IE, cookies and domain
names that I thought might be useful to share.
At FindMyPast - where I’m currently contracting - we heavily
use GitHub Flow which involves multiple development branches
being worked on at once.
By convention we use underscores in our branch names e.g. “dev_feature_branch”, and
have recently automated hosting of feature branches for test purposes. For example
for the above branch we’ll setup a host for dev_feature_branch.findmypast.com
For a feature I was working on, everything worked great on Chrome and Firefox, but for
some reason we couldn’t login properly using IE.
After much head-scratching, we fired up Fiddler which gave us the
answer.
!! WARNING !!: Server hostname contains an underscore and this response sets a cookie.
Internet Explorer does not permit cookies to be set on hostnames containing underscores.
See http://support.microsoft.com/kb/316112
The obvious solution is not to use underscores in the domain name used for the virtual host,
either by convention of stripping them out when setting everything up.
Hopefully if you see a similar issue, this may help you debug it quicker than we did!
21 Oct 2015
Unfortunately there was a bug in v2.0.0 of Count The Days Left where the watch
complication wasn’t reliably updating itself.
Fortunately the issue was interesting enough to blog about :)
What I believe was the issue - not 100% sure but it appears fixed now - is that a watchOS 2 app only has a limited budget
of time where it’s allowed to update itself.
In the getNextRequestedUpdateDateWithHandler
function in my complication data source class, I was telling the watch
to update once an hour. This seemed a reasonable compromise (without thinking much!) where the watch would update
itself once an hour automatically, even if the options for the countdown didn’t change - which forces a complication refresh.
However I believe that once an hour is too frequent. If you read the Apple Documentation
closely, it says:
At the end of a scheduled update, ClockKit calls the getNextRequestedUpdateDateWithHandler: method to get the time for the next scheduled update. Specify a date as far into the future as you can manage. Do not ask the system to update your complication within minutes. Instead, provide data to last for many hours or for an entire day. If your budget is exhausted, the next scheduled update does not occur until after your budget is replenished.
Now obviously my count only updates once a day, so I can return in getNextRequestedUpdateDateWithHandler
to next update
itself at the start of tomorrow, which means it will only automatically update once a day (but should be correct all that day!)
It appears to have worked on my watch overnight, so I’m sending a v2.0.1 to the app store today (Wed 21 Oct 2015). The last version
made it past review and into the store in about 2 hours(!), but it’s unlikely to be that quick again.
Update (2015-10-29)
Turns out it was more complicated than I thought :)
Even after the fixes above the complications still intermoittently failed to update overnight. I just think the “automatic updating”
is just a bit flaky.
Anyway, I’ve much improved things by putting in an additional call to update watch complication every time the iPhone app,
today widget or watch app is access. I also added a background app refresh handler in the iPhone app to call the same update
method when ever it is intermittently called.
All of these calls shouldn’t be too frequent - so should have a neglible effect on battery life I hope - and guarantee
as much as I can the complication is up to date.
Another version has been submitted to the app store, and hopefully this one will fix the issue.
N.B. All code can be seen on GitHub
17 Oct 2015
All done! A final few tweaks and v2.0.0 of Count The Days Left has been submitted to the app store for review.
Things I learnt
Developing for the watch is fiddly
watchOS 2 is a pretty big change from the first version, with the extension code now running on the watch. In theory this should
help the performance of watch apps - and I believe it does - but some thought needs to be put into to how you manage your
data flow between the phone and the watch to optimise this.
I also struggled for a while understanding the best workflow for testing watch apps, using a combination of the Xcode
simulators and the watch itself, but definitely learnt a lot along the way what works best.
Is Swift ready for prime time?
I really like Swift as a language compare to Objective-C (although now I’m used to the crazy square bracket syntax I don’t mind
the latter any more), but I don’t feel as though Xcode is quite as reliable when building a Swift project compared to an
Objective-C one.
Sometimes the dependency detection on what changed seemed a bit flaky, or the compiler would say a setting is missing from
an Info.plist file when it was actually there (and a recompilation “fixed” the issue).
None of the problems are major, and won’t put me off starting a new project using Swift. However I’m hoping things get
fixed in later versions of both Xcode and Swift.
What next?
Right now I think I’ve reached the end of features I can add to the app, at least until iOS10/watchOS3 come out and offer some
new things to play with.
However I’m always open to suggestions, so drop me a line at johnp@bravelocation.com or
reach out on Mastodon to @johnp@bravelocation.com if you have any thoughts.
v2.0 Articles
N.B. All code can be seen on GitHub
15 Oct 2015
So I decided against adding a glance to the app as promised last time, as
it doesn’t really add much value over the app itself, and to be honest I found it quicker to go straight to the app
rather than try to scroll through my glances.
Anyhoo, I’ve had a load of fun trying to get the complications working, and after more to-ing and fro-ing finally have it working.
This should have been easy, especially as the built in templates for the smaller complications already have the “progress ring”
that matches almost exactly what I want.
Apple’s documentation
is OK if a little dry, and there are few helpful examples that can be found with a quick search.
The one key point I found was that the Xcode project settings where you set the data class to be used for the
complications data isn’t quite correct.
What you need to do is set the class in Info.plist to be:
$(PRODUCT_MODULE_NAME).ComplicationsDataSource
not:
ComplicationsDataSource
as the target\General\Complications Configuration would suggest.
So I think v2.0.0 is just about done! I’ll need to generate the App Store screen shots and text as usual, and then hopefully
it’ll be released in the next couple of weeks.
I’ll probably write a summary post soon too to summarise all these ramblings :)
N.B. All code can be seen on GitHub
11 Oct 2015
TL;DR
This is a fairly long story with a lot of false trails, so if you just want the best way of testing on the watch I’d skip to the end
The app never seems to start on the watch
In my last post I mentioned that although the app worked fine in the Watch simulator,
it was “slow” on the watch - in other words it never seemed to load at all.
I first assumed the problem was that the app initialization was taking too long, so I tried a few things including:
- Adding in-memory caching to the data model class to (maybe) improve subsequent reading of user settings post initialization
- Changing the data model to be held in the App/Watch delegate class so it’s only initialized once per app
- Moving the watch session connection code to run on a background thread so it didn’t block the UI thread loading the initial view controller
All of these changes made a difference in the load time on the simulator, but still no joy running on the watch :(
Pushing a TestFlight app to the Watch
I finally realised I must be missing something, so after doing a bit of research I found out that only signed versions
of a Watch app will run on the actual device.
This made a lot of sense, so I followed the further advice of pushing an archived version of the code to the App Store,
and then installing it on my phone (then watch) via a TestFlight install.
Now this actually worked, but it took me ages to actually get an acceptable archive working.
The problem was I was trying to reuse the code in both iOS and watchOS using a code library, and I just couldn’t get the archiving
to work and sign the various bits of code correctly.
This is crazy complicated (for me at least) and to be honest whenever I’ve used a dynamic library other that through pods I’ve
always had issues. Not sure if it’s me or it really is a complicated problem, but it seems the worst part of iOS development
compared to most other languages and platforms.
In the end I gave up with the library approach on the watch, and just added the shared code files to the Watch Extension target
directly which did the trick.
Solution: Add watch to provisoning profiles
Although the “TestFlight” solution worked, clearly it’s non-optimal as there’s a long iteration time
to test any changes on the actual watch.
Some more research found this excellent post
by Jared Sinclair on how to solve this problem by adding you watch to your provisoning profiles.
I won’t repeat Jared’s post here, and it’s sort of “obvious once you think about it” (which clearly I didn’t!), but I guess I did learn a lot
along the various false trails - which is the purpose of the exercise :)
Next Steps
- Add a glance view
- Add watch complications
- Ship it!
N.B. All code can be seen on GitHub