DEV Community

Cover image for barK: iOS Logging in KMP
Iván Garza Bermea
Iván Garza Bermea

Posted on • Originally published at Medium

barK: iOS Logging in KMP

A few months ago I introduced the world to barK, a light-weight KMP logging library I created while I was interviewing through the summer. 

In my first article, I used Android to showcase all of the features as well as example usage. Most of the information in that piece is still relevant. But after a few months of development, that initial article starts getting a little bit outdated. And most importantly, I never covered iOS!

Therefore, for this article I will present an updated version of the initial setup — barK has since migrated from Jitpack to Maven — as well as basic usage for the iOS side of a KMP project.

NOTE: barK only works for iOS KMP, and not for regular iOS applications!

Setting Up

Setting up barK for iOS usage in KMP begins the same way as any other KMP library integration: by bringing in the Maven dependency into your gradle.build.kts. Next, we explicitly export the barK library into the iOS framework. Finally, we’ll create a BarkExtensions.swift file inside Xcode so the iOS project has a clean, idiomatic interface.

Gradle Setup

Let’s begin with the Gradle setup. To get started, integrate the barK library in your project using either the a libs.versions.toml or directly in your build.gradle.kts Gradle build file.

// libs.versions.toml
[versions]
...
bark = <version>

[libraries]
...
bark = { module = "com.ivangarzab:bark", version.ref = "bark" }

// build.gradle.kts
dependencies {
    ...
    implementation(libs.bark)
}
Enter fullscreen mode Exit fullscreen mode

Then, find your iOS framework declaration — most likely in the :shared module — and directly export the the library into iOS for access.

listOf(
    iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "Shared"
            isStatic = true
            ...
            export(libs.bark)
        }
    }
Enter fullscreen mode Exit fullscreen mode

This is done so that the iOS app has visibility over the barK types.

NOTE: You’ll probably want to implement the barK library in the same module that you declare your iOS framework!

barK Extensions

The second part of the set up is to create a new file on the iOS project called BarkExtensions.swift as to enable clean and safe usage of the barK library.

The /ios directory inside the barK GitHub repository has further instructions on how this part work. But the gist is to bring in the /ios/BarkExtensions.swift file into the KMP iOS app.

For example, one my side-projects has it located at: iosApp/project/iosApp/BarkExtensions.swift.

Features

With the project set up, let’s take a quick look at the three main features that barK offers: Tagging, Training & Logging. I will make the effort to point out any differences that the iOS implementation may have with Android, as to fully elucidate the iOS capabilities and how it differs to its counterpart.

Tagging

We’ll begin with Tagging. There are two main ways that barK manages log tags for iOS: You can, (1) enable the tag auto-detection feature, or (2) set your own global tag.

Tag Auto-Detection

Differently to Android, barK doesn’t automatically add the class name into its logs on the iOS side. This is actually an optional feature that needs to be enabled manually; at least for iOS classes!

Bark.autoTagDisabled = false
Enter fullscreen mode Exit fullscreen mode

The Bark.autoTagDisabled flag controls whether the iOS version of barK will be automatically including tags or not. The reason why this flag defaults to Off, is because generating tags on the iOS side is rather costly.

Global Tag

On the other hand, barK also accepts a global tag to be set across the board, same as it does on the Android side.

Bark.tag("Global tag")
Enter fullscreen mode Exit fullscreen mode

By calling the .tag() function of the BarkExtensions.swift wrapper, you can set a global tag at any point of the app. It is also possible to .untag() the global instance, and get back to normal usage.

NOTE: Global tags would override the auto-detection feature, so make sure you don’t mix and match!

Training

Moving on to Training, the barK system requires to be ‘trained’ as to know what to log, and where to send these logs. Calling Bark.train(Trainer) will get that done . This function can be called multiple times in order to add multiple trainers to the global instance.

You can also Bark.untrain(Trainer) or Bark.releaseAllTrainers() to clean up Trainercapabilities.

static func train(trainer: Trainer)
static func untrain(trainer: Trainer)
static func releaseAllTrainers()
Enter fullscreen mode Exit fullscreen mode

Logging

Finally, with Tagging & Training taken care of, we can now focus on Logging.

static func v(message: String, throwable: Error? = nil)
static func d(message: String, throwable: Error? = nil)
static func i(message: String, throwable: Error? = nil)
static func w(message: String, throwable: Error? = nil)
static func e(message: String, throwable: Error? = nil)
Enter fullscreen mode Exit fullscreen mode

Logging in iOS works exactly the same as in Android.

Bark.d("Your log")
Enter fullscreen mode Exit fullscreen mode

The log levels that barK uses are Android-centric, in order to better fit the Kotlin shared layer, and avoid overfitting solutions.

Muzzling

BarK also offers a simple way of ‘muzzling’ (or muting) the library during runtime, by simply calling the muzzle() function.

static func muzzle()
static func umuzzle()
Enter fullscreen mode Exit fullscreen mode

NOTE: This function can be particularly useful when dealing with flows containing sensitive data, for example.

Example Usage

With the library integrated, and the features explained, we will now take a look at usage. The following block shows a typical init & implementation of the barK library on iOS.

@main
struct iOSApp: App {

    init() {
        ...
        startLogging()
    }

    private func startLogging() {
        #if DEBUG
        let volume = Level.verbose
        #else
        let volume = Level.debug
        #endif

        Bark.autoTagDisabled = false
        Bark.train(trainer: NSLogTrainer(volume: Level.verbose))
        Bark.v("Bark has been initilized for iOS successfully!")
    }
}
Enter fullscreen mode Exit fullscreen mode

Notice that we’re setting the volumeLevel of the Trainer based on whether we’re running a DEBUG or production build.

Moreover, we’re enabling the autoTagDisabled flag so that we can get automatic tag detection.

Next, we’re doing a simple .train() call using the built-in NSLogTrainer receiving the volume Level from before.

At the end, we’re doing a single .v() call to print our first log, indicating that barK has been initialized successfully!

Afterthought

We have now seen how barK works for KMP iOS applications!

The idea behind barK is not only to make logging easier to client applications, but also for SDK libraries and their integrating clients as well.

Therefore, I will publish another article diving deeper into SDK usage.

And once again, feel free to check out the first article of this series for the Android implementation.

For now, let me know what you think of this little passion project of mine. And keep those Android + iOS apps barking loudly!

barK: Because every log deserves a good home. 🐶🏠


Resources

Top comments (0)