👨‍💻 Your first awesome Swift Script, Part-1

Omar Labib
3 min readJan 11, 2023

Automate your boring localization stuff with a handy Swift script 🔥.

As an iOS engineer, I often find myself doing some repetitive tedious tasks every now and then, who doesn’t. When thinking about automating these tasks I used to tackle them developing some bash utilities and scripts. But that wasn’t the best experience for me, bash is ready super powerful but kind of obsolete!.

Why Not developing these scripts using the language we talk pretty well? Luckily we CAN, and easily!!

So here I’m tackling a tedious problem for iOS engineers and will try to develop a script in Swift to solve it.

Problem statement

Managing localization keys in projects supporting multiple languages can be a very tedious. Last project I was working at, it was localized for 12 languages so you can imaging the effort to insert, remove or delete a key in this project. This simple task was kind of couple-of-minutes task.

Solution

We will be building a Swift script that does the following.

  1. Asks you for the project directory.
  2. Finds all the localizable files in the directory.
  3. Then you will tell it what are you trying to do? Add new key-value pair, or modifying the values for a particular key, or delete the values for a a key.

Let’s see who we can build this awesome script. We will be building this as a swift package

Let’s roll up our sleeves, Open the terminal and type the following..

$ mkdir Localize
$ cd Localize
$ swift package init --type executable

Here you’re making a new directory, navigate to it, and initialize a new package at this directory.

For reusability, this script will receive the project directory in its arguments when it’s being run so that you can use it with many projects.

Let’s first get this directory from the command line arguments.

enum Utils {
static var projectDirectory: String? {
CommandLine.arguments.count <= 1 ? nil : CommandLine.arguments[1]
}
}

We’re gonna need some other utility functions as well, so I will add the Utils enum and add all the other utility functions as static on that enum.

We will then need a utility function to get the localizable files in the directory by looping over all the files in the root directory and filter files with Localizable.strings suffix.

enum Utils {
static var localizableFiles: [String] {
guard
let rootDir = Utils.projectDirectory,
let files = FileManager.default.enumerator(atPath: rootDir)?.allObjects.compactMap({ $0 as? String })
else {
return []
}

return files.filter({$0.hasSuffix("/Localizable.strings") })
}
}

This function will return an array of strings of the Localizable.strings, as relative paths to the root project directory.

Finally we will add two utility functions to read the content of a file at a url, and write to a file at a url.

enum Utils {
static func contentOf(file relativeFilePath: String) -> FileContent {
guard let rootDir = projectDirectory else { return [] }

let filePath = "file:///\(rootDir)/\(relativeFilePath)"
guard
let url = URL(string: filePath),
let fileContent = try? String(contentsOf: url, encoding: .utf8)
else {
return []
}

return fileContent.trimmingCharacters(in: .newlines).components(separatedBy: "\n")
}
}

This function will take the relative path of a file -to the root directory, access this file to read its content and return an array of strings, each item in that array represent a line in the file.

And to write to a file, we will add this function..

enum Utils {
static func write(content: String, to relativeFilePath: String) -> Bool {
guard let rootDir = projectDirectory else { return false }

let filePath = "file:///\(rootDir)/\(relativeFilePath)"

guard
let url = URL(string: filePath)
else {
return false
}
do {
try content.write(to: url, atomically: true, encoding: .utf8)
return true
} catch {
return false
}
}
}

This function will take the content we want to write to the file, and the relative path to that file. It returns true if write has been a success to the file otherwise false.

So far we have developed our kit, and we’re able to:

  1. Get the root directory from the arguments.
  2. Extract the localizable files existing at that directory.
  3. Read the content of a file knowing it’s path.
  4. Writing to a file at a path.

In part 2 of this tutorial, we will be using this kit to do the the really awesome work to manage our localization files.

--

--