Delightful framework for iOS to easily persist structs, images, and data

Overview

Disk

GitRoyalty Platform: iOS 9.0+ Language: Swift 4 Carthage compatible License: MIT

InstallationUsageDebuggingA WordDocumentationApps Using DiskLicenseContributeQuestions?

Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind. Disk uses the new Codable protocol introduced in Swift 4 to its utmost advantage and gives you the power to persist structs without ever having to worry about encoding/decoding. Disk also helps you save images and other data types to disk with as little as one line of code.

Compatibility

Disk requires iOS 9+ and is compatible with projects using Swift 4.0 and above. Therefore you must use at least Xcode 9 when working with Disk.

Installation

Support Disk's contributors with a monthly subscription on https://gitroyalty.com/saoudrizwan/Disk to install this package.

Subscribe on GitRoyalty
* comes with a 2 week free trial and can be cancelled anytime

Usage

Disk currently supports persistence of the following types:

  • Codable
  • [Codable]
  • UIImage
  • [UIImage]
  • Data
  • [Data]

These are generally the only types you'll ever need to persist on iOS.

Disk follows Apple's iOS Data Storage Guidelines and therefore allows you to save files in four primary directories and shared containers:

Documents Directory .documents

Only documents and other data that is user-generated, or that cannot otherwise be recreated by your application, should be stored in the <Application_Home>/Documents directory and will be automatically backed up by iCloud.

Caches Directory .caches

Data that can be downloaded again or regenerated should be stored in the <Application_Home>/Library/Caches directory. Examples of files you should put in the Caches directory include database cache files and downloadable content, such as that used by magazine, newspaper, and map applications.

Use this directory to write any application-specific support files that you want to persist between launches of the application or during application updates. Your application is generally responsible for adding and removing these files (see Helper Methods). It should also be able to re-create these files as needed because iTunes removes them during a full restoration of the device. In iOS 2.2 and later, the contents of this directory are not backed up by iTunes.

Note that the system may delete the Caches/ directory to free up disk space, so your app must be able to re-create or download these files as needed.

Application Support Directory .applicationSupport

Put app-created support files in the <Application_Home>/Library/Application support directory. In general, this directory includes files that the app uses to run but that should remain hidden from the user. This directory can also include data files, configuration files, templates and modified versions of resources loaded from the app bundle.

Temporary Directory .temporary

Data that is used only temporarily should be stored in the <Application_Home>/tmp directory. Although these files are not backed up to iCloud, remember to delete those files when you are done with them so that they do not continue to consume space on the user’s device.

Application Group Shared Container .sharedContainer(appGroupName: String)

Multiple applications on a single device can access a shared directory, as long as these apps have the same groupIdentifier in the com.apple.security.application-groups entitlements array, as described in Adding an App to an App Group in Entitlement Key Reference.

For more information, visit the documentation: https://developer.apple.com/documentation/foundation/nsfilemanager/1412643-containerurlforsecurityapplicati


With all these requirements and best practices, it can be hard working with the iOS file system appropriately, which is why Disk was born. Disk makes following these tedious rules simple and fun.

Using Disk is easy.

Disk handles errors by throwing them. See Handling Errors Using Do-Catch.

Structs (must conform to Codable)

Let's say we have a data model called Message...

struct Message: Codable {
    let title: String
    let body: String
}

... and we want to persist a message to disk...

let message = Message(title: "Hello", body: "How are you?")
try Disk.save(message, to: .caches, as: "message.json")

... or maybe we want to save it in a folder...

try Disk.save(message, to: .caches, as: "Folder/message.json")

... we might then want to retrieve this message later...

let retrievedMessage = try Disk.retrieve("Folder/message.json", from: .caches, as: Message.self)

If you Option + click retrievedMessage, then Xcode will show its type as Message. Pretty neat, huh? example

So what happened in the background? Disk first converts message to JSON data and atomically writes that data to a newly created file at <Application_Home>/Library/Caches/Folder/message.json. Then when we retrieve the message, Disk automatically converts the JSON data to our Codable struct type.

What about arrays of structs?

Thanks to the power of Codable, storing and retrieving arrays of structs is just as easy as the code above.

var messages = [Message]()
for i in 0..<5 {
    messages.append(Message(title: "\(i)", body: "..."))
}
try Disk.save(messages, to: .caches, as: "messages.json")
let retrievedMessages = try Disk.retrieve("messages.json", from: .caches, as: [Message].self)

Appending structs (Thank you for the suggestion @benpackard)

Disk also allows you to append a struct or array of structs to a file with data of the same type.

try Disk.append(newMessage, to: "messages.json", in: .caches)

Note: you may append a single struct to an empty file, but then in order to properly retrieve that struct again, you must retrieve it as an array.

Using custom JSONEncoder or JSONDecoder (Thank you @nixzhu and @mecid)

Behind the scenes, Disk uses Apple's JSONEncoder and JSONDecoder classes to encode and decode raw JSON data. You can use custom instances of these classes if you require special encoding or decoding strategies for example.

let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
try Disk.save(messages, to: .caches, as: "messages.json", encoder: encoder)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let retrievedMessages = try Disk.retrieve("messages.json", from: .caches, as: [Message].self, decoder: decoder)

Note: appending a Codable structure requires Disk to first decode any existing values at the file location, append the new value, then encode the resulting structure to that location.

try Disk.append(newMessage, to: "messages.json", in: .caches, decoder: decoder, encoder: encoder)

Images

let image = UIImage(named: "nature.png")
try Disk.save(image, to: .documents, as: "Album/nature.png")
let retrievedImage = try Disk.retrieve("Album/nature.png", from: .documents, as: UIImage.self)

Array of images

Multiple images are saved to a new folder. Each image is then named 0.png, 1.png, 2.png, etc.

var images = [UIImages]()
// ...
try Disk.save(images, to: .documents, as: "FolderName/")

You don't need to include the "/" after the folder name, but doing so is declarative that you're not writing all the images' data to one file, but rather as several files to a new folder.

let retrievedImages = try Disk.retrieve("FolderName", from: .documents, as: [UIImage].self)

Let's say you saved a bunch of images to a folder like so:

try Disk.save(deer, to: .documents, as: "Nature/deer.png")
try Disk.save(lion, to: .documents, as: "Nature/lion.png")
try Disk.save(bird, to: .documents, as: "Nature/bird.png")

And maybe you even saved a JSON file to this Nature folder:

try Disk.save(diary, to: .documents, as: "Nature/diary.json")

Then you could retrieve all the images in the Nature folder like so:

let images = try Disk.retrieve("Nature", from: .documents, as: [UIImage].self)

... which would return -> [deer.png, lion.png, bird.png]

Appending images

Unlike how appending a struct simply modifies an existing JSON file, appending an image adds that image as an independent file to a folder.

try Disk.append(goat, to: "Nature", in: .documents)

Note: it's recommended to manually save an independent image using the save(:to:as:) function in order to specify a name for that image file in case you want to retrieve it later. Using the append(:to:in:) function results in creating a file with an auto-generated name (i.e. if you append an image to a folder with images already present (0.png, 1.png, 2.png), then the new image will be named 3.png.) If the image name is not important, then using append(:to:in:) is fine. Appending arrays of images is similar in behavior.

Data

If you're trying to save data like .mp4 video data for example, then Disk's methods for Data will help you work with the file system to persist all data types.

let videoData = Data(contentsOf: videoURL, options: [])
try Disk.save(videoData, to: .documents, as: "anime.mp4")
let retrievedData = try Disk.retrieve("anime.mp4", from: .documents, as: Data.self)

Array of Data

Disk saves arrays of Data objects like it does arrays of images, as files in a folder.

var data = [Data]()
// ...
try Disk.save(data, to: .documents, as: "videos")
let retrievedVideos = try Disk.retrieve("videos", from: .documents, as: [Data].self)

If you were to retrieve [Data] from a folder with images and JSON files, then those files would be included in the returned value. Continuing the example from the Array of images section:

let files = try Disk.retrieve("Nature", from: .documents, as: [Data].self)

... would return -> [deer.png, lion.png, bird.png, diary.json]

Appending Data

Appending Data or an array of Data is similar to appending an image or array of images—new files are created with auto-generated names and added to the specified folder.

try Disk.append(newDataObject, to: "Folder/", in: .documents)

Large files

It's important to know when to work with the file system on the background thread. Disk is synchronous, giving you more control over read/write operations on the file system. Apple says that "because file operations involve accessing the disk, performing those operations asynchronously is almost always preferred."

Grand Central Dispatch is the best way to work with Disk asynchronously. Here's an example:

activityIndicator.startAnimating()
DispatchQueue.global(qos: .userInitiated).async {
    do {
        try Disk.save(largeData, to: .documents, as: "Movies/spiderman.mp4")
    } catch {
        // ...
    }
    DispatchQueue.main.async {
        activityIndicator.stopAnimating()
        // ...
    }
}

Don't forget to handle these sorts of tasks being interrupted.

iOS 11 Volume Information

Apple introduced several great iOS storage practices in Session 204, putting emphasis on several new NSURL volume capacity details added in iOS 11. This information allows us to gauge when it's appropriate to store data on the user's disk.

  • Total capacity
Disk.totalCapacity
  • Available capacity
Disk.availableCapacity
  • Available capacity for important usage. This indicates the amount of space that can be made available for things the user has explicitly requested in the app's UI (i.e. downloading a video or new level for a game.)
Disk.availableCapacityForImportantUsage
  • Available capacity for opportunistic usage. This indicates the amount of space available for things that the user is likely to want but hasn't explicitly requested (i.e. next episode in video series they're watching, or recently updated documents in a server that they might be likely to open.)
Disk.availableCapacityForOpportunisticUsage

Note: These variables return Optional Ints since retrieving file system resource values may fail and return nil. However this is very unlikely to happen, and this behavior exists solely for safety purposes.

Helper Methods

  • Clear an entire directory
try Disk.clear(.caches)
  • Remove a file/folder
try Disk.remove("video.mp4", from: .documents)
  • Check if file/folder exists
if Disk.exists("album", in: .documents) {
    // ...
}
  • Move a file/folder to another directory
try Disk.move("album/", in: .documents, to: .caches)
  • Rename a file/folder
try Disk.rename("currentName.json", in: .documents, to: "newName.json")
  • Get file system URL for a file/folder
let url = try Disk.url(for: "album/", in: .documents)
  • Mark a file/folder with the do not backup attribute (this keeps the file/folder on disk even in low storage situations, but prevents it from being backed up by iCloud or iTunes.)
try Disk.doNotBackup("album", in: .documents)

Everything in your app’s home directory is backed up, with the exception of the application bundle itself, the caches directory, and temporary directory.

try Disk.backup("album", in: .documents)

(You should generally never use the .doNotBackup(:in:) and .backup(:in:) methods unless you're absolutely positive you want to persist data no matter what state the user's device is in.)

URL Counterparts

Most of these helper methods have URL counterparts, in case you want to work with files directly with their file system URLs.

let fileUrl = try Disk.url(for: "file.json", in: .documents)
  • Remove a file/folder
try Disk.remove(fileUrl)
  • Check if file/folder exists
if Disk.exists(fileUrl) {
    // ...
}
  • Move a file/folder to another directory
let newUrl = try Disk.url(for: "Folder/newFileName.json", in: .documents)
try Disk.move(fileUrl, to: newUrl)
  • Mark a file/folder with the do not backup attribute
try Disk.doNotBackup(fileUrl)
try Disk.backup(fileUrl)
  • Check if URL is of a folder
if Disk.isFolder(fileUrl) {
    // ...
}

Debugging

Disk is thorough, meaning that it will not leave an error to chance. Almost all of Disk's methods throw errors either on behalf of Foundation or custom Disk Errors that are worth bringing to your attention. These errors have a lot of information, such as a description, failure reason, and recovery suggestion:

do {
    if Disk.exists("posts.json", in: .documents) {
        try Disk.remove("posts.json", from: .documents)
    }
} catch let error as NSError {
    fatalError("""
        Domain: \(error.domain)
        Code: \(error.code)
        Description: \(error.localizedDescription)
        Failure Reason: \(error.localizedFailureReason ?? "")
        Suggestions: \(error.localizedRecoverySuggestion ?? "")
        """)
}

The example above takes care of the most common error when dealing with the file system: removing a file that doesn't exist.

A Word from the Developer

After developing for iOS for 8+ years, I've come across almost every method of data persistence there is to offer (Core Data, Realm, NSKeyedArchiver, UserDefaults, etc.) Nothing really fit the bill except NSKeyedArchiver, but there were too many hoops to jump through. After Swift 4 was released, I was really excited about the Codable protocol because I knew what it had to offer in terms of JSON coding. Working with network responses' JSON data and converting them to usable structures has never been easier. Disk aims to extend that simplicity of working with data to the file system.

Let's say we get some data back from a network request...

let _ = URLSession.shared.dataTask(with: request) { (data, response, error) in
    DispatchQueue.main.async {
        guard error == nil else { fatalError(error!.localizedDescription) }
        guard let data = data else { fatalError("No data retrieved") }

        // ... we could directly save this data to disk...
        try? Disk.save(data, to: .caches, as: "posts.json")

    }
}.resume()

... and retrieve it later as [Post]...

let posts = try Disk.retrieve("posts.json", from: .caches, as: [Post].self)

Disk takes out a lot of the tedious handy work required in coding data to the desired type, and it does it well. Disk also makes necessary but monotonous tasks simple, such as clearing out the caches or temporary directory (as required by Apple's iOS Data Storage Guidelines):

try Disk.clear(.temporary)

Disk is also significantly faster than alternative persistence solutions like NSKeyedArchiver, since it works directly with the file system. Best of all, Disk is thorough when it comes to throwing errors, ensuring that you understand why a problem occurs when it does.

Documentation

Option + click on any of Disk's methods for detailed documentation. documentation

Apps Using Disk

License

Disk uses the MIT license. Please file an issue if you have any questions or if you'd like to share how you're using Disk.

Contribute

Please feel free to create issues for feature requests or send pull requests of any additions you think would complement Disk and its philosophy.

Questions?

Contact me by email [email protected], or by twitter @sdrzn. Please create an issue if you come across a bug or would like a feature to be added.

Comments
  • Feature Idea: Migrating Data

    Feature Idea: Migrating Data

    This library is awesome- lightweight, fun, and useful. Thank you for making this!

    Have you thought about any kind of data migration? For example, if someone adds/removes a property to a struct after saving it, and then tries to load a version of a json file saved using the old version of the stuct?

    Guessing you probably don't want to build something as robust as Realm's schema migration... have you brainstormed ideas on the best way to implement this?

    Thanks again!

    opened by analogcode 11
  • Saving a new Codable into a folder

    Saving a new Codable into a folder

    Hello @saoudrizwan! Thank you for sharing Disk, I am excited to try it out. I'm running into a small issue I've tried to debug but I'm not having any luck. Maybe you can help.

    I shortened the code to the smallest reproducible case. With this code...

    struct Message: Codable {
        var body: String
    }
    
    static func saveMessage() {
        do {
            let messages = [Message]()
            try Disk.save(messages, to: .documents, as: "Messages/")
            
            let first = Message(body: "Hello world.")
            try Disk.save(first, to: .documents, as: "Messages/first.json")
        } catch let error as NSError {
            print(error)
        }
    }
    

    I get this error...

    Error Domain=NSCocoaErrorDomain Code=516 "The file “Messages” couldn’t be saved 
    in the folder “Documents” because a file with the same name already exists." 
    UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/F45ABDA8-33A7-4003-9552-66722A855C55/Documents/Messages, 
    NSUnderlyingError=0x1c024d500 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}
    

    Things I've tried:

    • Delete the app, install it onto my device fresh, and try again
    • Change the name of the folder

    My end goal is to have a folder with individual JSON files, one for each Codable. From the readme, it seems that the one possible set of steps is:

    1. Create a folder from an empty array of type [Codable]
    2. Save an object of that same Codable type with a path that is [folderName]/[fileName].json

    Am I perhaps using Disk incorrectly? Thank you for your help!

    opened by sahandnayebaziz 8
  • The package dependency graph can not be resolved; unable find any available tag for the following requirements:  https://github.com/saoudrizwan/Disk.git — 0.6.3

    The package dependency graph can not be resolved; unable find any available tag for the following requirements: https://github.com/saoudrizwan/Disk.git — 0.6.3

    I get this error when adding the package to XCode Version 11.0 beta 5 (11M382q) on the latest Catelina...

    The package dependency graph can not be resolved; unable find any available tag for the following requirements:

    https://github.com/saoudrizwan/Disk.git — 0.6.3

    Screenshot 2019-08-08 at 10 56 20

    Right now I'm using the master branch as a workaround.....

    opened by tychop 6
  • Crash: Adding a new variable is causing.

    Crash: Adding a new variable is causing.

    Hi @saoudrizwan , I am a long time user of this library! Thank you. ✌️Today, I came across a crash is happening when I will add a new variable to my struct.

    Example:

    1. I need to create a struct named: Person. I will store name and age details of a person.
    struct Person: Codable {
       var name: String = ""
       var age: Int = 30
    }
    
    1. Now, I will create few objects of Person and will save it using Disk.

    2. Later, I have a new requirements and I need to add one more variable to store a person's gender. So I'll update my struct like this:

    struct Person: Codable {
       var name: String = ""
       var age: Int = 30
       var gender: String = "M"
    }
    
    1. Now, I will run the app. This will cause a crash.

    The only solution is to empty Person.json file and run the app again.

    Anyways to fix this?

    opened by hemangshah 6
  • Getting an error

    Getting an error " No exact matches in call to static method 'save' " when trying to save to disk

    I'm following a SwiftUI tutorial (https://peterfriese.dev/replicating-reminder-swiftui-firebase-part1/) which leverages Disk, however I'm getting the following error when I try to use the Disk.retrieve and Disk.save methods:

    "No exact matches in call to static method 'retrieve'" "No exact matches in call to static method 'save'"

    I'm using Xcode 11.5.

    opened by Jsayer7 4
  • Custom Encoder not getting passed

    Custom Encoder not getting passed

    When I want to pass encoder with custom nonConformingFloatEncodingStrategy to Disk.append, it will not get passed inside the function to the Disk.save function. The encoder parameter is there but it has a default value and is not overwritten.

    I think this should be added.

    opened by vojtabohm 4
  • Feature idea: Data migrations / support for schema change

    Feature idea: Data migrations / support for schema change

    Say we start with some model user:

    class User {
        enum CodingKeys: String, CodingKey {
            case name = "name"
        }
        var name: String
    }
    

    And later we add a property called like birthdate: Date

    In Disk, we are faced with two choices... We can omit birthdate from the CodingKeys enum forever and provide a default value for all user's birthdates (not ideal), or we can add birthdate to the keys and lose all previously stored User files (also not ideal).

    Is there some way to get the NSKeyArchiver to fall back and not abort altogether? Like, if in the event that the model DOES provide a default value for a property, it could maybe not absolutely require all of the CodingKeys.

    Use case: I use Disk with this protocol to quickly store singleton objects with settings/preferences on them. Currently, if I "add" a preference property to one of these singletons, all of the previous settings get wiped out. Just curious if this is a reasonable feature in the scope of Disk.

    public protocol Persistable: Codable {
        init()
        static func load() -> Self
        static func save(_ obj: Self)
        static var persistenceFileName: String { get }
    }
    
    extension Persistable {
        public static func load() -> Self {
            if let manager = try? Disk.retrieve(Self.persistencePath, from: .documents, as: Self.self) {
                return manager
            } else {
                return Self()
            }
        }
    
        public static func save(_ obj: Self) {
            try? Disk.save(obj, to: .documents, as: Self.persistencePath)
        }
    
        public static var persistencePath: String {
            return self.persistenceFileName.appending(".json")
        }
    }
    
    opened by lacyrhoades 4
  • Fatal error with trying to save Image

    Fatal error with trying to save Image

    I've tried to save an image as shown in your example, and the app always crashes with,

    fatal error: Optional does not conform to Encodable because UIImage does not conform to Encodable

    When I hold down Alt/Option and hover over my declaration, it shows the declaration for the Codable initializer even though I've used Disk.save with an UIImage.

    Any ideas if I am using it wrong?

    opened by feodormak 4
  • Append to file, empty contents of file

    Append to file, empty contents of file

    Apologies if this is supported already - I have only reads the reader so far.

    Is there a command for appending to a file? Or should we read the file first back into model objects, append objects, and rewrite?

    Similarly, can we remove the contents of a file without removing the file itself? Or should we delete it and recreate it with an empty array for example?

    opened by benpackard 4
  • Error Creating And Saving To Subfolder

    Error Creating And Saving To Subfolder

    I'm getting a permissions error when trying to save data to a subfolder that is created as part of the save.

    Here's the code where I'm trying to save the data:

        do {
            let data = try Data(contentsOf: url)
            let path = "Inbox/PDF/\(title)"
            try Disk.save(data, to: .documents, as: path)
        } catch let error as NSError {
            print("""
                Domain: \(error.domain)
                Code: \(error.code)
                Description: \(error.localizedDescription)
                Failure Reason: \(error.localizedFailureReason ?? "")
                Suggestions: \(error.localizedRecoverySuggestion ?? "")
                """)
        }
    

    When I run this on the simulator, it works just fine. However, when I run it on a device, I get this response:

    Domain: NSCocoaErrorDomain
    Code: 513
    Description: You don’t have permission to save the file “PDF” in the folder “Inbox”.
    Failure Reason: You don’t have permission.
    Suggestions: To view or change permissions, select the item in the Finder and choose File > Get Info.
    

    I tracked it down, and the error is bubbling up from Disk+InternalHelpers.swift::188. That line calls createDirectory(at:withIntermediateDirectories:attributes:), which is throwing the error. The really weird thing is that "PDF" is supposed to be a subfolder of "Inbox", not a file inside it.

    Here's the value for the subfolderUrl variable: file:///var/mobile/Containers/Data/Application/AC2C2E56-8188-426A-8C59-E508B56C7954/Documents/Inbox/PDF/

    Am I doing something wrong here? Why would this work in the simulator, but throw on a device?

    opened by forgot 3
  • Permission denied during read operation

    Permission denied during read operation

    I've saved an object with try! Disk.save(recipe, to: .documents, as: "Recipes/"+recipe.fileName+".json") and tried to load it with try! Disk.retrieve("Recipes", from: .documents, as: [Recipe].self) and got the following error: Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=257 "The file “Ingredients” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/Users/danyel/Library/Developer/CoreSimulator/Devices/9D629C11-EC8B-4B0D-A5FD-1D2FC8B1D5F5/data/Containers/Data/Application/A55E48FA-C5C6-4796-8A32-946E51313879/Documents/Ingredients, NSUnderlyingError=0x60400024d650 {Error Domain=NSPOSIXErrorDomain Code=13 "Permission denied"}}

    Any ideas for a solution?

    opened by y1337x 3
  • Running unit test on your local machine (macOS) clears ~/Documents/ folder

    Running unit test on your local machine (macOS) clears ~/Documents/ folder

    I foolishly ran unit tests on my local machine without looking at the code first :\ I even agreed to give permissions to access/edit files in my Documents folder. Little did I know, later on everything in my ~/Documents folder was completely erased off of my hard drive, it went straight into the void past the bin.

    Luckily I was able to restore all deleted files using iCloud. But there might be other people who do the same mistake. It would be a very good idea to put some kind of dummy protection in place for unit tests like this.

    enhancement 
    opened by Roudique 0
  • Pass compression quality for jpeg images

    Pass compression quality for jpeg images

    It would be good to have an ability to pass quality parameter when saving jpeg images.

    Or probably global variable like: Disk.jpegCompressionQuality = 0.95

    This would save half of disk space

    enhancement 
    opened by dannydaddy3 1
  • Using alternates besides JSONDecoder for deserialization

    Using alternates besides JSONDecoder for deserialization

    ZippyJSON (a framework that I created), is a 3-4x faster version of JSONDecoder. Using it could be a big speed boost for consumers. However, the customization provided by https://github.com/saoudrizwan/Disk/pull/28 doesn't allow for anything besides JSONDecoder (or a subclass of it). This makes it difficult to use ZippyJSON because alternate implementations are best not to subclass it to ensure good performance. Here are some possibilities:

    • Allow users to pass in anything that implements TopLevelDecoder, and not just JSONDecoder
    • Mention performance wins from using a custom implementation like ZippyJSON
    • Simply use ZippyJSON behind the scenes by default
    enhancement 
    opened by michaeleisel 2
  • Return an optional url with discardable result

    Return an optional url with discardable result

    I love Disk! I'd like to use this with my existing API, but I need the url where the file was stored. No worries if this doesn't go in, but it would be nice for me to have this.

    opened by kkendall33 0
Owner
Saoud Rizwan
Full Stack & Mobile Engineer
Saoud Rizwan
A lightweight generic cache for iOS written in Swift with extra love for images.

Haneke is a lightweight generic cache for iOS and tvOS written in Swift 4. It's designed to be super-simple to use. Here's how you would initalize a J

Haneke 5.2k Dec 29, 2022
MrCode is a simple GitHub iPhone App that can cache Markdown content (include images in HTML) for read it later.

MrCode is a simple GitHub iPhone App that can cache Markdown content (include images in HTML) for read it later.

hao 448 Dec 19, 2022
Cachyr A typesafe key-value data cache for iOS, macOS, tvOS and watchOS written in Swift.

Cachyr A typesafe key-value data cache for iOS, macOS, tvOS and watchOS written in Swift. There already exists plenty of cache solutions, so why creat

Norsk rikskringkasting (NRK) 124 Nov 24, 2022
Vellum is local persistent data storage for iOS

Vellum Requirements Swift 5.0 or higher iOS 9.3 or higher Installation Cocoapods Vellum is available through CocoaPods. To install it, simply add the

Nayanda Haberty 16 Dec 8, 2021
High performance cache framework for iOS.

YYCache High performance cache framework for iOS. (It's a component of YYKit) Performance You may download and compile the latest version of sqlite an

null 2.3k Dec 16, 2022
A library and tool for interacting with both the local and remote asset caches.

Asset Cache Tool A library and tool for interacting with both the local and remote asset caches. This is based on research I did a few years ago on th

Kenneth Endfinger 20 Dec 23, 2022
Carlos - A simple but flexible cache, written in Swift for iOS 13+ and WatchOS 6 apps.

Carlos A simple but flexible cache, written in Swift for iOS 13+ and WatchOS 6 apps. Breaking Changes Carlos 1.0.0 has been migrated from PiedPiper de

National Media & Tech 628 Dec 3, 2022
Everyone tries to implement a cache at some point in their iOS app’s lifecycle, and this is ours.

Everyone tries to implement a cache at some point in their app’s lifecycle, and this is ours. This is a library that allows people to cache NSData wit

Spotify 1.2k Dec 28, 2022
Fast, non-deadlocking parallel object cache for iOS, tvOS and OS X

PINCache Fast, non-deadlocking parallel object cache for iOS and OS X. PINCache is a fork of TMCache re-architected to fix issues with deadlocking cau

Pinterest 2.6k Dec 28, 2022
WireGuard for iOS and macOS

WireGuard for iOS and macOS This project contains an application for iOS and for macOS, as well as many components shared between the two of them. You

WireGuard 608 Dec 28, 2022
CachyKit - A Caching Library is written in Swift that can cache JSON, Image, Zip or AnyObject with expiry date/TTYL and force refresh.

Nice threadsafe expirable cache management that can cache any object. Supports fetching from server, single object expire date, UIImageView loading etc.

Sadman Samee 122 Dec 28, 2022
Track is a thread safe cache write by Swift. Composed of DiskCache and MemoryCache which support LRU.

Track is a thread safe cache write by Swift. Composed of DiskCache and MemoryCache which support LRU. Features Thread safe: Implement by dispatch_sema

Cheer 268 Nov 21, 2022
A caching and consistency solution for immutable models.

?? Data Rocket Data is a model management system with persistence for immutable models. Motivation Immutability has many benefits, but keeping models

Peter Livesey 652 Nov 11, 2022
🏈 Cache CocoaPods for faster rebuild and indexing Xcode project.

Motivation Working on a project with a huge amount of pods I had some troubles: - Slow and unnecessary indexing of pods targets, which implementation

Vyacheslav Khorkov 487 Jan 5, 2023
A repository for showcasing my knowledge of the Objective-C++ programming language, and continuing to learn the language.

Learning Objective-C-Plus-Plus I hardly know anything about the Objective-C++ programming language. This document will go over all of my knowledge of

Sean P. Myrick V19.1.7.2 3 Nov 8, 2022
A repository for showcasing my knowledge of the Objective-C programming language, and continuing to learn the language.

Learning Objective-C I hardly know anything about the Objective-C programming language. This document will go over all of my knowledge of the Objectiv

Sean P. Myrick V19.1.7.2 3 Nov 8, 2022
Rock - Paper - Scissors game. CPU gives you a sign and asks to win or lose your move. Than you have to decide witch sign do you choose to score a point

RockPaperScissors 2nd challange from HackingWithSwift.com. The CPU gives you a sign (rock, paper or scissors) and asks you either to win or to lose th

Pavel Surový 0 Nov 27, 2021
Collect payments with iPhone, Apple Watch, and Siri using Apple Pay

Offering Apple Pay in Your App Collect payments with iPhone, Apple Watch, and Si

Edgar Papyan 4 Dec 14, 2021
Periodum-apple - iPad and macOS client for periodum.com

Periodum  iPad and macOS client for periodum.com

Umur Gedik 1 Jan 31, 2022