Welcome to the mac world

Wether you’re new to macOS or an user coming from different machines, the tools we tend to use is very important to be productive.

Here you’ll find my list and what I install on a fresh machine to get started.

System tools

First and foremost, I start with brew to handle all my packages, but instead of doing so manually, I use my dotfiles and my “magic” install script that does all the heavy lifting for me.

Keyboard tools

I use a TypeMatrix with a Colemak layout and also the internal keyboard of my laptop with its default layout. Colemak layout is available on a fresh macOS install but it is far from perfect as there are a lot of missing dead keys (to type accented letters mainly), so I start by installing the layout provided on this page: Colemak mac

To easily switch between the two and get almost the same feeling, I use Karabiner Elements.

Everyday tools

  • sdkman: to manage installation of various sdk (mainly Java based)
  • iTerm: nice terminal app with profiles
  • tmux: multi terminal in one window, switching terminal with a keystroke
  • zsh: Z-Shell
  • starship: fast shell prompt
  • ripgrep: faster grep
  • bat: nice cat alternative (with paging / highlighting)
  • exa: replacement for ls
  • Alfred: Spotlight with more features, this is my main app launcher / switcher
  • SetApp: App subscription service, use many tools from this (BetterTouchTool, iStat, BarTender…)

Now that iOS13 is available for more than year, we can start to set it as a lower bound for our deployments.

This allows us to play with all sugar that Apple put in it, one of the biggest thing is the arrival of SwiftUI to supercede UIKit (write cross platform UIs and so on).

Bridging the two worlds

Using SwiftUI from UIKit

The first thing we might try to do, is embedding a SwiftUI View inside our UIViewController based application. To do so, Apple gives us UIHostingViewController which is a simple bridging controller, straightforward to use.

struct MyNewView: View {
    var view: some View {
        Text("I'm in SwiftUI")
    }
}
class MyNewViewViewController: UIHostingViewController {
    init(){
        super.init(rootView: MyNewView())
    }
}

UIViewController in SwiftUI

However, at times we still need to reuse our good old UIViewController, either because we can not afford a full SwiftUI rewrite, so we want to keep old code and migrate pieces by pieces or because we are using something not yet adapted to SwiftUI.

In my case it was using the camera to scan a QRCode.

Non elegant solution

I faced a few issue with examples I found out, most of them are adding an extension to the UIViewController that makes it conform to UIViewControllerRepresentable.

class MyViewController: UIViewController{
    var cancellable: Cancellable?
    // classic stuff
}

extension MyViewController: UIViewControllerRepresentable {
    public typealias UIViewControllerType = MyViewController

    public func makeUIViewController(context _: UIViewControllerRepresentableContext<MyViewController>) -> UIViewControllerType {
        self // don't .init() please, class instance is already available
    }

    public func updateUIViewController(_: UIViewControllerType, context _: UIViewControllerRepresentableContext<MyViewController>) {}

    static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator _: Coordinator) {
        uiViewController.cancellable?.cancel()
    }
}

struct MyView: View {
  var body: some View {
    MyViewController()
  }
}

I find this not easy to read, as we’re doing weird thing by returning self from a function named makeUIViewController. Some example are telling to return MyViewController() instead of self. Please don’t do this otherwise you’re creating the UIViewController twice for each call !

I also had a leak when using Combine (more to come about this great framework), my Cancellables were never freed, leading in a memory cycle that kept the UIViewController living even though it was no longer presented.

It is important to do proper house keeping in the dismantleUIViewController method if you don’t want to use too much memory and slow down your app.

Nice looking way of doing

You will find a working example below, basically we need to implement a UIViewControllerRepresentable struct to represent be the container for our UIViewController in the SwiftUI world.

class MyViewController: UIViewController{
    var cancellable: Cancellable?
    // classic stuff
}

struct MyGreatView: UIViewControllerRepresentable {
    public typealias UIViewControllerType = MyViewController

    public func makeUIViewController(context _: UIViewControllerRepresentableContext<MyGreatView>) -> UIViewControllerType {
        MyViewController()
    }

    public func updateUIViewController(_: UIViewControllerType, context _: UIViewControllerRepresentableContext<MyGreatView>) {}

    static func dismantleUIViewController(_ uiViewController: UIViewControllerType, coordinator _: Coordinator) {
        uiViewController.cancellable?.cancel()
    }
}

struct MyView: View {
  var body: some View {
    MyGreatView()
  }
}

As I was working on an iOS project, I added unit tests to ensure things are not behaving badly (and will not).

During the process, a common pattern showed up and a few fields were required, I came up with the idea to basically create a BaseTest for my tests, so that everything is unified.

Base idea

I came up with something like the following for my tests

class BaseTest<T, Action>: XCTestCase {
    var subject: T!
    var actions: [Action]!

    override func setUp(){
        subject = T()
        actions = []
    }
}

class LoginTest: BaseTest<LoginMiddleware, LoginAction> {
    func test_loginIsWorking(){
        // ...
    }
}

It worked very well within XCode, I could run the tests by hitting the 🔹 in the gutter.

Bad things happen

The thing that I discovered later (thanks to the CI feedback), is that XCode was not properly discovering my tests as it should. At first I blamed the fact that my new tests were not at the top level of my sources folder (and the other ones were), but it was easy to check that this was not the problem at all.

Then, I blamed fastlane and thought that I’ve missed something in my test target configuration or something, but in fact, the problem was similar when using classical CMD + U key combo.

XCode was simply not discovering my test.

Workaround

Inheritance is often misused, in this case, I think it is relevant, but I applied classical way of working around this. I changed my BaseTest to a BaseHelper instead, to which the test delegates the calls.

With this, the test class properly inherits XCTestCase and is discovered as expected (even in subfolders).

class BaseHelper<T, Action>{
    var subject: T!
    var actions: [Action]!

    init() {
        subject = T()
        actions = []
    }
}

class LoginTest: XCTestCase {
    var helper: BaseHelper<LoginMiddleware, LoginAction>!

    override func setUp(){
        helper = .init()
    }
    func test_loginIsWorking(){
        // ...
    }
}

The nice thing in this solution is that the setUp call is no longer magical !

It’s been a long time

It’s been a very long time since last post (5 years…).

I am convinced that blogging is useful, at least for my present self, and for my future self that tends to lose track of important things.

What will this be about ?

In the past I blogged a lot about Java / Maven and so on, my days have evolved to another languages, so I guess there will be less JVM things (but may be a few Kotlin) and more Swift / Rust on the other end.

Technical stack

In a blog reboot, we (I mean dev) like to follow the latest trend and migrate our blog system to latest hype stack. I will avoid this and focus on writing instead, so the site stays in its 15’ shape:

The sole new thing is that I’m trying to write using gitpod.io to get a distraction free environment.

Il est courant maintenant d’utiliser Firebase dans les applications mobiles. Que ce soit pour la partie statistiques, les notifications ou le reporting des crashes. L’intégration est très facile, mais peut devenir compliquée dès lors qu’on manipule des target différentes avec des configurations différentes. Cet article va vous détailler une façon simple mais efficace de gérer ces différences.

Multiples fichiers plist

Les fichiers plist sont des fichiers XML de configuration pour XCode. Lors de la création d’un projet dans Firebase, il est possible de récupérer une version générée contenant les bonnes variables pour votre projet. Le problème qui peut se produire est lorsqu’on génère plusieurs livrables avec des configurations qui doivent être différentes grace aux target. Il n’est pas possible, ni confortable, de gérer le cas de plusieurs fichiers plist dans les sources. La redéfinition du nom du fichier à faire utiliser par Firebase ne marche pas à tous les coups et il a tendance à aller lire la valeur par défaut GoogleService-Info.plist.

L’idée est donc d’utiliser un script au build qui se chargera de configurer correctement le fichier plist qui sera inclu dans le livrable.

Première étape : variabilisation

Pour être capable de générer le fichier plist correctement, il est nécessaire de le variabiliser. Dans mon cas, j’ai identifié les valeurs suivantes qui devaient être variabilisées:

  • GOOGLE_APP_ID

  • BUNDLE_ID

  • CLIENT_ID

  • REVERSED_CLIENT_ID

J’ai donc ajouté des variables à la configuration de mon build pour représenter ces valeurs qui doivent être personnalisées. J’utilise des fichiers .xcconfig mais ceci fonctionne également avec l’ajout manuel (dans Build Settings > + > Add User-Defined settings)

Unresolved directive in #excerpt - include::site/static/lightbox.adoc[]

En plus de ça, j’ai modifié le fichier GoogleService-Info.plist téléchargé sur Firebase pour enlever les éléments qui allaient être remplacés à terme. Plus exactement, j’ai remplacé les valeurs par des chaînes du type WILL BE REPLACED AT BUILD TIME, qui me permettent facilement de me rendre compte d’un oubli de configuration.

Deuxième étape : génération du fichier

Une fois le fichier et l’environnement préparé, il ne reste qu’à générer la version finale. Pour se faire, je me base sur le système de build de XCode qui permet de définir simplement des étapes. Les variables définies dans la configuration du projet sont en effet disponibles simplement lors de l’exécution des étapes de build. En utilisant l’outil defaults inclu dans macOS pour la manipulation des fichiers plist, il est donc facile de définir les valeurs attendues dans le fichier. J’ai ainsi rajouté une étape shell script correspondant à ceci dans le processus de build.

defaults write "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" GOOGLE_APP_ID ${GOOGLE_APP_ID}
defaults write "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" BUNDLE_ID ${PKG_IDENTIFIER}
defaults write "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" CLIENT_ID ${CLIENT_ID}
defaults write "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" REVERSED_CLIENT_ID ${REVERSED_CLIENT_ID}

Enfin, il faut ordonner cette étape de build après celle de copie des ressources, pour effectuer la modification uniquement dans le fichier binaire de l’application et non dans les sources du projet.

Bénéfices

Il est donc facile de disposer de configuration différente de Firebase entre le debug et la release par exemple. Ainsi vous pouvez tester l’envoi de notification, par exemple, sans risquer de polluer des utilisateurs de production, ou activer le crash reporting uniquement en production par exemple.