Skip to content

Commit

Permalink
initial project commit for public version
Browse files Browse the repository at this point in the history
  • Loading branch information
gligorkot committed Oct 12, 2018
1 parent c882bc1 commit 3266af4
Show file tree
Hide file tree
Showing 36 changed files with 2,836 additions and 0 deletions.
69 changes: 69 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
Podfile.lock

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
*.DS_Store
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4.2
20 changes: 20 additions & 0 deletions Classes/Configuration/EnvironmentConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// EnvironmentConfiguration.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

final class EnvironmentConfiguration: StorageKitConfigurationProtocol {

private let _storage: StorageProtocol

init(storage: StorageProtocol) {
self._storage = storage
}

var storage: StorageProtocol {
return _storage
}
}
37 changes: 37 additions & 0 deletions Classes/Configuration/StorageKitConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// StorageKitConfiguration.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

final class StorageKitConfiguration {

private static let instance = StorageKitConfiguration()

class var shared: StorageKitConfigurationProtocol {
return StorageKitConfiguration.instance.configuration
}

private init() {}

private var config: StorageKitConfigurationProtocol?

private var configuration: StorageKitConfigurationProtocol {
if let config = config {
return config
} else {
preconditionFailure("GKStorageKit configuration must be setup before use")
}
}

class func setup(with config: StorageKitConfigurationProtocol) {
StorageKitConfiguration.instance.config = config
}

class func tearDownConfig() {
StorageKitConfiguration.instance.config = nil
}

}
19 changes: 19 additions & 0 deletions Classes/Configuration/StorageKitConfigurationProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// StorageKitConfigurationProtocol.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

protocol StorageKitConfigurationProtocol {
var storage: StorageProtocol { get }
}

/// every time a new interface is added above, it needs to be returned in the default implementation below
protocol StorageKitDecorator: StorageKitConfigurationProtocol {}
extension StorageKitDecorator {
var storage: StorageProtocol {
return StorageKitConfiguration.shared.storage
}
}
62 changes: 62 additions & 0 deletions Classes/Core/StorageError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// StorageError.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

import GKBaseKit

enum StorageError: BaseError {

case saveStringError(String, String)
case saveObjectError(Codable, String)
case saveFileError
case getError(String)
case deleteError(String)
case cleanStorageError
case cleanFileStorageError

func toResponse() -> ErrorResponse {
return StorageErrorContentAdapter.adapt(self)
}

}

private struct StorageErrorResponse: ErrorResponse {

let title: String
let message: String
let debugDescription: String

init(title: String = "Storage", message: String = "Something went wrong.", debugDescription: String = "") {
self.title = title
self.message = message
self.debugDescription = debugDescription
}
}

private final class StorageErrorContentAdapter: BaseErrorContentAdapter {
typealias ErrorType = StorageError

static func adapt(_ error: StorageError) -> ErrorResponse {
switch error {
case .saveStringError,
.saveObjectError:
return StorageErrorResponse(message: "Cannot save secure value. Keychain inaccessible.", debugDescription: "Cannot save secure value. Keychain inaccessible.")
case .saveFileError:
return StorageErrorResponse(message: "Unable to save file to documents directory.", debugDescription: "Unable to save file to documents directory.")
case .getError:
return StorageErrorResponse(message: "Cannot retrieve secure value. Keychain inaccessible.", debugDescription: "Cannot retrieve secure value. Keychain inaccessible.")
case .deleteError:
return StorageErrorResponse(message: "Cannot remove secure value. Keychain inaccessible.", debugDescription: "Cannot remove secure value. Keychain inaccessible.")
case .cleanStorageError:
return StorageErrorResponse(message: "Cannot clean secure storage. Keychain inaccessible.", debugDescription: "Cannot clean secure storage. Keychain inaccessible.")
case .cleanFileStorageError:
return StorageErrorResponse(message: "Unable to clean file storage.", debugDescription: "Unable to clean file storage.")
}
}

}

32 changes: 32 additions & 0 deletions Classes/Default Impl/DefaultStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// DefaultStorage.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

import Valet

public final class DefaultStorage: StorageProtocol {

public init() {}

public var storageIdentifier: String {
return "defaultStorage"
}

public var secureStorage: SecureStorage {
return Valet.valet(with: Identifier(nonEmpty: storageIdentifier)!, accessibility: .whenUnlocked)
}

public var userDefaults: UserDefaults {
return UserDefaults(suiteName: storageIdentifier)!
}

public var fileStorageUrl: URL {
// documents storage is the default fileStorageUrl
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}

}
14 changes: 14 additions & 0 deletions Classes/Protocol/StorageProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// StorageProtocol.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

public protocol StorageProtocol {
var storageIdentifier: String { get }
var secureStorage: SecureStorage { get }
var userDefaults: UserDefaults { get }
var fileStorageUrl: URL { get }
}
21 changes: 21 additions & 0 deletions Classes/Storage Library/SecureStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// SecureStorage.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

import Valet

public protocol SecureStorage {
func canAccessKeychain() -> Bool
func set(object: Data, forKey key: String) -> Bool
func object(forKey key: String) -> Data?
func set(string: String, forKey key: String) -> Bool
func string(forKey key: String) -> String?
func removeObject(forKey key: String) -> Bool
func removeAllObjects() -> Bool
}

extension Valet: SecureStorage {}
51 changes: 51 additions & 0 deletions Classes/Storage/FileStorageService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// FileStorageService.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

import GKBaseKit

final class FileStorageService: FileStorageInterface {

private var documentsUrl: URL

init(documentsUrl: URL) {
self.documentsUrl = documentsUrl
}

func storeFile(_ fileData: Data, fileExtension: String, onSuccess: @escaping (URL) -> (), onFail: @escaping FailureBlock) {
DispatchQueue.global(qos: .userInitiated).async {
let fileName = UUID().uuidString
let destinationUrl = self.documentsUrl.appendingPathComponent("\(fileName).\(fileExtension)")

if let _ = try? fileData.write(to: destinationUrl, options: Data.WritingOptions.atomic) {
onSuccess(destinationUrl)
} else {
// unable to write to file error here
let error = StorageError.saveFileError
onFail(error.toResponse())
}
}
}

func cleanStorage(onSuccess: @escaping () -> (), onFail: @escaping FailureBlock) {
DispatchQueue.global(qos: .userInitiated).async {
do {
let filePaths = try FileManager.default.contentsOfDirectory(at: self.documentsUrl, includingPropertiesForKeys: nil, options: [])
for filePath in filePaths {
try FileManager.default.removeItem(at: filePath)
}
onSuccess()
} catch {
// unable to clear documents directory
let error = StorageError.cleanFileStorageError
onFail(error.toResponse())
}
}
}


}
62 changes: 62 additions & 0 deletions Classes/Storage/ObjectStorageService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// ObjectStorageService.swift
// GKStorageKit
//
// Created by Gligor Kotushevski on 20/03/17.
// Copyright © 2017 Gligor Kotushevski. All rights reserved.
//

import Foundation
import GKBaseKit

final class ObjectStorageService: ObjectStorageInterface, StorageKitDecorator {

private var objectStorage: UserDefaults

init(storage: UserDefaults) {
self.objectStorage = storage
}

func storeCollection<T: Codable>(_ collection: Array<T>, forKey key: String, onSuccess: () -> ()) {
objectStorage.set(collection.map({ try? JSONEncoder().encode($0) }), forKey: key)
onSuccess()
}

func getCollection<T: Codable>(forKey key: String, onSuccess: ([T]?) -> ()) {
if let dataArray = objectStorage.value(forKey: key) as? [Data] {
onSuccess(dataArray.map({ try! JSONDecoder().decode(T.self, from: $0) }))
} else {
onSuccess(nil)
}
}

func storeObject<T: Codable>(_ value: T, forKey key: String, onSuccess: () -> ()) {
objectStorage.set(try? JSONEncoder().encode(value), forKey: key)
onSuccess()
}

func getObject<T: Codable>(forKey key: String, onSuccess: (T?) -> ()) {
if let data = objectStorage.value(forKey: key) as? Data {
onSuccess(try? JSONDecoder().decode(T.self, from: data))
} else {
onSuccess(nil)
}
}

func removeValue(forKey key: String, onSuccess: () -> ()) {
objectStorage.removeObject(forKey: key)
onSuccess()
}

func cleanStorage(onSuccess: () -> ()) {
let dict = objectStorage.dictionaryRepresentation()
for key in dict.keys {
objectStorage.removeObject(forKey: key)
}
objectStorage.removeSuite(named: storage.storageIdentifier)
// force synchronize as we've seen the cache not being refreshed at times
let _ = objectStorage.synchronize()
onSuccess()
}

}
Loading

0 comments on commit 3266af4

Please sign in to comment.