Calculating sunrise and sunset hours in Swift
After two days of struggle I came up with my application screen showing this :)
There’s sunrise time in the first label and sunset time in the second. At least now I can see the pros of having emoji here :). Calculating these two is crucial for my Magic Hours app.
So, what is needed in order to calculate the sunset / sunrise hour of a location on a specific date:
- latitude and longitude of the location,
- time zone of the location.
Getting proper time zone for a location is a little bit more tricky. When I was working on my magic hours windows phone project, I couldn’t find any third party library that would allow to get the information based on latitude and longitude. I ended up generating requests to geonames.org, which was not an ideal solution, as it required the app to be online.
Yesterday I found this github project: LatLongToTimezone, which is exactly what I want :) There was only one single file that needed to be included in my project. Unfortunately, the file was also large in size and Xcode was unable to show it in editor: never ending waiting wheel :) The solution was to split the file in smaller files, which fortunately someone already did in split-up-timezone-mapper github project.
Ok, so I have geographical coordinates and time zone, so it’s time to do some math and write some code. In my windows phone project I did use sun equations presented on cybermoon.pl. They did the job :)
My class SunInfo takes latitude, longitude and date parameter in its constructor. Sunrise and sunset hours are calculated in the Coordinated Universal Time – UTC, that’s why the time zone of a location is so important. At the end I’m adding time zone offset to calculated values in order to display sunrise and sunset in local time. I was pleasantly surprised to discover, that TimeZone struct is included in Swift’s Foundation api. I used some third party library in my windows phone project.
Dates in Swift
At the beginning I couldn’t figure out how to deal with date in Swift. The approach is slightly different from what I know from c# and I stuck on simplest things like: how to get the hour and minute or the date part from the date object ;) Apparently a Date object only holds reference to seconds from 1970 and is independent of a particular calendar or time zone. To represent a Date as a structure of year, month and day, it has to be interpreted it in the context of a Calendar.
This code snippet gets current hour and minute in Swift:
let date = Date(); let calendar = Calendar.current; let hour = calendar.component(.hour, from: date); let minute = calendar.component(.minute, from: date);
Swift lacks % opearator
The next surprise is that there’s no modulo, %, operator in Swift.
In most programming languages the % operator computes the remainder after dividing its first operand by its second. For example in c# I write a line of code like this:
bool isEven = a % b == 0;
and in Swift:
let isEven = a.truncatingRemainder(dividingBy: b) == 0;
Swift’s syntax looks in this case just to verbose to me :) I’ve came across a statement saying that functions in Swift should be read as sentences. Come on, we’re writing code here and not a novel ;) And talking about complexity..
Xcode likes to keep things simple
Look at the error line: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions.
I’m not quite sure if this was just a hint or a real problem preventing the code from compiling, as there were other issues I had to fix. In the end I did break the expression into two statements :P
And finally, a piece of good advice from me:
Think twice about the names you’re going to use in your Swift code…
because Xcode can’t refactor Swift code.
It looks like a feature Apple didn’t have time to implement. It’s like: sure we’ll do, it’s on our roadmap, but you know, there’s whole bunch of issues we have to deal with right now, and you know, you are welcome to use find and replace at any moment.