Track fitness goals with Apple Watch

Run 80 miles in a month; do 2 000 000 steps in a year…

It doesn’t matter if you run, bike, or walk, fitness goals are indisputably beneficial for many reasons. They keep you on track towards your long-term plans whether it’s losing weight or running a marathon (or both). Since you clicked on this article, I will assume you track your workouts with your Apple Watch, and I will show you a few ways to track the fitness goals that will hopefully help you on your fitness journey.

Health app

A great starting point is the Health app you already have on your iPhone. There are many trends and statistics available in the Summary tab. If you set yourself a specific goal (for example the step count), you will need to open the data type detail and set the desired time period in the top selection bar. For example, to show daily averages by months select “Years” in the top selection. You can then click on each chart column to show the exact value.

Apple Health Example

The Health app is a simple solution that works out of the box, but it has some limitations as you can see. For example, if you’d like to view the above-shown chart in a total step count in months (not daily averages), you cannot do that. Another good example is the ran distance because the Health app merges run and walked distances, thus making it impossible to track how many miles you tracked in a running workout this month.

HealthExport Remote

I was frustrated with the Health app limitations and lack of any customizability. I think everybody should have the ability to view fitness data in a way that will be most useful for them. We are all different, so naturally, we want to see fitness data from a different point of view, thus a certain level of customizability is necessary. It’s great if you are happy with the Health app and there is nothing wrong with it, but I wanted to build something for people for whom the functions of this app were not enough.

HealthExport started as a simple iPhone app helping users to export Health data into CSV files. The app features expanded as time went on. A year after the initial release I launched HealthExport Remote service that lets users access fitness data from a browser. The website gives users the option to create customizable dashboards, which is the primary way I track my fitness goals nowadays.

Let’s go back to the example we talked about. My goal is to run 100 kilometres in a month.

HealthExport Remote Goal Tracking Dashboard Example

I created a “Running” dashboard with four charts. The two charts in the first row are gauges showing my goal progress this and the previous month. Then there is a bar chart and a table visualizing monthly sums of ran distance in this year. The best thing is that you can tailor the dashboard exactly to your needs and you can have as many of them as you want. There are also other sections on the website like Workout and Nutrition overviews.

I could talk about dashboard configurations for hours, but the strength of the service is its customizability and you are the only one who knows what charts and layouts will be most useful to you. There is a month free trial so you can try the service and cancel it if it won’t meet your expectations. I built HealthExport as a service for me and other fitness enthusiasts, so I will be more than happy if you reach out to me with any feedback or feature suggestions.

I hope this article helped you on your fitness journey and I wish you all the luck in meeting and execting your goals.

Push notifications beyond the basics

Push notifications are a great way to communicate with your users. They let you inform the user about what’s going on. Another good usecase is to remind potentially lost users about your app (a user who didn’t use the app in a while).

In this article, I’m gonna assume you know the basics of push notifications. If you’ve never implemented them yet, check out for example the Firebase Cloud Messaging getting started guide.


Today I want to talk about improving the user experience of push notifications. Sure, the basics work, but I think we can do better. One way to improve the UX is to think about these two cases:

  • The user receives a notification while the app is in the foreground
  • The user launches your app and has an active notification

Usually, we attach notification actions only to opening the notification, but that doesn’t have to be that way.

I recently thought about this for EventBattle - a scavenger hunt mobile game. We want to ask our users for feedback once they played for a while and we use push notifications for that. Let’s think about the two cases listed above. If the app is in the foreground, we can show the feedback dialog right away and there is no need to show the notification. If the user launches the app and has an active feedback notification, it would be nice to show the feedback dialog and cancel the active notification.

EventBattle push notifications flow diagram

Receive the notification while the app is in the foreground

UNUserNotificationCenterDelegate has function userNotificationCenter(_:willPresent:withCompletionHandler:), where you decide how to handle incoming notification while in foreground. In our case, we want to check the notification type and also verify the applicationState just to be sure we’re in the foreground.

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        switch (notification.notificationType, UIApplication.shared.applicationState) {
        case (.feedback, .active):
            // Handle the notification here
            completionHandler([])
        default:
            completionHandler([.alert, .sound])
        }
    }

App launch with an active notification

We can use the getDeliveredNotifications function on an UNUserNotificationCenter to get active notifications, ie. notifications currently displayed to the user. Thus it’s simple to check if there is some active notification we are looking for.

extension UNUserNotificationCenter {
    func activeFeedbackNotificationExists(completionHandler: @escaping (Bool) -> Void) {
        getDeliveredNotifications { notifications in
            let activeFeedbackNotificationExists = notifications.contains(where: {
                $0.notificationType == .feedback
            })
            DispatchQueue.main.async {
                completionHandler(activeFeedbackNotificationExists)
            }
        }
    }
}

To cancel the active notification we need to get its identifier. We get it from the same getDeliveredNotifications function we used in the last example. Once we have the identifier, we can cancel the notification via removeDeliveredNotifications(withIdentifiers:).

extension UNUserNotificationCenter {
    func cancelActiveFeedbackNotifications() {
        getDeliveredNotifications { notifications in
            notifications
                .filter {
                    $0.notificationType == .feedback
                }
                .forEach { notification in
                    self.removeDeliveredNotifications(withIdentifiers: [notification.request.identifier])
                }
        }
    }
}

I hope I gave you a new perspective about push notification’s UX. It’s always good to think about outside the box. Yes, you can attach the action to click on the notification, but maybe other ways will make the application more pleasant for your users.

Learning resources for iOS developers

The software development field is changing rapidly. Apple announces a lot of changes at their yearly WWDC conference. Recently, Apple started to release new features even during the year (like the mouse pointer support introduced last Spring). Besides the iOS SDK, there are a lot of best practices, frameworks, architectures, etc. that don’t have any update schedule. Until now I’ve talked only about keeping up with changes, but nobody knows everything so we should also strive to learn new things. All in all, learning is an essential part of a developer’s life. Today, I want to share my tips for learning resources.

Apple Developer app

https://apps.apple.com/us/app/apple-developer/id640199958

Apple introduced the Developer app back in 2019 and it has been a good resource for me ever since. You can find all the WWDC sessions inside the app, with search, transcripts, bookmarking, and other nice features. In addition to the dub dub videos, Apple posts interesting articles inside the app. It’s free to get the app from AppStore, so I encourage everyone to download it and give it a shot.

Swift by Sundell

https://swiftbysundell.com

John has been writing articles about iOS development for ages. I’m sure you have visited his website already through a link in Google search results. John divides articles into categories and there is also a search so you can always find what you are looking for. If you want to learn something new but don’t know what, check out the Discover section.

Hacking with Swift

https://www.hackingwithswift.com

Hacking with Swift is similar to Swift by Sundell. Paul is a good teacher with many years of experience. You can find pretty much everything on the website. Paul is also known for quickly publishing articles about just released new iOS SDK changes (like within a few hours). There is also Hacking with Swift YouTube channel you can check out as well.

Standford CS193p

https://cs193p.sites.stanford.edu

Standford CS193p is a course recording from Standford university as the name suggests. It’s completely free to watch it. The course is aimed at beginners, so this is the resource I recommend to everyone who wants to start with iOS development. The course has been around for ages and, actually, I learned the basics of iOS development with this course years ago. Don’t worry, they publish a new version of the course every year, so it’s not outdated.

Podcasts

My favorite way of learning new things nowadays are podcasts. I like that I can listen to them while doing other things such as driving or working out. When I hear something interesting, I bookmark it with the Airr podcast app so I can look up more information about it later on a computer. My favorite podcasts about iOS development are:

  • Under the Radar by Marco Arment and David Smith
  • Stacktrace by John Sundell and Gui Rambo
  • Swift by Sundell by John Sundell
  • AppForce 1 by Jeroen Leenarts

I hope you have discovered a new learning resource while reading this article. There are a lot of resources and I know I’ve missed some good ones so take this list just as a starting point. I always like to chat with people on Twitter, so feel free to reach out anytime if you have a recommendation for a learning resource, feedback about my article, or just want to chat in general.

How I add GPS coordinates to DSLR photos automatically

Most professional cameras nowadays still don’t have a build in GPS module. When you are photographing a wedding, you probably don’t need GPS coordinates. On the other hand, one of my hobbies is traveling. I like to take photos during my trips and I’d like to view them later on a map. But my Sony A6500, unfortunately, doesn’t have a GPS module.

Recently I’ve been looking for a solution to this problem and it turns out to be quite simple. There are tools that can add GPS coordinates to photos based on a GPX file. Let’s look at how to do it.

Map with photos

GPX file is a file containing a series of GPS coordinates. There are many GPX recorder apps available on AppStore and Google Play. In my case, I track my outdoor photoshoots with my Apple Watch as outdoor walks, so the location data is already being tracked. To generate the GPX file, I use one of my iOS apps - HealthExport app.

When you have the GPX file, you need a tool that can associate images with GPS coordinates in the GPX file. I use exiftool, which is available for free at https://exiftool.org/. After you install exiftool, you can open the Terminal app on your mac (command line on Windows) and type following command:

exiftool -geotag "{path to your GPX file}" "{path to your folder with photos}"

If you have any feedback or question, feel free to reach out via Twitter.

Format dates the right way in Swift, iOS

How to convert Date into a human readable string? Most iOS apps perform date formatting, but many of them don’t do it properly. Let’s look at a few ways how to convert Date into String in Swift.

If you type “How to format date in Swift?” into Google, the first result is a StackOverflow answer with DateFormatter.

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return dateFormatter.string(from: date)

The code above works, but there are two main problems with it - “2021-01-01 00:00:00” isn’t an easily readable date format for humans and it’s not localized into the user’s locale. There are usecases were you want to use fixed dateFormat such as when encoding data for an external API, but you shouldn’t use it for texts you display to the user.

Better way to use DateFormatter would be:

let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .medium
return dateFormatter.string(from: date)

When you use the dateStyle and timeStyle, the output is automatically formatted to the phone’s locale (for example “April 13, 2021 at 1:55:30 PM” in US). If you want to omit the date or time part, you can just set its style to .none.

Sometimes you want to display formatted date in relation to another date. A good example is a feed of activity, where “3 minutes ago” looks much better than “April 13, 2021 at 1:55 PM”. Fortunately, since iOS 13 we have the RelativeDateTimeFormatter, so relative date formatting can be done in a few lines of code. It’s recommended to specify formattingContext (beginningOfSentence, middleOfSentence, standalone, …) for proper formatting in all locales.

let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
formatter.formattingContext = .standalone
return formatter.localizedString(for: self, relativeTo: Date())

Let’s say you have a workout with start and end dates. You can use format both dates with DateFormatter , but there is a better way - DateIntervalFormatter allows you to format time interval (two Date values).

let dateFormatter = DateIntervalFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .short
return dateFormatter.string(from: workoutStart, to: workoutEnd)

DateIntervalFormatter output may look like “April 13, 2021, 6:01 AM – 2:11 PM”. Similarly to the DateFormatter, you can specify desierd length by setting the dateStyle and timeStyle and you don’t have to worry about localizing the format to user’s locale.

Propper date formatting significantly improves the user experience and it is a matter of a few code lines. If you have any feedback or question, feel free to reach out via Twitter.