Giter VIP home page Giter VIP logo

easystash's Introduction

Easy Stash

CI Status Version Carthage Compatible SPM compatible License Platform Swift

Description

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.

  • Swift 5
  • Support iOS, macOS, tvOS, watchOS
  • Synchronous APIs with explicit try catch
  • Persist UIImage/NSImage
  • Persist Codable objects, including primitive types
  • Persist Data
  • 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. By default, folder and searchPathDirectory specify that files will be saved at /Library/Application Support/{BUNDLE_ID}/Default/

var storage: Storage? = nil

var options: Options = Options()
options.folder = "Users"

storage = try? Storage(options: options)

do {
    try storage?.save(image, forKey: "image")
    try storage?.save(users, forKey: "codable")
} catch {
    print(error)
}

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

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

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

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

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

try storage.exists(forKey: "has_updated_profile")

Remove file

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

Remove all files

try storage.removeAll()

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

let files = try storage.files()

Check folder size

let size = try storage.folderSize()

Check if folder has content

try storage.isEmpty()

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

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

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

    }
}

Installation

EasyStash is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'EasyStash'

EasyStash is also available through Carthage. To install just write into your Cartfile:

github "onmyway133/EasyStash"

EasyStash is also available through Swift Package Manager. Add EasyStash as a dependency to your Package.swift. For more information, please see the Swift Package Manager documentation.

.package(url: "https://github.com/onmyway133/EasyStash", from: "1.1.8")

EasyStash can also be installed manually. Just download and drop Sources folders in your project.

Author

Khoa Pham, [email protected]

Contributing

We would love you to contribute to EasyStash, check the CONTRIBUTING file for more info.

License

EasyStash is available under the MIT license. See the LICENSE file for more info.

easystash's People

Contributors

aaronpearce avatar bellebethcooper avatar chwo avatar jarrodparkes avatar magauran avatar onmyway133 avatar piemonte avatar yspreen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

easystash's Issues

Public method to get fileUrl

It's possible to get fileUrl by getting folderUrl and appending fileName to it but I've noticed that this code already exists inside Storage but it's not a public method.

func fileUrl(forKey key: String) -> URL {
    return folderUrl.appendingPathComponent(key, isDirectory: false)
}

I think it would be nice to make it public so that it will be convenient to get fileUrl by just using it. It could be even computed property. Another approach is to return fileUrl from save method.

Support directly specifying folder URL.

Thank you for your work, but it seems in your code, you just simply construct the folder URL by using the configurable FileManager.SearchPathDirectory and folder. However, it will be a lot better if you can just have an option for me to directly set a URL since FileManager API is robust and easy to use enough, and your options don't directly support situations like App Group Containers, which I believe is somewhat a must for apps with extensions.

Option to bypass in-memory cache

I'm using EasyStash to store some data in my iOS app, and I want to re-use that data in an iOS 14 widget. It seems like the widget and the app use different memory, so when the app updates the cache, the widget continues using the old data from its in-memory cache, which hasn't been updated, but the newer data is available to the widget from the on-disk cache. My current workaround is to add an option to Options to bypass the in-memory cache, and based on that Bool, only ever look up the key in the on-disk cache for my widget.

Just wondering if it's worth a PR for this, or if there's a better way to do it?

Archive to Appstore Connect fails: Info.plist file is missing

This bundle Payload/<app>/Frameworks/EasyStash.framework is invalid. The Info.plist file is missing the required key: CFBundleVersion. Please find more information about CFBundleVersion at https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleversion

carthage build fails

> carthage update 'EasyStash'

*** Skipped building EasyStash due to the error:
Dependency "EasyStash" has no shared framework schemes

If you believe this to be an error, please file an issue with the maintainers at https://github.com/onmyway133/EasyStash/issues/new

I believe this to be an error, so I want to file an issue with the maintainers ;)

Error when loading an Image

"Type of expression is ambiguous without more context"

This is the error I'm getting when trying to load an image that was previously persisted.

import Foundation
import UIKit
import EasyStash


class Stash {
    var storage: Storage? = nil
    init(){
        
    }
    
    func saveImage() {
        var options: Options = Options()
        options.folder = "Users"

        storage = try? Storage(options: options)
       
        guard let imageURL = URL(string: "https://somedomain.com/8607G.jpg") else {
            fatalError("inavlid URL")
        }      
        let data = try? Data(contentsOf: imageURL)       
        let img = UIImage(data: data!)    
        do {
            try storage?.save(object: img!, forKey: "image")
            
        } catch {
            print(error)
        }      
    }
     func loadImage(){
                let loadedImage: UIImage = try storage.load(forKey: "image")    ****Error****
      } 
}

Add new data to existing json file

First of all, I am parsing local json file and saving it to Users/******/Library/Developer/CoreSimulator/Devices/A9E40B28-82C7-4399-8DBC-AD2F1001C71C/data/Containers/Data/Application/1CAB4F6F-0820-4697-86EC-31E31CA09C6A/Library/Application%20Support/file_name/data path.

After that, I want to add a new data from my textField to bottom of that saved file's json data but saved file's json data removing and only my textField text data is writing.

ss1

ss2

Load objects for maxAge

Hello @onmyway133 👋,

would you like to see a possibility to load objects related to a maximum age? I like your project and the simplicity but would like to see a function like this:

func load<T: Codable>(forKey key: String, as: T.Type, maxAge: TimeInterval? = nil)

The idea is to use the modificationDate and the current Date() as references.

I ask in advanced before I put effort in the implementation and you don't like to see something like it.

TvOS Crash!

If i use tvos on a REAL device, it crash with description:

Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=513 "У Вас нет разрешения на сохранение файла «Application Support» в папке «Library»." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/BFD14D85-021C-4A4E-8DBE-A63E386CE6DD/Library/Application Support, NSUnderlyingError=0x28010b630 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}: , line 14

storage = try! Storage(options: options)

Fixed with:

options.searchPathDirectory = .cachesDirectory

Error on saving and loading Codable objects

Hi,

Here is how am saving and loading the objects:

var storage: Storage? = nil (Global variable)

In viewDidLoad:
var options: Options = Options()
options.folder = "People"
self.storage = try? Storage(options: options)

Saving:
do {
let savedUsersArray = try self.storage?.load(forKey: "People", as: [User].self)
if var safeArray = savedUsersArray {
for user in self.savedUsers {
safeArray.append(user)
}
try self.storage?.save(object: safeArray, forKey: "People")
} else {
try self.storage?.save(object: self.allUsers, forKey: "People")
}
} catch {
print(error)
}

Loading:

do {
let savedUsersArray = try self.storage?.load(forKey: "People", as: [User].self)
if let safeArray = savedUsersArray {

        } else {

        }

} catch {
print(error)
}

However, I keep getting the error:
Error Domain=NSCocoaErrorDomain Code=260 "The file “People” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users/eliaserbenjamin/Library/Developer/CoreSimulator/Devices/F083E6A1-DE4D-4490-9202-4EE3A9D41421/data/Containers/Data/Application/FBC32428-2EE8-4D40-83F4-0732DAAF9E13/Library/Application Support/People/People, NSUnderlyingError=0x7fef78bdb240 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

The codable object:

struct User: Codable, Equatable {

var id = ""
var username: String
var email: String
var pushId = ""
var avatarLink = ""
var status: String
var country: String
var cellphoneNumber: String
var isOnline: String
var lastSeen: Double

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.