A simple trick to reduce import statement clutter in Swift

A simple trick to reduce import statement clutter in Swift

Quite frequently I find myself needing to import a module to gain access to a type (usually a model), but I never interact with that module again. Recently I discovered simple way to skip having to put import ModuleName at the top of my code. This trick is especially handy in SwiftUI, but works for any use case.

Here's an example, let's say a user wants to see their weight from HealthKit in either pounds or kilograms. To do so they press a button. In order to change this information we will create a mock class that interacts with the health data store and returns the current unit for the user's weight:

import HealthKit
class DataProvider: ObservableObject {
    @Published var unit: HKUnit?
    
    func replaceUnit(_ unit: HKUnit) {
        self.unit = unit
    }
}

Back in the view, we display the current unit, and provide a button to change the unit:

import SwiftUI
import HealthKit
struct ContentView: View {
    @StateObject var provider = DataProvider()
    var body: some View {
        VStack {
            Text("Unit type: \(provider.unit?.unitString ?? "<No Unit>")")
            Button("Replace Model") {
                if let unit = provider.unit?.unitString, unit == "kg" {
                    provider.replaceUnit(HKUnit(from: "lb"))
                } else {
                    provider.replaceUnit(HKUnit(from: "kg"))
                }
            }
        }
        .padding()
    }
}

This is pretty simplistic, but imagine you are working on an app with many different views, all which need access to HealthKit objects like HKUnit, HKQuantityType, or HKSampleQuery. Every time you need to create a new HealthKit object for the DataProvider to interact with, you will need to include the import statement for your View to be able to initialize a HealthKit type.

A simple solution

A nice feature of Swift is that you can initialize an object without naming it directly with .init(). Once the compiler knows about DataProvider.replaceUnit(_ unit: HKUnit) it remembers the HKUnit type contextually, which allows you to create any new HKUnit in the body of the replaceUnit method without explicitly using its type:

import SwiftUI
//import HealthKit //Not needed any more!
struct ContentView: View {
    ...

            Button("Replace Model") {
                if let unit = provider.unit?.unitString, unit == "kg" {
                    provider.replaceUnit(.init(from: "lb"))
                } else {
                    provider.replaceUnit(.init(from: "kg"))
                }
            }
        }
        .padding()
    }
}

Once you get used to typing .init instead of the type's name, your import statement lines will drop considerably.

The only catch to this approach is that you can't declare a variable and initialize the HKUnit type. It has to be done in-line as a parameter of .replaceUnit(:), meaning this won't work:

let pound = HKUnit(from: "lb")
provider.replaceUnit(pound)
Show Comments