...
본문 바로가기

WWDC

WWDC 19 - Making Apps with Core Data


  • 세션보면서 정리한 내용입니다. 해석이 잘못된 경우가 있을수있으니 발견하시면 댓글로 남겨주시면 감사하겠습니다🙏🏻

https://developer.apple.com/wwdc19/230

 

Making Apps with Core Data - WWDC19 - Videos - Apple Developer

Core Data helps manage the flow of data throughout your app. Hear about new features in Core Data that make your code simpler and more...

developer.apple.com

 


 

 

요약

  • 각 model들에 대해👍 relationship으로 다대다, 혹은 일대다, 다대일로 설정할수있다.  또한 삭제룰도 정할수있다.
  • Persistent container가 손쉽게 core data stack을 세팅해주고, 관리할수있다.
    • persistentstore coordinator 
    • managed object context
    • manged object model 
  • query generations을 통해 안정적이고, 최신데이털르 유지하도록하게해준다. 
  • context는 백그라운드에서도 실행이가능하다. 
  • 다수의 데이터를 insert해야하는경우 batch insert를 사용할수있다. 
  • fetchrrequest를 데이터를 가져오는데, 이제는 👍Combine과 결합되어!! view에서 손쉽게 데이터수정및반영이 즉각적으로가능하다!!!! 
  • 하지만 많은데이터를 요청할때는, 👍Fetch Results controller 이용할수있다!
    • tableview 밀접하게 delegate메소드가있다.
    • 또한이제는 👍diffableDatasource + Snapshot과도 사용되어질수있기때문에 collectionView 가능하다! 
  • 👍derived attributes를 통해서 아주 손쉽게 denormalization이 가능하다!
  • remote change notification 생겼다 ( 어려워… ) 

 

 

 


 

간단한앱을 사용할것이다.

아래는 +버튼을통해 새로운 post를 추가한다. 

 

 

그리고 post를 누르면 title,content, tags, atachments를 작성할수있다. 

 

tag 추가 

 

또한 태그매니저에서 태그를 추가할수있다. 

 

 

오케이! 자그럼 필요한 데이터 모델들을 생각해보자 .

 

Post에서 attachments, tag가있고, 

attachment는 꽤 커질수있으므로, 데이터를 따로 저장하는게 좋겠다.

또한 리스트뷰에서는 thumbnail만 필요하겠다. 그러므로 큰 데이터는 따로 저장해야겠다. 

 

이를 Core data로 표현하고, Xcode 안에있는 Model Editor로 나타낼수있다. 

각 Model들을 relationship을 정해준것 

 

이러한 relationship에는 몇가지 룰을적용할수있다. 

위에서 attachment가 삭제될경우 image data도 자동적으로삭제되게 정할수있다. 이를 cascade deletion rule이라한다.

 

attachment <-> ImageData 

그리고 attachment 와 imagedata는 1-1연결되어있다 ( 화살표가각각 하나씩! ) 

  • 이는 imagedata는 하나의 attachment에 의해서만 표현되어질수있고,  attachment는 하나의 image data에의해 backed 되어질수있다. 

 

 

Post <- >> attachment 

  • 이는 post는 여러개의 attachment를 가질수있지만, attachment는 하나의 post만 가질수있다. 

 

post << - >> tag

  • 이는 post는 여러개의 tag를 가질수있고, tag또한 여러개의 post를가질수있다. 

 

 

 

 


모델들( Manged Object Model ) 을 정의했고, 이제 조금더 core data type에 친숙해보자.

 

이러한 model들은 persistentStoreCoordinatore에의해 요구된다. 

  • 이름에서알수있ㄷ스이 persistent store들을 관리하는책임을가진다.
  • 파일시스템에 존재하는 데이터베이스에 수많은 store들이 있고, 이것을 Persistent store coordinator가 관리한다. 

마지막으로 우리가 대부분의시간을 사용하는, Managed Object Context가있다. 

  • 무언가를 하든간에 context가 필요하다. 만약 fetch request를 실행하려면 context가 필요하다.
  • 또한 context는 수행하기위해서는 coordinator를 알아야한다. 

즉 core data의 타입들은 모두 서로 상호의존적이라할수있다.

 

하지만 이러한 타입들을 캡슐화한것이 Persistent container이다.

  • 이를통해서 손쉽게 core data type들의 스택을 세팅ㅇ할수있다. 
  • 이름을통해서 container를 로드한다. 

 

  • 만약 코드로 모델을 만들었다면, 다른이니셜라이저를 작성하면된다.

 

container를 가졌다면, persistent store를 로드한다. 

  • 각 컴프리션블록은 store마다 호출되고, 에러를 반환한다. 
  • 이이후에는 이제 managed object context에 집중하면된다. 

 

 

 

Manged object Contexts는 data에 매끄럽게접근해주고, 특정상황에 유용한 몇가지옵션들이있다.

 

query generations 

  • 변하거나 삭제되어도, 안전하고 일관된 접근하도록하는, stores data의 안정적인 뷰를 제공한다. 

  • 항상 최신데이터를 유지하도록 할수있다. 

 

 

 

 


오케이! 여기까지 core data의 스택들을 세팅햇다.

그럼 앱에서는 어떻게 사용할까?? 

 

우선 모든 store reuqest 그리고 managed object와의 interactions 들은 무조건 context queue안에서 완료되어야한다. 

context는 backgroundTask로도 수행할수있다. 

 

 

 

그럼 앱에 데이터를 추가하는방법은? 

간단하게, 우리가 만든 모델은 Xcode에의해 서브클래스로 생성되어지고, 이를 이용하면된다. 

 

 

만약 많은데이터가 필요하다면??? 

예를들어 서버로부터 수많은데이터가 들어왔을때 위와같이 작업하면 boilerplate도 늘어나고, 리소스오버헤드, 메모리사용도 증가된다.

 

그러므로 batch insertion을 이용할수있다. 

  • 오버헤드를 줄여준다.

예를들어 천개의 post를 [String: Any] 타입으로 일렬로 만들어주어, bacthInsert한다. 

그리고그에따른 결과를 반환하고,  success인지체크한다.

 

이렇게 아래는 3개의 post가 있고, 그에맞는 Post. 

 

 

 

batch작업에는 하지만 몇가지 직접 핸들링해야하는 부분들이있다.   ( 잘 이해가안가네요 ㅠ ) 

  •  If you have unique constraints than any existing objects matching the dictionary will be pulled out of the database and updated with new values instead. 
  • Attributes that are optional or configured with default values can be omitted from the dictionary as well. In the case of updating an object with unique constraint, the existing values will not be changed.
  • This goes for relationships as well. Batch insertions can't be used to set relationships. But if a batch insert updates an existing object due to a unique constraint, the existing relationships will be left alone.
  • contextDidSaveNotification을 호출하지않는다. 

 

 

 


오케이! 여기까지 데이타들을보았고, 그럼 컨트롤러는 무엇이필요할까? 

fetch 와 rpesent data가 필요하다.

 

fetchReques를이용하여 object를 가져올수있다. 

아래는 Tag object를 요구하고, 이름으로써 predicate를 지정하고, 가장첫뻔재를 가져온것! 

그다음 뷰에게 세팅했다. 

 

 

오케이 변하지않는데이터는 이것만으로충분히 작동한다. 

하지만 tag의 이름과 컬러가 뷰안에서 변경된다면? 

 

Combine 프레임워크와 결합하여, KVO를 이용하면 !!!

엄청 쉽게할수있따. ( 대박 .. ) 👍 ( 박수박수 ) 

  • object로부터 publisher를 얻을수있다...
  • assign하여 바로 label할당할수있자나~~ 대박.!!

Combine의 더 자세한설명은 세션을참고하고, 물론 제가 쓴글도 있습니다. ㅎ 

 

 

Detail View에서는 외부로부터 주입받을테니, 아래와같이 작성할수있다.

 

 

 

자, detailview의 부모는 리스트형식의 뷰이겠다.Collection or tableview. 

그럼 많은 데이터를 가져와야하고, 

이는 fetch request를 통해서도 달성할수있다. 

 

가져온결과들을 sort할수있따. 이름순으로 정렬! 

음, 이름이 충분히유니크하지않다면? 추가적인 descriptor를 추가할수있다?

 

또다른 옵션으로는 batchsize를 이용.

이를통해 한번에 store로부터 얼마나 많은 object를 로드할것인지명시할수이싿. 

엄청많은데이터일경우 한번에 다 메모리에올릴경우 시간도시간이고 공간낭비이니. 

 

그러므로 아래와같이 이름순으로 50개로 딱 뽑아올수있다. 

 

하지만 만약 데이터가수정된다면😢? 

변화에도 항상 최신상태를 유지해야해! 

 

짜잔~ Fetch Results Controller를 이용할수있다. 

  • Core Data는 lFetch Result Controller의형식?으로 live queries를 지원한다.
  • fetchrequest, context를 이니셜라이저에 넣어준다. 
  • delegate도 해주어야함!
    • fetch reuqest에서 변화들은 이 delegate를 통해서 받아온다.

 

 

다양한 delegate 메소드가있다. 

데이터가변할때랑, section이 변할때나~ 

 

각각 delegate 메소드들은 tableview's API와 일치하기위해 만들어졌다. 

하지만 그렇게매칭되기위해서는 많은 코드들이필요하고 컬렉션뷰는 이러한 콜백에 지원을안한다. 

 

 

 

하지만~~! 

이번에는 snapshot과 가능해졌다~! ( 박수 박수 ) 👍

tableview를 사용한다면 data source들을 diffableDataSource로 사용하고, snapshot을사용한다. 

마찬가지로! collectionView도 difableDataSource, snapshot을 사용하여 Fetched Reuslts Controller를 사용할수있다. 단한줄로!!!

  • 아주간단히, datasource에 snapshot을 주엇을뿐!
  •  

 

 

또한 새로운 delegate 메서드가추가됐다. 

  • 새로소개된 collectionDifference 의 타입을 사용한다.  이것은 section fetching을 사용하지않을때 지원된다  ( 어렵네용 ) 

 

새로운 delegate메서드를이용하여, 컬렉션뷰의 하나의 섹션에 변화를 반영하는방법!

 

 

 

오케이! 여기까지 view에게 fetched result controller를 적용하는방법을 알아써.

근데 만약 성능때문 등으로 ( 비싸서 )  fetch하는것자체가 어렵다면? 

 

denormalization이 필요할것이다. 

  • 작년 세션에 소개했엇고, 요약하면 데이터를 카피해서 더 효율적으로 접근하도록 하는 방법이다.  추가적인공간의 오버헤드가존재하지만 대부분 충분히감수할만하다. 
  • 이앱에서 예를들면, 각 tag를 사용한 포스트가 얼마나있는지를 추적하기위해 좋은대안이된다. 
  • 즉 tag에 postCount attributes를 추가하면된다!! ( 간단간단 ) 
    • 그러므로 특정태그를 사용한 포스트가 추가되거나 삭제될때 카운팅해주면된다. 

 

음, Derived Attributes가 새로나왔는데, 이는 다양한기능을지원하고, object model안에서 설정가능하고, 

이를통해 훨씬 denormalization을 가능하게해준다.. 

 

 

Demo로 Derived attributes를 적용하는법을 보자.

 

아래와같이 각 태그에 몇개의포스트가있는지 확인하는건데, 

tag의 연결된 posts의 개수로 찾아왔다. 그러므로 post가 더많아질수록 성능은 떨어지겠다. 

 

 

그럼 derived attributes를 적용해보자. 

attributes를 추가해주고 - postCount 

integer타입으로해주고, 

 

인스펙터로 가서, derived를 체크해준다!  그러면 derivation 공간이생기고, 

 

여기에는 뷰에서사용하는 코드를 입력해준다. 

 

 

 

반영하기위해 다시 리빌드한번해주고, 

코드를 변경해주고! 끝!!! 

 

짜잔~~~!!!!!!!!( 때박........  박수갈채 ) 

  • atttribute추가하고, derived체크해주고, derivation에 코드작성하고,   코드수정한게끝인데??????!!!!!!!!!!!
  • 최신데이터로 유지할필요도없다......!!!!!! 대박...... (실환가?  그럼 post에서 tag수정해도 그냥반영되는거....????? ) 

 

 

derivation은 공식문서에도 나와있고, 총 4개로 구성되어이따. 

3번째가 위에서 한 방법. 

 

 

 

 

 

 

 

 

 

 

 


 

 

persistent history 는 2017에 소개되었고,  이번에 추가된 기능이있다. 

 

오잉? Transaction과 change안에 fetchRequest가 생겼따? 

 

 

 

 

 

PersistentHistory에서 어떻게 history가 만들어지는것을 어떻게알까?  많은방법들이있지만, 다 단점을갖고있다.

즉 다른 coordinator들에서의변화가 발생할때는,  완벽한솔루션들이아니다. 

그래서 이번에 새로운 notification이 생겼다. 

Remote Change Notificaitons

  • 각 coordinator의 notification? 비동기적이다.  

이를 키기위해서는, 새로운 옵션이생겼다. 

 

persistentstore가 로드하기전에 description을 셋해준다.

그럼 coordinator는 remote changes 를 듣게된다 

또한 각 변화가발생했을때, coordinator는 remote change notification을 보내게된다.

 

 

또한, persistentHistory가 가능하다면,  notification에는 history token key도가지고있다.. ( 어렵당 ) 

 

 

 

 

데모를통해서 학인해보자. 

 

이컨트롤러는 coordinator에변화가있을때마다 항상최신으로 유지하는 책임을가지고있다. 

storeRemoteChange는 remote change notification을 받을때마다 호출된다. 

 

토큰을 꺼내고, 

업데이트하고자하는 context를 가지고, perform 수행한다. 

 

그다음 우리가 흥미있어하는 것으로만 persistent history를 줄이기위해서, predicate를 설정하고 ,

흑흑.. 무슨말인지모르겠다 (하지만 사람들은 박수를치네 🥲 ) 

 

 

 

 


 

CoreData를 test할경우 몇가지 고려해야한다.

 

연락처앱같은경우는 최소 수만개의 object를 테스팅해봐야한다.  이러한경우에는 선형시간알고리즘도괜찮다 

하지만포토앱같은경우는 수백만개이상으로 테스팅해보고, 이정도스케일일경우 log이하의 알고리즘이어야한다????? 

 

우선 intergration test는  concurrency debugging을 얻기위해  최적화된 configuration을 사용해야한다. 

이는 shceme editor에서 설정할수있다. 

 

intergration test는 시간이많이걸릴수있지만,

 

unit test는 가능한 빨라야하겠다. 

그러므로 in-memory store를 이용할수있다. 

다음과같이 description에 url을 /dev/null을 지정한다 

대신에 in-memory store는 다른 coordinator와 공유되어질수가없다. 

 

 

공유되어지게하기위해서는 named in-memory 를 사용해야하고, 아래와같이 append해준다. 

emote chang enotification 도 가능해지므로 테스트가 가능해진다. 

 

 

마지막으로 Snaitzer를 이용해라.