Skip to content

Instantly share code, notes, and snippets.

@dennda
Created May 5, 2016 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dennda/f93d150752b8e3307e47968ccf820594 to your computer and use it in GitHub Desktop.
Save dennda/f93d150752b8e3307e47968ccf820594 to your computer and use it in GitHub Desktop.
//
// FileClient.swift
// Flashcards
//
// Created by David Klionsky on 4/18/16.
// Copyright © 2016 Duolingo, Inc. All rights reserved.
//
import Foundation
/// Maybe these should be two separate protocols?
protocol Serializable {
func serialize() -> NSData?
static func deserialize<T where T: Serializable>(data: NSData) -> T?
}
struct UIImageSerializable: Serializable {
private let underlyingImage: UIImage
private let serializer: (UIImage) -> NSData?
func serialize() -> NSData? {
return serializer(underlyingImage)
}
static func deserialize<T where T : Serializable>(data: NSData) -> T? {
return UIImage(data: data) as? T
}
}
enum UIImageSerializationFormat {
case JPEG(quality: CGFloat)
}
extension UIImage {
func toSerializable(format: UIImageSerializationFormat) -> Serializable {
return UIImageSerializable(underlyingImage: self, serializer: { image in
switch format {
case .JPEG(let quality):
return UIImageJPEGRepresentation(image, quality)
}
})
}
}
/// Backwards compat with flashcards:
extension UIImage: Serializable {
func serialize() -> NSData? {
return toSerializable(.JPEG(quality: 0.75)).serialize()
}
static func deserialize<T where T : Serializable>(data: NSData) -> T? {
return UIImage(data: data) as? T
}
}
class FileClient {
private let baseDirectory: String
private var baseDirectoryPath: String {
get {
let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
return (documentsDirectory as NSString).stringByAppendingPathComponent(baseDirectory)
}
}
init(baseDirectory: String) {
self.baseDirectory = baseDirectory
let manager = NSFileManager.defaultManager()
let path = baseDirectoryPath
if !manager.fileExistsAtPath(path) {
do {
LogInfo("Creating directory at \(path)")
try manager.createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
} catch {
LogError("Couldn't create directory at \(path): \(error)")
}
/*
Add an attribute to the base directory that prevents it from being backed up to iCloud and iTunes.
Content that is not user generated and shouldn't be backed up is typically saved in either the
<Application home>/tmp directory or in the /Library/caches directory, but these may be destroyed under
low memory conditions or app exit. If app-generated content must persist in order for the app to work,
you should mark it with a "do not back up" attribute.
See https://developer.apple.com/library/ios/qa/qa1719/_index.html
*/
do {
let url = NSURL(fileURLWithPath: path, isDirectory: true)
try url.setResourceValue(true, forKey: NSURLIsExcludedFromBackupKey)
} catch {
LogError("Couldn't set backup attribute for \(path): \(error)")
}
}
}
private func getPathForFilename(filename: String) -> String {
assert(filename != "")
return (baseDirectoryPath as NSString).stringByAppendingPathComponent(filename)
}
func fileExists(filename: String) -> Bool {
let path = getPathForFilename(filename)
return NSFileManager.defaultManager().fileExistsAtPath(path)
}
func deleteFileWithName(filename: String) {
let path = getPathForFilename(filename)
do {
try NSFileManager.defaultManager().removeItemAtPath(path)
} catch {
LogError("Couldn't delete file at \(path): \(error)")
}
}
// MARK: - Data
func saveData(data: NSData, filename: String) -> Bool {
let path = getPathForFilename(filename)
return data.writeToFile(path, atomically: true)
}
func loadDataWithFilename(filename: String) -> NSData? {
let path = getPathForFilename(filename)
return NSData(contentsOfFile: path)
}
// MARK: - JSON
func saveJSON(json: JSON, filename: String) -> Bool {
do {
let data = try NSJSONSerialization.dataWithJSONObject(json, options: [])
return saveData(data, filename: filename)
} catch {
LogError("Couldn't JSON serialize object: \(json), \(error)")
return false
}
}
func loadJSONWithFilename(filename: String) -> JSON? {
guard let data = loadDataWithFilename(filename) else {
return nil
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? JSON {
return json
} else {
LogError("Deserialized data but was not JSON type.")
}
} catch {
LogError("Couldn't deserialize data at \(filename) into JSON: \(error)")
}
return nil
}
// MARK: - Serializable
func save(serializable: Serializable, filename: String) -> Bool {
if let data = serializable.serialize() {
return saveData(data, filename: filename)
}
return false
}
func load<T where T: Serializable>(filename filename: String) -> T? {
guard let data = loadDataWithFilename(filename) else {
LogError("Couldn't load image data at \(filename)")
return nil
}
return T.deserialize(data)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment