Checking for missing translations in iOS projects

When you work on an iOS app localized into multiple languages one of the biggest challenges is making sure that everything is translated, no string is missing in any language. Xcode does not provide any tool to make this easier for you, but there are some 3rd party tools that you can integrate into your workflow.

verify-string-files

I have recently found a quite old project called verify-string-files. It is a command line tool take takes you base localization file and compares it to all the translations, informing you about missing strings. Altough the last commit to this project was way back in 2014 it still works reliably, there have not been any changes to the way iOS does localization.

The best thing about this tool is that you can integrate it to your build process.

Build process integration

The tool is available only as source code, so you have to build it first. I put the built binary to a support folder of my projects.

Then I have a Build-Phases folder with all the script used in the build process. I think it is a much better solution than embedding the script right into the Xcode project.

To integrate verify-string-files you just need a really simple script, providing the path to the base localization file

${PROJECT_DIR}/support/verify-string-files -master ${SRCROOT}/iOSSampleApp/Resources/Base.lproj/Localizable.strings

In Xcode, just add a new Run scrip phase and call $SRCROOT/Build-Phases/check-strings where check-strings is the script name. Just do not forget to chmod +x the actual file.

This will result in build errors when there are missing string, shown directly in the base localization file

[Read More]
swift  ios  xcode 

Automating your iOS app development and distribution workflow

I am a big fan of automation in software development so when I started doing iOS development one of my goals was to automate everything on the iOS project.

No more manual versioning, manual build distribution… let the computers do the work for me. Here is the setup I ended up with.

Separate app ids for development and distribution

Let’s say your app id is com.company.app. You can use that app id when developing the app, running it in the simulator or on your device and also when distribution the app using services like TestFlight or uploading it to the AppStore.

With just one app id you cannot have both the AppStore version and a development version installed on your device. This a problem when working on an app that you use daily for communication with your colleagues, but you are working on a feature that requires you to use a different server, or some MDM, etc.

There is also another problem you will encounter if you use push notifications. When you create a push notification certificate for production, push notifications for your app will work with AppStore builds, with ad-hoc builds in TestFlight and other services but you will not get any notifications when running the app on your device deployed from Xcode. At least not easily.

To counter that, I use separate app ids

  • com.company.app with push notifications set to production for AppStore and ad-hoc distribution
  • com.company.app.dev with push notifications set to sandbox for development

This of course requires your backend to support it by choosing the right push notifications certificate depending on the app id.

With this setup, I can

  • use the AppStore version and the development version of the app on my device at the same time
  • have push notifications working also in the development version of the app

Automatic builds and tests with Gitlab

The iOS project I work on uses self-hosted Gitlab instance, so using Gitlab CI was the obvious choice. You just need a machine with macOS that is always online, install the Gitlab runner, connect it to the Gitlab instance and you are done. Everything else is just a matter of configuration in a file in your repository.

[Read More]
swift  ios  xcode 

Creating and using your own Xcode file templates

Working on an iOS or macOS project in Xcode you typically create classes with the same structure over and over again.

I use coordinators so I am creating new UIViewControllers, each time referencing RxSwift, having methods for setting up UI, bindings .. most of the time also containing a delegate for the coordinator.

Having to create files with the same structure over and over again manually is a waste of time, a much better solution is creating Xcode file templates for those files.

Xcode file templates

File template location

All the Xcode custom file templates are located in ~/Library/Developer/Xcode/Templates/File Templates and grouped into sections by their folder name. If you want Xcode to show a “Custom” section at the bottom of the new file dialog, just create a ~/Library/Developer/Xcode/Templates/File Templates/Custom folder.

File template structure

Each file template is a separate folder with a name ending in .xctemplate. If you want to create a simple “Swift Class” file template, you have to create a folder named Swift Class.xctemplate in ~/Library/Developer/Xcode/Templates/File Templates/Custom.

Each file template folder should contain at least 3 files:

  • TemplateInfo.plist - describing the template
  • TemplateIcon.png - icon shown in the Xcode new file dialog
  • ___FILEBASENAME___.swift - the actual template file
[Read More]
swift  ios  xcode 

Using iOS strings in a safer way

When developing any application it is a good practice not to hard-code your strings but to use some kind of a strings file. In iOS you typically use the standard Localizable.strings file as storage and some string based API to use those strings, like

extension String {
    var localized: String {
        return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
    }
}

This of course works but it is not exactly “safe”, if you make a typo the compiler has no way to warn you and you, or worse your customers, will find out at runtime. There is a better way.

SwiftGen is a Swift code generator that will help you with that. It can generate enums for your strings, assets, storyboards. With a simple configuration SwiftGen reads your Localizable.strings file and generates a L10n enum with all the strings

internal enum L10n {

  /// Search colleagues by name or surname
  internal static let enterpriseDirectorySearchInfo = L10n.tr("Localizable", "enterprise_directory_search_info")
   /// Copyright © Igor Kulman\nAll rights reserved.\n\nVersion %@
  internal static func welcometxt(_ p1: String) -> String {
    return L10n.tr("Localizable", "welcometxt", p1)
  }
  ...
}

Simple strings are generated as properties and strings with formatting parameters as functions, so you always known how many parameters to use. It also makes it easier to find the correct string by showing the strings in Xcode intellisense

If you want a more complete example, take a look at my iOS sample app on Github

swift  ios 

Workaround for UINavigationBar button remaining faded after back navigation

The iOS 11 has many bugs, more are introduced with every update. I only just recently discovered a bug in the registration part of the application I work on.

The registration flow contains a few screens to gather the user data. The navigation among those screens (managed by a coordinator) is done by Back and Next buttons in the UINavigationBar. The users can at any time get back to the previous screen, and if they are running iOS 11.2 they will see the bug:

The users tap the Next button to go to the next screen and when they get back, the Next button is faded. It works, can be tapped, but does not look right. This only happens on iOS 11.2.

[Read More]
swift  ios