Introducing EasyStash - Easy data persistence in Swift
Tags: swift, cache, ios, macos
Published: 2019-06-12 07:45:27

---
title: Introducing EasyStash - Easy data persistence in Swift
published: true
description: 
tags: swift,cache,ios,macos
---

The library is available on CocoaPods https://github.com/onmyway133/EasyStash

EasyStash is an easy and lightweight persistence framework in Swift. With simple abstraction over `NSCache` and `FileManager`, it saves us from tedious work of saving and loading objects. There are no clever async, expiry handling or caching strategy for now, just save and load.

- [x] Swift 5
- [x] Support iOS, macOS, tvOS
- [x] Synchronous APIs with explicit try catch
- [x] Persist UIImage/NSImage
- [x] Persist Codable objects, including primitive types
- [x] Persist Data
- [x] Test coverage

## Usage

The main and only class is `Storage` which encapsulates memory and disk cache. All operations involving disk are error prone, we need to handle error explicitly.

With `Options`, we can customize `folder` name, `searchPathDirectory`, `encoder` and `decoder` for `Codable`

```swift
let options = Options()
options.folder = "Users"
storage = try! Storage(options: options)

try storage.save(image, forKey: "image")
try storage.save(users, forKey: "codable")
```

Memory cache is checked first before doing disk operations, so we won't hit disk that often.

### Saving and loading images

Works for both UIImage and NSImage. Because image and data loading uses the same signatures, we need to explicitly specify type

```swift
try storage.save(image, forKey: "image")
let loadedImage: UIImage = try storage.load(forKey: "image")
```

### Saving and loading Codable objects

Uses `JSONEncoder` and `JSONDecoder` under the hood to serialize and deserialize to and from `Data`

```swift
let user = User(name: "A", age: 10)
let cities = [City(name: "Oslo"), City(name: "New York")]

try storage.save(users, forKey: "user")
try storage.save(cities, forKey: "cities")

let loadedUser = try storage.load(forKey: "user", as: User.self)
let loadedCities = try storage.load(forKey: "cities", as: [City].self)
```

### Saving and loading Data

```swift
try storage.save(object: data, forKey: "data")
let loadedData: Data = try storage.load(forKey: "data")
```

### Saving and loading primitives

Although primitives like `Int, String, Bool` conform to `Codable`, they can't be serialized into `Data` using `JSONEncoder` because json needs root object. This framework handles this case, so you can just save and load as normal

```swift
try storage.save(100, forKey: "an int")
try storage.save(isLoggedIn, forKey: "a boolean")
```

### Folder informations

EasyStash includes some helpful functions to check file and folder within its `Storage`.

Check if file exists

```swift
try storage.exists(forKey: "has_updated_profile")
```

Remove file

```swift
try storage.remove(forKey: "a flag")
```

Remove all files

```swift
try storage.removeAll()
```

List all files. Each file has `name`, `url`, `modificationDate` and `size` information

```swift
let files = try storage.files()
```

Check folder size

```swift
let size = try storage.folderSize()
```

Check if folder has content

```swift
try storage.isEmpty()
```

Remove files based on predicate. This is useful when we want to clear expired objects, or objects based certain criteria.

```swift
try storage.removeAll(predicate: { $0.modificationDate < migrationDate })
```

## Async

`EasyStash` is designed to be synchronous. If we want to do async, it's easy as using `DispatchQueue`

```swift
DispatchQueue.global().async {
    do {
        try storage.save(largeImage, forKey: "large_image")
    } catch {
        
    }
}
```