iOS
...
Data Objects
Manipulación de Datos en Swift con ParseSwift
16 min
analizar tipos de datos en swift introducción al guardar datos en una base de datos de back4app, cada entidad se almacena en un formato de par clave valor el tipo de dato para el campo de valor va desde los fundamentales (como string , int , double , float , y bool ) hasta estructuras más complejas el principal requisito para almacenar datos en una base de datos de back4app es que la entidad debe conformarse al parseswift protocolo a su vez, este protocolo proporciona un conjunto de métodos para almacenar, actualizar y eliminar cualquier instancia de una entidad en esta guía, aprenderás cómo crear y configurar una entidad para guardarla en tu base de datos de back4app en el ejemplo del proyecto, la entidad que estamos almacenando contiene información sobre una receta este tutorial utiliza una aplicación básica creada en xcode 12 y ios 14 en cualquier momento, puedes acceder al proyecto completo a través de nuestros repositorios de github repositorio de ejemplo de ios objetivo para entender cómo se analizan y almacenan los objetos en una base de datos de back4app requisitos previos para completar este inicio rápido, necesitas xcode una aplicación creada en back4app sigue el tutorial de nueva aplicación parse para aprender cómo crear una aplicación parse en back4app nota sigue el tutorial de instalación del sdk de parse (swift) para crear un proyecto de xcode conectado a back4app entendiendo nuestra aplicación de recetas la funcionalidad de la aplicación se basa en un formulario donde se puede ingresar información sobre una receta dependiendo de la información, el tipo de datos puede variar en nuestro ejemplo, la receta tiene las siguientes características campo tipo de dato descripción nombre cadena nombre de la receta porciones int número de porciones disponible bool determina si la receta está disponible o no categoría categoría una enumeración personalizada que clasifica una receta en tres categorías desayuno, almuerzo y cena ingredientes \[ingredientes] el conjunto de ingredientes encerrados en una estructura customingredient opciones laterales \[cadena] nombres de las opciones adicionales con las que viene la receta información nutricional \[cadena\ cadena] un diccionario que contiene información sobre el contenido nutricional de la receta fecha de lanzamiento fecha una fecha que muestra cuándo estaba disponible la receta además, hay más tipos de datos que se utilizan para implementar la funcionalidad de la base de datos, como la relación entre objetos estos tipos de datos no se cubren en este tutorial referencia rápida de los comandos que vamos a usar dado un objeto, digamos receta, si deseas guardarlo en una base de datos de back4app, primero debes hacer que este objeto cumpla con el protocolo parseswift (disponible a través del sdk de parseswift) 1 import foundation 2 import parseswift 3 4 struct recipe parseobject { 5 /// enumeration for the recipe category 6 enum category int, caseiterable, codable { 7 case breakfast = 0, lunch = 1, dinner = 2 8 9 var title string { 10 switch self { 11 case breakfast return "breakfast" 12 case lunch return "lunch" 13 case dinner return "dinner" 14 } 15 } 16 } 17 18 19 20 /// a string type property 21 var name string? 22 23 /// an integer type property 24 var servings int? 25 26 /// a double (or float ) type property 27 var price double? 28 29 /// a boolean type property 30 var isavailable bool? 31 32 /// an enumeration type property 33 var category category? 34 35 /// an array of structs 36 var ingredients \[ingredient] 37 38 /// an array of strings 39 var sideoptions \[string] 40 41 /// a dictionary property 42 var nutritionalinfo \[string string] 43 44 /// a date type property 45 var releasedate date? 46 } antes de almacenar instancias de este objeto en una base de datos de back4app, todas sus propiedades deben cumplir con los protocolos codable y hashable hacemos uso de los siguientes métodos para gestionar estos objetos en la base de datos de back4app create //the procedure for reading and updating a recipe object is similar since they rely on the save() method how a recipe is instantiated determines if we are creating or updating the object on the back4app database when creating a new instance we use 1 var newrecipe recipe 2 3 // setup newrecipe's properties 4 newrecipe name = "my recipe's name" 5 newrecipe servings = 4 6 newrecipe price = 3 99 7 newrecipe isavailable = false 8 newrecipe category = breakfast 9 newrecipe sideoptions = \["juice"] 10 newrecipe releasedate = date() 11 12 13 // saves newrecipe on your back4app database synchronously and returns the new saved item it throws and error if something went wrong 14 let savedrecipe = try? savedrecipe save() 15 16 // saves savedrecipe on your back4app database asynchronously, and passes a result\<todolistitem, parseerror> object to the completion block to handle the save proccess 17 savedrecipe save { result in 18 // handle the result to check the save was successfull or not 19 } update //and to update an existing instance, we have to provide the objectid value which identifies the the object on the back4app database a satandard update can be implemented in the following way 1 let recipetoupdate = recipe(objectid "object id") 2 3 // update the properties you need 4 recipetoupdate name = "my updated recipe's name" 5 recipetoupdate servings = 5 6 recipetoupdate price = 5 99 7 recipetoupdate isavailable = true 8 recipetoupdate category = lunch 9 recipetoupdate sideoptions = \["juice", "coffee"] 10 recipetoupdate releasedate = date() addingtimeinterval(3600 24) 11 12 13 // save changes synchronousty 14 try? recipetoupdate save() 15 16 // or save changes asynchronously 17 recipetoupdate save { result in 18 // handle the result 19 } read //for reading objects stored on your back4app database, recipe now provides the query() static method which returns a query\<recipe> this query object can be constructed using one or more queryconstraint objects in the following way 1 let query = recipe query() // a query to fetch all recipe items on your back4app database 2 let query = recipe query("name" == "omelette") // a query to fetch all recipe items with name "omelette" on your back4app database 3 let query = recipe query(\["name" == "omelette", "price" = 9 99]) // a query to fetch all recipe items with name = "omelette" and price = 9 99 4 5 // fetches the items synchronously or throws an error if found 6 let fetchedrecipes = try? query find() 7 8 // fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation 9 query find { result in 10 // handle the result 11 } delete //any deletion process is performed by calling the method delete() on the object to be deleted 1 var recipetodelete recipe 2 3 // delete recipetodelete synchronously 4 try? recipetodelete delete() 5 6 // delete recipetodelete asynchronously 7 recipetodelete delete { result in 8 // handle the result 9 } 1 crear la plantilla de la aplicación de recetas comenzamos creando un nuevo proyecto en xcode este tutorial debería verse así en cualquier momento, puedes acceder al proyecto completo a través de nuestros repositorios de github repositorio de ejemplo de ios ve a xcode y encuentra el archivo scenedelegate swift para agregar una barra de navegación en la parte superior de la aplicación, configuramos un uinavigationcontroller como el controlador de vista raíz de la siguiente manera 1 class scenedelegate uiresponder, uiwindowscenedelegate { 2 3 var window uiwindow? 4 5 func scene( scene uiscene, willconnectto session uiscenesession, options connectionoptions uiscene connectionoptions) { 6 guard let scene = (scene as? uiwindowscene) else { return } 7 8 window = init(windowscene scene) 9 window? rootviewcontroller = uinavigationcontroller(rootviewcontroller recipescontroller()) 10 window? makekeyandvisible() 11 12 // additional logic 13 } 14 15 16 } la clase del controlador de vista raíz (recipescontroller) para el controlador de navegación es una subclase de uiviewcontroller en la que dispondremos un formulario para crear y actualizar objetos de receta en la base de datos de back4app 2 configurar el objeto receta los objetos que deseas guardar en tu base de datos de back4app deben conformarse al protocolo parseobject en nuestra aplicación de recetas, este objeto es receta por lo tanto, primero necesitas crear este objeto crea un nuevo archivo recipe swift y agrega lo siguiente 1 import foundation 2 import parseswift 3 4 struct recipe parseobject { 5 /// enumeration for the recipe category 6 enum category int, caseiterable, codable { 7 case breakfast = 0, lunch = 1, dinner = 2 8 9 var title string { 10 switch self { 11 case breakfast return "breakfast" 12 case lunch return "lunch" 13 case dinner return "dinner" 14 } 15 } 16 } 17 18 // required properties from parseobject protocol 19 var objectid string? 20 var createdat date? 21 var updatedat date? 22 var acl parseacl? 23 24 /// a string type property 25 var name string? 26 27 /// an integer type property 28 var servings int? 29 30 /// a double (or float ) type property 31 var price double? 32 33 /// a boolean type property 34 var isavailable bool? 35 36 /// an enumeration type property 37 var category category? 38 39 /// an array of structs 40 var ingredients \[ingredient] 41 42 /// an array of strings 43 var sideoptions \[string] 44 45 /// a dictionary property 46 var nutritionalinfo \[string string] 47 48 /// a date type property 49 var releasedate date? 50 51 /// maps the nutritionalinfo property into an array of tuples 52 func nutritionalinfoarray() > \[(name string, value string)] { 53 return nutritionalinfo map { ($0 key, $0 value) } 54 } 55 } donde ya hemos agregado todas las propiedades necesarias a receta de acuerdo con la tabla de características de las recetas el tipo de dato ingredient es una estructura que contiene la cantidad y la descripción del ingrediente como se mencionó anteriormente, este tipo de dato debe conformarse a los protocolos codable y hashable para ser parte de las propiedades de recipe 1 import foundation 2 3 struct ingredient hashable, codable { 4 var quantity float 5 var description string 6 } además, la propiedad category en recipe tiene una enumeración (category) como tipo de dato que también cumple con los protocolos correspondientes 1 struct recipe parseobject { 2 /// enumeration for the recipe category 3 enum category int, caseiterable, codable { 4 case breakfast = 0, lunch = 1, dinner = 2 5 6 7 } 8 9 } 3 configurando recipescontroller en recipescontroller debemos implementar toda la configuración necesaria para la navigationbar y el formulario utilizado para capturar todas las propiedades de recipe este tutorial no cubre cómo implementar el diseño del formulario luego nos enfocamos en la lógica relacionada con la gestión de tipos de datos utilizando el sdk de parseswift a continuación, destacamos los puntos clave en recipescontroller que nos permiten entender cómo implementamos la conexión entre la interfaz de usuario y los datos que provienen de su base de datos back4app 1 class recipescontroller uiviewcontroller { 2 enum previousnext int { case previous = 0, next = 1 } 3 4 5 6 var recipes \[recipe] = \[] // 1 an array of recipes fetched from your back4app database 7 8 // section header labels 9 private let recipelabel uilabel = titlelabel(title "recipe overview") 10 private let ingredientslabel uilabel = titlelabel(title "ingredients") 11 private let nutritionalinfolabel uilabel = titlelabel(title "nutritional information") 12 13 // 2 a custom view containing input fields to enter the recipe's information (except nutritional info and ingredients) 14 let recipeoverviewview recipeinfoview 15 16 // 3 a stack view containig the fields to enter the recipe's ingredients 17 let ingredientsstackview uistackview 18 19 // 4 a stack view containig the fields to enter the nutritional information 20 let nutritionalinfostackview uistackview 21 22 // 5 buttons to handle the crud logic for the recipe object currently displayed 23 private var savebutton uibutton = uibutton(title "save") 24 private var updatebutton uibutton = uibutton(title "update") 25 private var reloadbutton uibutton = uibutton(title "reload") 26 27 var currentrecipeindex int? // 6 an integer containing the index of the current recipe presenten from the recipes property 28 29 override func viewdidload() { 30 super viewdidload() 31 setupnavigationbar() 32 setupviews() 33 } 34 35 override func viewdidappear( animated bool) { 36 super viewdidappear(animated) 37 handlereloadrecipes() 38 } 39 40 private func setupnavigationbar() { 41 navigationcontroller? navigationbar bartintcolor = primary 42 navigationcontroller? navigationbar titletextattributes = \[ foregroundcolor uicolor white] 43 navigationcontroller? navigationbar istranslucent = false 44 navigationcontroller? navigationbar barstyle = black 45 navigationitem title = "parse data types" uppercased() 46 } 47 48 private func setupviews() { 49 // see the project example for more details 50 51 savebutton addtarget(self, action #selector(handlesaverecipe), for touchupinside) 52 updatebutton addtarget(self, action #selector(handleupdaterecipe), for touchupinside) 53 reloadbutton addtarget(self, action #selector(handlereloadrecipes), for touchupinside) 54 } 55 56 57 } 3 manejo de la entrada del usuario y análisis de un objeto receta en un archivo separado (llamado recipescontroller+parseswiftlogic swift), utilizando una extensión ahora implementamos los métodos handlesaverecipe(), handleupdaterecipe() y handleupdaterecipe() para manejar los datos de entrada 1 import uikit 2 import parseswift 3 4 extension recipescontroller { 5 /// retrieves all the recipes stored on your back4app database 6 @objc func handlereloadrecipes() { 7 view\ endediting(true) 8 let query = recipe query() 9 query find { \[weak self] result in // retrieves all the recipes stored on your back4app database and refreshes the ui acordingly 10 guard let self = self else { return } 11 switch result { 12 case success(let recipes) 13 self recipes = recipes 14 self currentrecipeindex = recipes isempty ? nil 0 15 self setuprecipenavigation() 16 17 dispatchqueue main async { self presentcurrentrecipe() } 18 case failure(let error) 19 dispatchqueue main async { self showalert(title "error", message error message) } 20 } 21 } 22 } 23 24 /// called when the user wants to update the information of the currently displayed recipe 25 @objc func handleupdaterecipe() { 26 view\ endediting(true) 27 guard let recipe = preparerecipemetadata(), recipe objectid != nil else { // prepares the recipe object for updating 28 return showalert(title "error", message "recipe not found ") 29 } 30 31 recipe save { \[weak self] result in 32 switch result { 33 case success(let newrecipe) 34 self? recipes append(newrecipe) 35 self? showalert(title "success", message "recipe saved on your back4app database! (objectid \\(newrecipe id)") 36 case failure(let error) 37 self? showalert(title "error", message "failedto save recipe \\(error message)") 38 } 39 } 40 } 41 42 /// saves the currently displayed recipe on your back4app database 43 @objc func handlesaverecipe() { 44 view\ endediting(true) 45 guard var recipe = preparerecipemetadata() else { // prepares the recipe object for storing 46 return showalert(title "error", message "failed to retrieve all the recipe fields ") 47 } 48 49 recipe objectid = nil // when saving a recipe object, we ensure it will be a new instance of it 50 recipe save { \[weak self] result in 51 switch result { 52 case success(let newrecipe) 53 if let index = self? currentrecipeindex { self? recipes\[index] = newrecipe } 54 self? showalert(title "success", message "recipe saved on your back4app database! (objectid \\(newrecipe id))") 55 case failure(let error) 56 self? showalert(title "error", message "failed to save recipe \\(error message)") 57 } 58 } 59 } 60 61 /// when called it refreshes the ui according to the content of recipes and currentrecipeindex properties 62 private func presentcurrentrecipe() { 63 64 } 65 66 /// adds the 'next recipe' and 'previous recipe' button on the navigation bar these are used to iterate over all the recipes retreived from your back4app database 67 private func setuprecipenavigation() { 68 69 } 70 71 /// reads the information the user entered via the form and returns it as a recipe object 72 private func preparerecipemetadata() > recipe? { 73 let ingredientscount = ingredientsstackview\ arrangedsubviews count 74 let nutritionalinfocount = nutritionalinfostackview\ arrangedsubviews count 75 76 let ingredients \[ingredient] = (0 \<ingredientscount) compactmap { row in 77 guard let textfields = ingredientsstackview\ arrangedsubviews\[row] as? doubletextfield, 78 let quantitystring = textfields primarytext, 79 let quantity = float(quantitystring), 80 let description = textfields secondarytext 81 else { 82 return nil 83 } 84 return ingredient(quantity quantity, description description) 85 } 86 87 var nutritionalinfo \[string string] = \[ ] 88 89 (0 \<nutritionalinfocount) foreach { row in 90 guard let textfields = nutritionalinfostackview\ arrangedsubviews\[row] as? doubletextfield, 91 let content = textfields primarytext, !content isempty, 92 let value = textfields secondarytext, !value isempty 93 else { 94 return 95 } 96 nutritionalinfo\[content] = value 97 } 98 99 let recipeinfo = recipeoverviewview\ parseinputtorecipe() // reads all the remaining fields from the form (name, category, price, serving, etc) and returns them as a tuple 100 101 // we collect all the information the user entered and create an instance of recipe 102 // the recipeinfo objectid will be nil if the currently displayed information does not correspond to a recipe already saved on your back4app database 103 let newrecipe recipe = recipe( 104 objectid recipeinfo objectid, 105 name recipeinfo name, 106 servings recipeinfo servings, 107 price recipeinfo price, 108 isavailable recipeinfo isavailable, 109 category recipeinfo category, 110 ingredients ingredients, 111 sideoptions recipeinfo sideoptions, 112 nutritionalinfo nutritionalinfo, 113 releasedate recipeinfo releasedate 114 ) 115 116 return newrecipe 117 } 118 119 /// called when the user presses the 'previous recipe' or 'next recipe' button 120 @objc private func handleswitchrecipe(button uibarbuttonitem) { 121 122 } 123 } 4 ¡ejecuta la aplicación! ¡antes de presionar el botón de ejecutar en xcode, no olvides configurar tu aplicación de back4app en la clase appdelegate! la primera vez que ejecutes el proyecto deberías ver algo como esto en el simulador (con todos los campos vacíos) ahora puedes comenzar a ingresar una receta para luego guardarla en tu base de datos de back4app una vez que hayas guardado una receta, ve a tu tablero de back4app https //parse dashboard back4app com/apps y ve a tu aplicación, en la sección de base de datos encontrarás la clase receta donde estarán todas las recetas creadas por la aplicación de ios en particular, vale la pena señalar cómo se almacenan tipos de datos no fundamentales como ingrediente, receta categoría o diccionarios si navegas a través de los datos guardados bajo la clase receta, encontrarás que el diccionario nutritionalinformation se almacena como un objeto json el array \[ingredientes] se almacena como un array de objetos json la enumeración receta categoría, dado que tiene un tipo de dato entero como rawvalue, se transforma en un tipo de valor número la propiedad releasedate, un valor de tipo fecha en swift , también se almacena como un valor de tipo fecha para concluir, al recuperar datos de tu base de datos de back4app, no necesitas decodificar todos estos campos manualmente, el sdk de parseswift los decodifica automáticamente eso significa que, al crear una consulta (query\<receta> en este caso) para recuperar datos, el método query find() analizará todos los tipos de datos y objetos json para devolver un array de recetas, no hay un procedimiento de análisis adicional que implementar