Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
gal-yedidovich/SwiftExtensions
SwiftExtensions - Robust, Reusable & Secure utilities
This library provides helpful utilities, like IO operations on the local disk, with encryption layer for security. It also provides a convenice Prefs
class for storing Key-Value pairs easily and safely with the same encryption layer.
Installation
SwiftExtensions is a Swift Package.
Use the swift package manager to install SwiftExtensions on your project. Apple Developer Guide
BasicExtensions
lots of convenince utility functions
Navigation
//Navigation:
extension ControllerID {
static let myCtrl = ControllerID(value: "myCTRL") //make sure you have added "myCTRL" in the Storyboard
}
viewController.push(to: .myCtrl)
//OR with `config` block
viewController.present(.myCtrl) { (vc: MyViewController) in
//do any configuration before presenting "myCTRL", for example: settting instance variables
}
JSON Encoding with Codable protocol
struct MyType: Codable {
//values
}
let data = MyType(...).json() //convert to JSON encoded data
let instance: MyType = .from(json: data) //convert back to your type
Asynchronous block
post {
//run in the main thread
}
async {
//run in a background thread
}
URLRequest builder
let req = URLRequest(url: "https://your.end.point")
.set(method: .POST) //OR .get, .put, .delete, .patch
.set(contentType: .json) //OR .xml, .urlEncoded etc.
.set(body: "some String or Data")
Another example:
let dict = ["title": "Bubu is the king", "message": "I am Groot"]
let req = URLRequest(url: "https://your.end.point")
.set(method: .PUT)
.set(contentType: .json)
.set(body: dict.json()) //allows encodable values
Localization
let helloWorld = "helloWorld".localized //provided you have "helloWorld" key in Localizable.strings files"
print(helloWorld) //will automatically use the wanted localization
StorageExtensions
Convenience Read & Write operations with GCM Encryption
For easy, safe & scalable storage architecture, the FileSystem
class gives you the ability to read & write files with GCM encryption, implemented using Apple's CryptoKit Framework.
- IO (Read/Write) operations are synchronous, for more control over threading.
- You are are required to use the
Filename
orFolder
structs to state your desired files/folders. best used withextension
like so:
extension Filename {
static let myFile1 = Filename(value: "name1InFileSystem")
static let myFile2 = Filename(value: "name2InFileSystem")
}
//Usage
let data = Data("Bubu is the king".utf8)
do {
try FileSystem.write(data: data, to: .myFile1)
let sameData = try FileSystem.read(file: .myFile1)
print(String(decoding: sameData, as: UTF8.self)) //"Bubu is the king"
try FileSystem.delete(file: .myFile1)
} catch {
//Handle errors
}
TIP
when instantiating
Filename
orFolder
you can (and probably should) use obfusctated names, for exmaple: use "--" insated of "secret.info".
Storage Customizations
You are able to change some values in the library.
FileSystem.rootURL
: defaults to the documents url of the app, it can change for example to an AppGroup url:
FileSystem.rootURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "your.app.group")
FileSystem.encryptor
: controls the underlining SimpleEncryptor that handles cryptographics:
FileSystem.encryptor = SimpleEncryptor(strategy: .gcm)
Prefs - Secure Key-Value pairs in storage.
Insapired after iOS's UserDefaults
& Android's SharedPreferences
, The Prefs
class enables you to manage Key-Value pairs easily and securely using the same encryption layer from Encryptor
, also comes with a caching logic for fast & non blocking read/writes operation in memory.
You can either use the Standard instance, which is also using an obfuscated filename, or create your own instances for multiple files, like so:
let standardPrefs = Prefs.standard //the built-in standard instance
//OR
let myPrefs = Prefs(file: .myFile1) //new instance using the Filename struct
You can put values:
let myPrefs.edit() //start editing
.put(key: .name, "Bubu") //using the static constant '.name'
.commit() //save your changes in memory & lcoal storage
extension PrefKey {
static let name = PrefKey(value: "obfuscatedKey") //value should be obfuscated
}
And you can read them:
if let name = myPrefs.string(key: .name) {
print("\(name), is the king")
}
PrefsValue - Wrapped property for SwfitUI.
Wrapping a variable with @PrefsValue
allows to manage a single value transparently in the prefs
extension PrefKey {
static let displayName = PrefKey(value: "someObfuscatedKey")
}
struct ContentView: View {
@PrefsValue(key: .displayName) var displayName: String = ""
var body: some View {...}
}
Views will re-render when a @PrefsValue
changes
Result API - Conveneince extension in URLSession.
Using Swift's "Associated values" the following convenience methods allow you to handle responses easily without the boilerplate like unwrapping data and checking for errors.
You can easily get relevant values from response using a switch
statement.
let req: URLRequest = ...
URLSession.shared.dataTask(with: req) { (response: NetResponse<Data, Data>) in
switch response {
case .success(let data): //handle success (status 2##) with given data
case .failure(let statusCode, let data): //handle failure (ex: status 400) with given status+data
case .error(let error): //handle given error
}
}.resume()
You can use the generic overload, to automatically decode the response data.
struct MySuccessType: Codable {
//values
}
struct MyFailureType: Codable {
//values
}
//in this example we will create an API function
func someApi(completion: @escaping (NetResponse<MySuccessType, MyFailureType>) -> Void)
let req: URLRequest = ...
URLSession.shared.dataTask(with: req, completion: completion).resume()
}
someApi { response in //response is of type: NetResponse<MySuccessType, MyFailureType>
switch response {
case .success(let successPayload): //handle success, 'successPayload' is of type MySuccessType.
case .failure(_, let failurePayload): //handle failure, 'failurePayload' is of type MyFailureType. ignoring the status code
case .error(let error): //handle given error.
}
}
You can also customize your usage, example with someApi
that ignore some results and the associated payload
someApi { response in
switch response {
case .success: //handle success, but ignore the payload
default: //handle either failure or error, ignoring the payload
}
}
JsonObject & JsonArray - dynamic JSON structs
Conveniece structs for working with dynamic JSON.
//new JSON example
let json = JsonObject()
.with(key: "name", value: "Bubu")
.with(key: "age", value: 10)
do {
let data: Data = try json.data()
} catch {
//handle encoding error
}
//receiving JSON as `Data` from an API
do {
let json = JsonObject(data: dataFromApi) //given data from API
if let name = json.string(key: "name"),
let age = json.int(key: "age") {
print("name: \(name), age: \(age)")
}
} catch {
//handle decoding error
}
License
Apache License 2.0
Github
link |
Stars: 0 |
Last commit: 5 days ago |
Related Packages
You may find interesting
Releases
CryptoExtensions - 2021-02-21T13:21:18
Introducing a new module "CryptoExtensions", with new CryptoKit extension: AES.CBC
Braking Changes:
- Renamed
Encryptor
toSimpleEncryptor
SimpleEncryptor
is now a class- moved
SimpleEncryptor
to CryptoExtensions module - more
FileSystem
methods are now throwing