Stuart Breckenridge

The personal blog of Stuart Breckenridge

Reducing Bundle Size and Improving Extension Performance

The today extension of The FFI List displays the number of registered FFIs broken down by their entity type. It’s fairly simple, but I made a few mistakes when putting it together:

  1. I copied the sqlite database into both the main app and the extension; and,
  2. The code to run queries on the database to obtain statistics was part of the extension.

The net effect was that the bundle size was approaching 180MB (two 80MB databases). In addition, performance of the extension was haphazard: I had reports, and had seen myself, instances where the extension returned a seemingly random count of total database entries. What’s worse was that I couldn’t debug the problem. No errors were raised, the underlying data was intact, and I couldn’t reproduce the problem with any regularity. It also took around upwards of four seconds for the queries to run.

In the upcoming v2.1 release, I’m happy to say these issues are fixed.

First, the sqlite database is longer embedded in both the main application and the extension. The bundle size is once again under 100MB.

Second, to make sure that the extension was able to show statistics like it did before, I created a Statistics.plist file and as part of the build process, I run some sqlite queries and then populate the data into the plist file like so:

sponsors=$( sqlite3 _YOUR_DATABASE_ "select count(ZENTITYTYPE) from ZFFIENTITY where ZENTITYTYPE = '01SP'")
/usr/libexec/PlistBuddy -c "Set 01SP $sponsors" "${TARGET_BUILD_DIR}/FFI.app/Statistics.plist"

When the main app runs, it will copy the Statistics.plist file into the shared container:

let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.ffiinfo")?.appendingPathComponent("/Statistics.plist")
let stats = Bundle.main.path(forResource: "Statistics", ofType: "plist")
        
guard let containerURL = container else {
    return shouldPerformAdditionalDelegateHandling
}
        
guard let statsPath = stats else {
    return shouldPerformAdditionalDelegateHandling
}

if FileManager.default.fileExists(atPath: containerURL.path) {
    do {
        try FileManager.default.removeItem(atPath: containerURL.path)
        try FileManager.default.copyItem(atPath: statsPath, toPath: containerURL.path)
    } catch {
        print(error)
    }
} else {
    do {
        try FileManager.default.copyItem(atPath: statsPath, toPath: containerURL.path)
    } catch {
        print(error)
    }
}

The extension then displays the available data from the plist when it exists in the container. This improves performance from several seconds to instantaneous as the extension is no longer running any database queries.