# Keep your CoreData store small by vacuuming

In this blog post, I'll explain the concept of `VACUUM` and why & how you can apply this concept to your CoreData store.

As an iOS developer, you can use Apple's CoreData framework to store data as part of an object graph.

![](https://docs-assets.developer.apple.com/published/8fc7c1ecbc/35317515-fd0c-418f-862d-d81efd29ed29.png align="center")

Core Data uses an [SQLite store](https://developer.apple.com/documentation/coredata/nssqlitestoretype) by default. SQLite has the concept of `VACUUM` and therefore you need to understand what this is and why you may need it.

## What is `VACUUM` ?

SQLite has the [`VACUUM`](https://www.sqlite.org/lang_vacuum.html) command to rebuild the database file and **repacks it into a minimal amount of disk space**.

## Why to `VACUUM` ?

### Reduce the database file size

Per default, SQLite databases do not automatically "free up" disk space when you delete data from tables or drop database objects like tables, views or indexes.

**The database file size remains unchanged.** Because SQLite just marks the deleted objects as free and reserves the space for future uses. As a result, the size of the database file always grows in size.

### Increase performance

Frequent inserts, updates, and deletes can cause the database file to become fragmented and then accessing the database file becomes slower - also the database file is unnecessarily large because data is not stored contiguously within the database file.

## How to `VACUUM` your CoreData store?

When you create a new iOS project using Core Data ...

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678733884010/fcf13b07-d616-43da-ad6f-f67b838f40e1.png align="center")

then the underlying SQLite database will **automatically be created with an incremental** [**auto vacuum**](https://www.sqlite.org/pragma.html#pragma_auto_vacuum).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1678734123334/14908b03-33f3-49eb-a6f5-b580c51a32a1.png align="center")

CoreData will ensure invoking the separate [incremental\_vacuum](https://www.sqlite.org/pragma.html#pragma_incremental_vacuum) pragma to cause the auto-vacuum. This has the effect that you can observe that the database size will **eventually** shrink after multiple deletes and you can see the following entry in your Xcode debug console.

```bash
CoreData: debug: PostSaveMaintenance: incremental_vacuum with freelist_count
```

<mark>So your CoreData store gets vacuumed automatically without any adoption efforts.</mark>

Stack Overflow and Apple documentation may suggest setting the [`NSSQLiteManualVacuumOption`](https://developer.apple.com/documentation/coredata/nssqlitemanualvacuumoption) as part of your setup code for your `NSPersistentContainer` but this **is not needed anymore** unless you use the [Legacy Stack Setup](https://developer.apple.com/documentation/coredata/setting_up_a_core_data_stack/setting_up_a_core_data_stack_manually).

I found an interesting command in the SQLite documentation.

> When [auto\_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) is enabled for a database free pages may be reclaimed after deleting data, causing the file to shrink, without rebuilding the entire database using VACUUM. However, using [auto\_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) can **lead to extra database file fragmentation**.
> 
> And [auto\_vacuum](https://www.sqlite.org/pragma.html#pragma_auto_vacuum) does not compact partially filled pages of the database as VACUUM does.

Is fragmentation a performance problem for CoreData?

In my analysis & testing, I created and deleted thousands of entries over hundred times and I was not able to observe that fragmentation would significantly impact performance.

If you are worried about fragmentation and if you want to ensure that the database file shrinks to a time of your choosing then you can manually execute the SQLite `VACUUM`. Unfortunately, you cannot do this via CoreData APIs but you can interact with the SQLite database file directly with SQLite3.

Here is an extension that allows you to simply call `viewContext.vacuum()`

```swift
import CoreData
import SQLite3

extension NSManagedObjectContext {
    func vacuum() {
        for store in self.persistentStoreCoordinator?.persistentStores ?? [] {
            guard let url = store.url else { continue }
            
            print("vacuuming the database...")
            
            var db: OpaquePointer?
            guard sqlite3_open(url.path, &db) == SQLITE_OK else {
                print("error opening database")
                sqlite3_close(db)
                db = nil
                continue
            }
            
            if sqlite3_exec(db, "VACUUM;", nil, nil, nil) != SQLITE_OK {
                let errmsg = String(cString: sqlite3_errmsg(db)!)
                print("VACUUM error: \(errmsg)")
            }
            
            if sqlite3_close(db) != SQLITE_OK {
                print("error closing database")
            }
            
            db = nil
        }
    }
}
```
