WIP - Complete App refactoring
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// PersistenceInterface.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
// Created by Vincent Meilinger on 06.05.24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import KeychainSwift
|
||||
|
||||
|
||||
protocol CookbookInterface {
|
||||
/// A unique id of the interface. Used to associate recipes to their respective accounts.
|
||||
var id: String { get }
|
||||
}
|
||||
|
||||
protocol ReadInterface {
|
||||
/// Get either the full image or a thumbnail sized version.
|
||||
/// - Parameters:
|
||||
/// - id: The according recipe id.
|
||||
/// - size: The size of the image.
|
||||
/// - Returns: The image of the recipe with the specified id. A UserAlert if the request fails, otherwise nil.
|
||||
func getImage(
|
||||
id: String,
|
||||
size: RecipeImage.RecipeImageSize
|
||||
) async -> (UIImage?, UserAlert?)
|
||||
|
||||
/// Get all recipe stubs.
|
||||
/// - Returns: A list of all recipes.
|
||||
func getRecipeStubs(
|
||||
) async -> ([RecipeStub]?, UserAlert?)
|
||||
|
||||
/// Get the recipe with the specified id.
|
||||
/// - Parameters:
|
||||
/// - id: The recipe id.
|
||||
/// - Returns: The recipe if it exists. A UserAlert if the request fails.
|
||||
func getRecipe(
|
||||
id: String
|
||||
) async -> (Recipe?, UserAlert?)
|
||||
|
||||
/// Get all categories.
|
||||
/// - Returns: A list of categories. A UserAlert if the request fails.
|
||||
func getCategories(
|
||||
) async -> ([Category]?, UserAlert?)
|
||||
|
||||
/// Get all recipes of a specified category.
|
||||
/// - Parameters:
|
||||
/// - categoryName: The category name.
|
||||
/// - Returns: A list of recipes. A UserAlert if the request fails.
|
||||
func getRecipeStubsForCategory(
|
||||
named categoryName: String
|
||||
) async -> ([RecipeStub]?, UserAlert?)
|
||||
|
||||
/// Get all keywords/tags.
|
||||
/// - Returns: A list of tag strings. A UserAlert if the request fails.
|
||||
func getTags(
|
||||
) async -> ([RecipeKeyword]?, UserAlert?)
|
||||
|
||||
/// Get all recipes tagged with the specified keyword.
|
||||
/// - Parameters:
|
||||
/// - keyword: The keyword.
|
||||
/// - Returns: A list of recipes tagged with the specified keyword. A UserAlert if the request fails.
|
||||
func getRecipesTagged(
|
||||
keyword: String
|
||||
) async -> ([RecipeStub]?, UserAlert?)
|
||||
}
|
||||
|
||||
protocol WriteInterface {
|
||||
/// Post either the full image or a thumbnail sized version.
|
||||
/// - Parameters:
|
||||
/// - id: The according recipe id.
|
||||
/// - size: The size of the image.
|
||||
/// - Returns: A UserAlert if the request fails, otherwise nil.
|
||||
func postImage(
|
||||
id: String,
|
||||
image: UIImage,
|
||||
size: RecipeImage.RecipeImageSize
|
||||
) async -> (UserAlert?)
|
||||
|
||||
/// Create a new recipe.
|
||||
/// - Parameters:
|
||||
/// - Returns: A UserAlert if the request fails. Nil otherwise.
|
||||
func postRecipe(
|
||||
recipe: Recipe
|
||||
) async -> (UserAlert?)
|
||||
|
||||
/// Update an existing recipe with new entries.
|
||||
/// - Parameters:
|
||||
/// - recipe: The recipe.
|
||||
/// - Returns: A UserAlert if the request fails. Nil otherwise.
|
||||
func updateRecipe(
|
||||
recipe: Recipe
|
||||
) async -> (UserAlert?)
|
||||
|
||||
/// Delete the recipe with the specified id.
|
||||
/// - Parameters:
|
||||
/// - id: The recipe id.
|
||||
/// - Returns: A UserAlert if the request fails. Nil otherwise.
|
||||
func deleteRecipe(
|
||||
id: String
|
||||
) async -> (UserAlert?)
|
||||
|
||||
/// Rename an existing category.
|
||||
/// - Parameters:
|
||||
/// - categoryName: The name of the category to be renamed.
|
||||
/// - newName: The new category name.
|
||||
/// - Returns: A UserAlert if the request fails.
|
||||
func renameCategory(
|
||||
named categoryName: String,
|
||||
newName: String
|
||||
) async -> (UserAlert?)
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// LocalDataInterface.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
// Created by Vincent Meilinger on 07.05.24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class LocalDataInterface: CookbookInterface {
|
||||
var id: String
|
||||
|
||||
init(id: String) {
|
||||
self.id = id
|
||||
}
|
||||
|
||||
enum LocalDataPath {
|
||||
case recipeStubs(category: String),
|
||||
recipe(id: String),
|
||||
image(id: String, size: RecipeImage.RecipeImageSize),
|
||||
categories,
|
||||
keywords
|
||||
|
||||
var path: String {
|
||||
switch self {
|
||||
case .recipe(let id):
|
||||
"recipe_\(id).data"
|
||||
case .recipeStubs(let category):
|
||||
"recipes_\(category).data"
|
||||
case .image(let id, let size):
|
||||
if size == .FULL {
|
||||
"image_\(id).data"
|
||||
} else {
|
||||
"thumb_\(id).data"
|
||||
}
|
||||
case .categories:
|
||||
"categories.data"
|
||||
case .keywords:
|
||||
"keywords.data"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Local Read Interface
|
||||
|
||||
extension LocalDataInterface: ReadInterface {
|
||||
|
||||
func getImage(id: String, size: RecipeImage.RecipeImageSize) async -> (UIImage?, (any UserAlert)?) {
|
||||
guard let data: String = await load(path: .image(id: id, size: size)) else {
|
||||
return (nil, PersistenceAlert.LOAD_FAILED)
|
||||
}
|
||||
guard let dataDecoded = Data(base64Encoded: data) else { return (nil, PersistenceAlert.DECODING_FAILED) }
|
||||
if let image = UIImage(data: dataDecoded) {
|
||||
return (image, nil)
|
||||
}
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
func getRecipeStubs() async -> ([RecipeStub]?, (any UserAlert)?) {
|
||||
return (nil, PersistenceAlert.LOAD_FAILED)
|
||||
}
|
||||
|
||||
func getRecipe(id: String) async -> (Recipe?, (any UserAlert)?) {
|
||||
if let recipe: Recipe? = await load(path: LocalDataPath.recipe(id: id)) {
|
||||
return (recipe, nil)
|
||||
}
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
func getCategories() async -> ([Category]?, (any UserAlert)?) {
|
||||
return (await load(path: LocalDataPath.categories), nil)
|
||||
}
|
||||
|
||||
func getRecipeStubsForCategory(named categoryName: String) async -> ([RecipeStub]?, (any UserAlert)?) {
|
||||
if let stubs: [RecipeStub] = await load(path: .recipeStubs(category: categoryName)) {
|
||||
return (stubs, nil)
|
||||
}
|
||||
return (nil, PersistenceAlert.LOAD_FAILED)
|
||||
}
|
||||
|
||||
func getTags() async -> ([RecipeKeyword]?, (any UserAlert)?) {
|
||||
if let keywords: [RecipeKeyword] = await load(path: .keywords) {
|
||||
return (keywords, nil)
|
||||
}
|
||||
return (nil, PersistenceAlert.LOAD_FAILED)
|
||||
}
|
||||
|
||||
func getRecipesTagged(keyword: String) async -> ([RecipeStub]?, (any UserAlert)?) {
|
||||
return (nil, PersistenceAlert.LOAD_FAILED)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Local Write Interface
|
||||
|
||||
extension LocalDataInterface: WriteInterface {
|
||||
|
||||
func postImage(id: String, image: UIImage, size: RecipeImage.RecipeImageSize) async -> ((any UserAlert)?) {
|
||||
if let data = image.pngData() {
|
||||
await save(
|
||||
data,
|
||||
path: LocalDataPath.image(id: id, size: size)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func postRecipe(recipe: Recipe) async -> ((any UserAlert)?) {
|
||||
await save(recipe, path: LocalDataPath.recipe(id: recipe.id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRecipe(recipe: Recipe) async -> ((any UserAlert)?) {
|
||||
return await postRecipe(recipe: recipe)
|
||||
}
|
||||
|
||||
func deleteRecipe(id: String) async -> ((any UserAlert)?) {
|
||||
await delete(path: .recipe(id: id))
|
||||
return nil
|
||||
}
|
||||
|
||||
func renameCategory(named categoryName: String, newName: String) async -> ((any UserAlert)?) {
|
||||
guard let stubs: [RecipeStub] = await load(path: .recipeStubs(category: categoryName)) else {
|
||||
return PersistenceAlert.LOAD_FAILED
|
||||
}
|
||||
await delete(path: .recipeStubs(category: categoryName))
|
||||
await save(stubs, path: .recipeStubs(category: newName))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Local Data Interface Utils
|
||||
|
||||
extension LocalDataInterface {
|
||||
|
||||
func load<T: Codable>(path ldPath: LocalDataPath) async -> T? {
|
||||
do {
|
||||
return try await DataStore.shared.load(fromPath: ldPath.path)
|
||||
} catch (let error) {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func save<T: Codable>(_ object: T, path ldPath: LocalDataPath) async {
|
||||
await DataStore.shared.save(data: object, toPath: ldPath.path)
|
||||
}
|
||||
|
||||
func delete(path ldPath: LocalDataPath) async {
|
||||
DataStore.shared.delete(path: ldPath.path)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// NextcloudDataInterface.swift
|
||||
// Nextcloud Cookbook iOS Client
|
||||
//
|
||||
// Created by Vincent Meilinger on 07.05.24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class NextcloudDataInterface: CookbookInterface {
|
||||
var id: String
|
||||
|
||||
var auth: Authentication
|
||||
var api: CookbookApi.Type
|
||||
|
||||
init(auth: Authentication, version: String) {
|
||||
self.id = UUID().uuidString
|
||||
self.auth = auth
|
||||
switch version {
|
||||
case "1.0":
|
||||
self.api = CookbookApiV1.self
|
||||
default:
|
||||
self.api = CookbookApiV1.self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Nextcloud Read Interface
|
||||
extension NextcloudDataInterface: ReadInterface {
|
||||
|
||||
func getImage(id: String, size: RecipeImage.RecipeImageSize) async -> (UIImage?, UserAlert?) {
|
||||
let (image, error) = await api.getImage(auth: auth.token, id: id, size: size)
|
||||
if let image {
|
||||
return (image, nil)
|
||||
}
|
||||
return (nil, error)
|
||||
}
|
||||
|
||||
func getRecipeStubs() async -> ([RecipeStub]?, UserAlert?) {
|
||||
return await api.getRecipes(auth: auth.token)
|
||||
}
|
||||
|
||||
func getRecipe(id: String) async -> (Recipe?, UserAlert?) {
|
||||
return await api.getRecipe(auth: auth.token, id: id)
|
||||
}
|
||||
|
||||
func getCategories() async -> ([Category]?, UserAlert?) {
|
||||
return await api.getCategories(auth: auth.token)
|
||||
}
|
||||
|
||||
func getRecipeStubsForCategory(named categoryName: String) async -> ([RecipeStub]?, UserAlert?) {
|
||||
return await api.getCategory(
|
||||
auth: UserSettings.shared.authString,
|
||||
named: categoryName
|
||||
)
|
||||
}
|
||||
|
||||
func getTags() async -> ([RecipeKeyword]?, (any UserAlert)?) {
|
||||
return await api.getTags(auth: auth.token)
|
||||
}
|
||||
|
||||
func getRecipesTagged(keyword: String) async -> ([RecipeStub]?, UserAlert?) {
|
||||
return await api.getRecipesTagged(auth: auth.token, keyword: keyword)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Nextcloud Write Interface
|
||||
extension NextcloudDataInterface: WriteInterface {
|
||||
|
||||
func postImage(id: String, image: UIImage, size: RecipeImage.RecipeImageSize) async -> ((any UserAlert)?) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func postRecipe(recipe: Recipe) async -> (UserAlert?) {
|
||||
return await api.createRecipe(auth: auth.token, recipe: recipe)
|
||||
}
|
||||
|
||||
func updateRecipe(recipe: Recipe) async -> (UserAlert?) {
|
||||
return await api.updateRecipe(auth: auth.token, recipe: recipe)
|
||||
}
|
||||
|
||||
func deleteRecipe(id: String) async -> (UserAlert?) {
|
||||
return await api.deleteRecipe(auth: auth.token, id: id)
|
||||
}
|
||||
|
||||
func renameCategory(named categoryName: String, newName: String) async -> (UserAlert?) {
|
||||
return await api.renameCategory(auth: auth.token, named: categoryName, newName: newName)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user