iOS
...
Data Objects
使用ParseSwift实现iOS对象关系与关联方法指南
15 分
关系 介绍 关系是组织存储在数据库中的数据对象的基本特征。 parseswift 提供了在您的 back4app 数据库中建立类之间关系所需的工具和方法。根据用例,我们可以识别以下类型的关系 1 1 仅连接两个数据对象的关系。 1\ n 一个数据对象与 n n 数据对象之间的关系 n\ n 一个数据对象与 n n 数据对象之间的关系到 n n 数据对象。 正如我们下面所看到的,实施 1 1 关系相对简单。对于 1\ n 和 n\ n 关系,实施涉及到 parserelation parserelation 对象,由 parseswift sdk 提供。还有其他替代方案来实现 1\ n 和 n\ n 关系。然而,由于效率和性能,我们建议遵循我们的方法 本教程使用在 xcode 12 中创建的基本应用程序和 ios 14 。 您可以随时通过我们的 github 存储库访问完整的项目。 ios 示例存储库 目标 了解如何在 back4app 数据库中实现关系。 先决条件 要完成此快速入门,您需要: xcode。 在 back4app 创建的应用程序。 请遵循 新 parse 应用程序教程 以了解如何在 back4app 创建 parse 应用程序。 注意: 请遵循 安装 parse sdk (swift) 教程 以创建一个连接到 back4app 的 xcode 项目。 了解我们的图书应用 项目模板是一个图书应用,用户输入书籍详细信息以将其保存在 back4app 数据库中。在应用的主屏幕上,您将找到填写表单的地方。 使用 + + 按钮位于导航栏的右上角,我们可以添加任意数量的 出版商 , 类型 和 作者 。一旦用户输入一本书,他们可以使用 添加书籍 添加书籍 按钮将书籍保存到他们的 back4app 数据库中。此外, 列出书籍 列出书籍 按钮将允许我们显示用户添加的所有书籍,并查看它们与出版商和作者的关系。 我们将要使用的命令快速参考 我们使用的对象有 作者 作者 , 出版商 出版商 , isbn isbn 和 书籍 书籍 genre 1 import foundation 2 import parseswift 3 4 struct genre parseobject { 5 6 7 var name string? 8 9 10 } author 1 import foundation 2 import parseswift 3 4 struct author parseobject { 5 6 7 var name string? 8 9 10 } publisher 1 import foundation 2 import parseswift 3 4 struct publisher parseobject { 5 6 7 var name string? 8 9 10 } isbn 1 import foundation 2 import parseswift 3 4 struct isbn parseobject { 5 6 7 var name string? 8 9 10 } book 1 import foundation 2 import parseswift 3 4 struct book parseobject { 5 6 7 var title string? 8 var publishingyear int? 9 var genre genre? 10 var isbn isbn? 11 12 13 } 在将这些对象的实例存储到 back4app 数据库之前,所有属性必须符合 可编码 可编码 和 可哈希 可哈希 协议。 我们使用以下方法来管理这些对象在 back4app 数据库中的内容: create //when creating a new instance of author we use 1 var newauthor author = author(name "john doe") 2 3 // saves newauthor on your back4app database synchronously and returns the new saved item it throws and error if something went wrong 4 let savedauthor = try? newauthor save() 5 6 // saves newauthor on your back4app database asynchronously, and passes a result\<author, parseerror> object to the completion block to handle the save process 7 newauthor save { result in 8 // handle the result to check wether the save process was successfull or not 9 } read //for reading any of the objects introduced above, we construct the corresponding queries for each of them for author we have 1 let authorquery = author query() // a query to fetch all author items on your back4app database 2 3 // fetches the items synchronously or throws an error if found 4 let fetchedauthors = try? query find() 5 6 // fetches the items asynchronously and calls a completion block passing a result object containing the result of the operation 7 query find { result in 8 // handle the result 9 } add relations //in 1 1 relations, it is sufficient to have the child object as a property of the parent object back4app automatically saves the child object when the parent object is saved for the remaining relations, we use the add( objects ) method via the relation property available in the parent parseobject adding a relation called authors on a book object would look like this 1 let somebook book 2 let authors \[author] 3 4 // adds the relation between somebook and authors under the name 'authors' 5 let booktoauthorsrelation = try? somebook relation? add("authors", objects authors) 6 7 // saves the relation synchronously 8 let updatedsomebook = try? booktoauthorsrelation save() 9 10 // saves the relation asynchronously 11 booktoauthorsrelation save { result in 12 // handle the result 13 } query relations //for 1 1 relations, it is enough to append the include() method in the query for instance, to query all the books together with their isbn relation, we use 1 var query = book query() include("isbn") 2 3 // fetches all books synchronously 4 let books = try? query find() 5 6 // fetches all books asynchronously 7 query find { result in 8 // handle the result 9 } remaining relations //for the remaining relations, we create a query by using the static method queryrelation( ,parent ) provided by the parseobject protocol querying the authors related to a book can be implemented in the following way 1 let somebook book 2 let authors \[author] 3 4 5 // we create a relation (identified by the name 'authors') betwee somebook and a set of authors 6 let booktoauthorsrelation = 7 guard let booktoauthorsrelation = try somebook relation? add("authors", objects authors) // book > author 8 else { 9 fatalerror("failed to add relation") 10 } 11 12 let savedrelation = try booktoauthorsrelation save() // saves the relation synchronously 13 14 booktoauthorsrelation save { result in // saves the relation asynchronously 15 // handle the result 16 } 1 下载图书应用模板 该 xcode xcode 项目具有以下结构 随时,您都可以通过我们的 github 仓库访问完整的项目。 ios 示例仓库 2 额外的 crud 流程 在进一步之前,有必要实现一些 crud 功能,以便我们能够保存 作者 作者 , 出版商 出版商 和 类型 类型 对象。在 maincontroller+parseswift swift maincontroller+parseswift swift 文件中,在 maincontroller maincontroller 类的扩展下,我们实现了以下方法 1 // maincontroller+parseswift swift file 2 extension maincontroller { 3 /// collects the data to save an instance of book on your back4app database 4 func savebook() { 5 view\ endediting(true) 6 7 // 1 first retrieve all the information for the book (title, isbn, etc) 8 guard let booktitle = booktitletextfield text else { 9 return presentalert(title "error", message "invalid book title") 10 } 11 12 guard let isbnvalue = isbntextfield text else { 13 return presentalert(title "error", message "invalid isbn value ") 14 } 15 16 let query = isbn query("value" == isbnvalue) 17 18 guard (try? query first()) == nil else { 19 return presentalert(title "error", message "the entered isbn already exists ") 20 } 21 22 guard let genreobjectid = genreoptionsview\ selectedoptionids first, 23 let genre = genres first(where { $0 objectid == genreobjectid}) 24 else { 25 return presentalert(title "error", message "invalid genre ") 26 } 27 28 guard let publishingyearstring = publishingyeartextfield text, let publishingyear = int(publishingyearstring) else { 29 return presentalert(title "error", message "invalid publishing year ") 30 } 31 32 let authors \[author] = self authoroptionsview\ selectedoptionids compactmap { \[weak self] objectid in 33 self? authors first(where { objectid == $0 objectid }) 34 } 35 36 let publishers \[publisher] = self publisheroptionsview\ selectedoptionids compactmap { \[weak self] objectid in 37 self? publishers first(where { objectid == $0 objectid }) 38 } 39 40 // since we are making multiple requests to back4app, it is better to use synchronous methods and dispatch them on the background queue 41 dispatchqueue global(qos background) async { 42 do { 43 let isbn = isbn(value isbnvalue) // 2 instantiate a new isbn object 44 45 let savedbook = try book( // 3 instantiate a new book object with the corresponding input fields 46 title booktitle, 47 publishingyear publishingyear, 48 genre genre, 49 isbn isbn 50 ) save() // 4 save the new book object 51 52 // here we will implement the relations 53 54 dispatchqueue main async { 55 self presentalert(title "success", message "book saved successfully ") 56 } 57 } catch { 58 dispatchqueue main async { 59 self presentalert(title "error", message "failed to save book \\((error as! parseerror) message)") 60 } 61 } 62 } 63 } 64 65 /// retrieves all the data saved under the genre class in your back4app database 66 func fetchgenres() { 67 let query = genre query() 68 69 query find { \[weak self] result in 70 switch result { 71 case success(let genres) 72 self? genres = genres // when setting self? genres, it triggers the corresponding ui update 73 case failure(let error) 74 self? presentalert(title "error", message error message) 75 } 76 } 77 } 78 79 /// presents a simple alert where the user can enter the name of a genre to save it on your back4app database 80 func handleaddgenre() { 81 // displays a form with a single input and executes the completion block when the user presses the submit button 82 presentform( 83 title "add genre", 84 description "enter a description for the genre", 85 placeholder nil 86 ) { \[weak self] name in 87 guard let name = name else { return } 88 let genre = genre(name name) 89 90 let query = genre query("name" == name) 91 92 guard ((try? query first()) == nil) else { 93 self? presentalert(title "error", message "this genre already exists ") 94 return 95 } 96 97 genre save { \[weak self] result in 98 switch result { 99 case success(let addedgenre) 100 self? presentalert(title "success", message "genre added!") 101 self? genres append(addedgenre) 102 case failure(let error) 103 self? presentalert(title "error", message "failed to save genre \\(error message)") 104 } 105 } 106 } 107 } 108 109 /// retrieves all the data saved under the publisher class in your back4app database 110 func fetchpublishers() { 111 let query = publisher query() 112 113 query find { \[weak self] result in 114 switch result { 115 case success(let publishers) 116 self? publishers = publishers 117 case failure(let error) 118 self? presentalert(title "error", message error message) 119 } 120 } 121 } 122 123 /// presents a simple alert where the user can enter the name of a publisher to save it on your back4app database 124 func handleaddpublisher() { 125 // displays a form with a single input and executes the completion block when the user presses the submit button 126 presentform( 127 title "add publisher", 128 description "enter the name of the publisher", 129 placeholder nil 130 ) { \[weak self] name in 131 guard let name = name else { return } 132 133 let query = publisher query("name" == name) 134 135 guard ((try? query first()) == nil) else { 136 self? presentalert(title "error", message "this publisher already exists ") 137 return 138 } 139 140 let publisher = publisher(name name) 141 142 publisher save { \[weak self] result in 143 switch result { 144 case success(let addedpublisher) 145 self? presentalert(title "success", message "publisher added!") 146 self? publishers append(addedpublisher) 147 case failure(let error) 148 self? presentalert(title "error", message "failed to save publisher \\(error message)") 149 } 150 } 151 } 152 } 153 154 /// retrieves all the data saved under the genre class in your back4app database 155 func fetchauthors() { 156 let query = author query() 157 158 query find { \[weak self] result in 159 switch result { 160 case success(let authors) 161 self? authors = authors 162 case failure(let error) 163 self? presentalert(title "error", message error message) 164 } 165 } 166 } 167 168 /// presents a simple alert where the user can enter the name of an author to save it on your back4app database 169 func handleaddauthor() { 170 // displays a form with a single input and executes the completion block when the user presses the submit button 171 presentform( 172 title "add author", 173 description "enter the name of the author", 174 placeholder nil 175 ) { \[weak self] name in 176 guard let name = name else { return } 177 178 let query = author query("name" == name) 179 180 guard ((try? query first()) == nil) else { 181 self? presentalert(title "error", message "this author already exists ") 182 return 183 } 184 185 let author = author(name name) 186 187 author save { \[weak self] result in 188 switch result { 189 case success(let addedauthor) 190 self? presentalert(title "success", message "author added!") 191 self? authors append(addedauthor) 192 case failure(let error) 193 self? presentalert(title "error", message "failed to save author \\(error message)") 194 } 195 } 196 } 197 } 198 }sw 有关此步骤的更多详细信息,您可以访问 基本操作指南 3 设置关系 在开始创建关系之前,先查看一下 快速参考 https //www back4app com/docs/ios/parse swift sdk/data objects/relationships#quick reference 部分,以了解我们想要相互关联的对象。在下面的图中,我们展示了这些对象是如何关联的 如图所示,关系是通过将 书籍 书籍 对象放在中间来创建的。箭头显示了每个对象是如何与 书籍 书籍 对象相关的。 4 实现关系 1 1 情况 通过在 1 1 关系中添加一个属性,可以轻松实现 书籍 书籍 对象,即, 1 struct book parseobject { 2 3 4 var isbn isbn? // esablishes a 1 1 relation between book and isbn 5 6 7 } 在这种情况下, 书籍 书籍 和 isbn isbn 之间存在一种 1 1 关系,其中 书籍 书籍 被识别为父项,而 isbn isbn 被识别为子项。在内部,当 back4app 保存一个 书籍 书籍 的实例时,它首先保存 isbn isbn 对象(在 isbn isbn 类名下)。在这个过程完成后,back4app 继续处理对象 书籍 书籍 。新的 书籍 书籍 对象以其 isbn isbn 属性被表示为一个 pointer\<isbn> pointer\<isbn> 对象。一个 pointer<> pointer<> 对象允许我们存储与其对应父项相关的唯一实例的 isbn isbn 对象。 1\ n 情况 对于 1\ n 关系,最有效的实现方式是通过一个 parserelation\<book> parserelation\<book> 对象。 parseswift 提供了一组方法来为任何符合 parseobject parseobject 协议的对象添加这些类型的关系。例如,如果我们想在 1\ n 1\ n 关系中创建 book book 和 author author 之间,我们可以使用 1 let somebook book 2 let authors \[author] 3 4 5 // we create a relation (identified by the name 'authors') between somebook and a set of authors 6 let booktoauthorsrelation = 7 guard let booktoauthorsrelation = try somebook relation? add("authors", objects authors) // book > author 8 else { 9 fatalerror("failed to add relation") 10 } 11 12 let savedrelation = try booktoauthorsrelation save() // saves the relation synchronously 13 14 booktoauthorsrelation save { result in // saves the relation asynchronously 15 // handle the result 16 } 将此代码片段适应于我们拥有的其他关系是简单的, 书籍 书籍 。 \ 将所有内容整合在一起 一旦我们建立了实现关系的基本想法,我们现在完成 savebook() savebook() 方法。在这个过程中,我们列出了需要记住的关键点。 1 extension maincontroller { 2 /// collects the data to save an instance of book on your back4app database 3 func savebook() { 4 5 // 1 first retrieve all the information for the book (booktitle, isbnvalue, etc) 6 7 8 // since we are making multiple requests to back4app, it is better to use synchronous methods and dispatch them on the background queue 9 dispatchqueue global(qos background) async { 10 do { 11 let isbn = isbn(value isbnvalue) // 2 instantiate a new isbn object 12 13 let savedbook = try book( // 3 instantiate a new book object with the corresponding input fields 14 title booktitle, 15 publishingyear publishingyear, 16 genre genre, 17 isbn isbn 18 ) save() // 4 save the new book object 19 20 // 5 add the corresponding relations for new book object 21 guard let booktoauthorsrelation = try savedbook relation? add("authors", objects authors), // book > author 22 let bootktopublishersrelation = try savedbook relation? add("publishers", objects publishers), // book > publisher 23 let genrerelation = try genre relation? add("books", objects \[savedbook]) // genre > book 24 else { 25 return dispatchqueue main async { 26 self presentalert(title "error", message "failed to add relations") 27 } 28 } 29 30 // 6 save the relations 31 = try booktoauthorsrelation save() 32 = try bootktopublishersrelation save() 33 = try genrerelation save() 34 35 dispatchqueue main async { 36 self presentalert(title "success", message "book saved successfully ") 37 } 38 } catch { 39 dispatchqueue main async { 40 self presentalert(title "error", message "failed to save book \\((error as! parseerror) message)") 41 } 42 } 43 } 44 } 45 46 47 } 5 查询关系 对于 1 1 关系,给定父级 书籍 书籍 及其子级 isbn isbn , 我们通过将其包含在 书籍 书籍 查询中来查询相应的子级 1 let query = book query() include("isbn") 2 3 let books = try query find() // retrieves synchronously all the books together with its isbn 4 5 query find { result in // retrieves asynchronously all the books together with its isbn 6 // handle the result 7 } 8 通过这个,所有来自于 query 的书籍也将正确设置 isbn 属性,并与相关的 isbn 对象关联。 另一方面,为了检索具有 1\ n 关系的对象, parseobject 协议提供了静态方法 queryrelation( ,parent ) 通过提供关系的名称(作为第一个参数)和父对象,此方法允许我们创建所需的查询。例如,要检索与特定 book 相关的所有 author ,我们可以使用以下代码片段 1 let book book // book from which we are trying to retrieve its related authors 2 3 do { 4 let authorsquery = try author queryrelations("authors", parent book) // 'authors' is the name of the relation it was saved with 5 6 authorsquery find { \[weak self] result in 7 switch result { 8 case success(let authors) 9 self? authors = authors 10 11 dispatchqueue main async { 12 // update the ui 13 } 14 case failure(let error) 15 dispatchqueue main async { 16 self? presentalert(title "error", message "failed to retrieve authors \\(error message)") 17 } 18 } 19 } 20 } 21 } catch { 22 if let error = error as? parseerror { 23 presentalert(title "error", message "failed to retrieve authors \\(error message)") 24 } else { 25 presentalert(title "error", message "failed to retrieve authors \\(error localizeddescription)") 26 } 27 } 同样,我们可以查询其他相关对象,例如 publisher 在 bookdetailscontroller swift 文件中,我们实现这些查询以显示一本书与作者和出版商的关系 1 // bookdetailscontroller swift file 2 class bookdetailscontroller uitableviewcontroller { 3 4 5 /// retrieves the book's details, i e , its relation with authors and publishers 6 private func fetchdetails() { 7 do { 8 // constructs the relations you want to query 9 let publishersquery = try publisher queryrelations("publishers", parent book) 10 let authorsquery = try author queryrelations("authors", parent book) 11 12 // obtains the publishers related to book and display them on the tableview, it presents an error if happened 13 publishersquery find { \[weak self] result in 14 switch result { 15 case success(let publishers) 16 self? publishers = publishers 17 18 // update the ui 19 dispatchqueue main async { 20 self? tableview\ reloadsections(indexset(\[section publisher rawvalue]), with none) 21 } 22 case failure(let error) 23 dispatchqueue main async { 24 self? presentalert(title "error", message "failed to retrieve publishers \\(error message)") 25 } 26 } 27 } 28 29 // obtains the authors related to book and display them on the tableview, it presents an error if happened 30 authorsquery find { \[weak self] result in 31 switch result { 32 case success(let authors) 33 self? authors = authors 34 35 // update the ui 36 dispatchqueue main async { 37 self? tableview\ reloadsections(indexset(\[section author rawvalue]), with none) 38 } 39 case failure(let error) 40 dispatchqueue main async { 41 self? presentalert(title "error", message "failed to retrieve authors \\(error message)") 42 } 43 } 44 } 45 } catch { // if there was an error during the creation of the queries, this block should catch it 46 if let error = error as? parseerror { 47 presentalert(title "error", message "failed to retrieve authors \\(error message)") 48 } else { 49 presentalert(title "error", message "failed to retrieve authors \\(error localizeddescription)") 50 } 51 } 52 } 53 54 55 } 6 运行应用程序! 在按下 xcode xcode 的运行按钮之前,请不要忘记配置您的 back4app back4app 应用程序中的 appdelegate appdelegate 类! 在添加新书之前,您必须添加几个 类型 、 出版商 和 作者 。然后,您可以开始输入书籍的信息以将其保存到您的 back4app 数据库中。一旦您保存了一本书,请打开您的 back4app 仪表板 https //parse dashboard back4app com/apps ,并转到与 xcode 项目相关的应用程序。在数据库部分,您将找到 书籍 书籍 类,其中存储了所有由 ios ios 应用程序创建的书籍。 此外,您可以看到 back4app 自动创建了 isbn isbn 类,以便与其相应的 书籍 书籍 对象关联。如果您返回 书籍 书籍 类,您可以识别每种关系的数据类型。在 isbn isbn 和 类型 类型 的情况下,数据类型是 指针<> 指针<> 。另一方面,对于像 作者 作者 和 出版商 出版商 这样的关系,数据类型是 关系<> 关系<> 。这是构建关系时需要牢记的一个关键区别。