iOS
...
Data Objects
ParseSwift SDK - 객체 관계 설정 및 구현 가이드
16 분
관계 소개 관계는 데이터베이스에 저장된 데이터 객체를 조직하는 기본적인 기능입니다 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에서 생성된 앱 back4app에서 parse 앱을 만드는 방법을 배우려면 새 parse 앱 튜토리얼 을 따르세요 참고 back4app에 연결된 xcode 프로젝트를 만들려면 parse sdk (swift) 설치 튜토리얼 을 따르세요 우리의 도서 앱 이해하기 프로젝트 템플릿은 사용자가 책 세부 정보를 입력하여 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 데이터베이스에 저장하기 전에 모든 속성이 다음 프로토콜을 준수해야 합니다 codable codable 및 hashable hashable 프로토콜 우리는 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 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 프로토콜을 준수하는 모든 객체에 대해 이러한 유형의 관계를 추가하는 메서드 집합을 제공합니다 예를 들어, 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 관계에서, 부모 book book 과 그 자식 isbn isbn , 우리는 book book 쿼리에 포함시켜 해당 자식을 쿼리합니다 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 프로젝트에 연결된 애플리케이션으로 이동하세요 데이터베이스 섹션에서 book book 클래스에서 ios ios 앱에서 생성된 모든 책이 저장됩니다 또한 back4app이 자동으로 isbn isbn 클래스를 생성하여 해당 book book 객체와 연결할 수 있도록 했습니다 book book 클래스로 돌아가면 각 관계 유형에 대한 데이터 유형을 확인할 수 있습니다 isbn isbn 및 장르 장르 , 데이터 유형은 pointer<> pointer<> 반면에 저자 저자 및 출판사 출판사 , 데이터 유형은 relation<> relation<> 이는 관계를 구성할 때 염두에 두어야 할 중요한 차이점입니다