<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Megi Sila on Medium]]></title>
        <description><![CDATA[Stories by Megi Sila on Medium]]></description>
        <link>https://medium.com/@meggsila?source=rss-947ac9305afc------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Stories by Megi Sila on Medium</title>
            <link>https://medium.com/@meggsila?source=rss-947ac9305afc------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 02 May 2026 19:24:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@meggsila/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Core Data Relationship in Swift 5— made simple]]></title>
            <link>https://medium.com/@meggsila/core-data-relationship-in-swift-5-made-simple-f51e19b28326?source=rss-947ac9305afc------2</link>
            <guid isPermaLink="false">https://medium.com/p/f51e19b28326</guid>
            <category><![CDATA[core-data]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[core-data-swift]]></category>
            <category><![CDATA[swift-5]]></category>
            <dc:creator><![CDATA[Megi Sila]]></dc:creator>
            <pubDate>Tue, 03 May 2022 12:50:21 GMT</pubDate>
            <atom:updated>2022-05-03T12:50:21.080Z</atom:updated>
            <content:encoded><![CDATA[<p>Persistent storage has become an essential part of the majority of iOS apps nowadays. Core Data is a persistence and/or in-memory cache framework that consists of a very powerful set of other tools that can be used within the app. While researching for my personal ongoing project I have faced a lack of updated content on this topic so I decided to make an article myself in Swift 5.</p><p>Firstly let’s start fresh and create a new project where the Core Data module is selected.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*1ieskEmHhUIJAkINV6xmCg.png" /></figure><p>Now we can see that there are two notable changes in this project: CoreDataRelationship.xcdatamodeld and AppDelegate.swift file with Core Data Stack code.</p><p>We must create at least two entities to add a relationship between them. In this example we are going to implement a one-to-many relationship between two entities: <strong>Singer</strong> and <strong>Song</strong> with their respective attributes as shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-P3mJfyoNBmFE_S4r_mghw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hxza3fc0OWQ0SK1PgFHrjQ.png" /></figure><p>Let’s select each entity and add a relationship between them. In the Relationships sections of the first entity <strong>Singer</strong> we are going to create a new relationship called <strong>songs</strong> by clicking the plus button and choose <strong>Song</strong> as destination. The inverse field is going to be left empty at the moment. In the attributes section of this relationship the type must be changed to <strong>To Many</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TXV7h6t5_HywZXUmzIRxGQ.png" /></figure><p>In the same way we are going to create a new relationship in the <strong>Song</strong> entity named <strong>singer</strong> with <strong>Singer</strong> as destination. Now you can see that in the inverse dropdown appears the relationship that we previously created: <strong>songs</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YNXrFtJiy5Oin_x3z2Bvbg.png" /></figure><p>If we jump back to <strong>songs</strong> relationship we can see that the inverse field has been updated automatically. Aldo make sure to set the Codegen to Manually/None in each entity’s attribute section.</p><p>Let’s go and regenerate our NSManagedObject Subclasses in the Editor menu now. Click Next, choose both entities and then Create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TjbvK4crwIeKG-Yo0ZE4hQ.jpeg" /></figure><p>Build the project after creating these classes to make sure everything is okay (⌘ + B). If it does not build successfully try to quit Xcode and reopen it again.</p><p>To manage data creation, fetching and deletion we are going to create a class called DataManager by firstly creating the NSPersistentContainer and saving the context.</p><pre><strong>class</strong> DataManager {<br>  <strong>static</strong> <strong>let</strong> shared = DataManager()</pre><pre>  <strong>lazy</strong> <strong>var</strong> persistentContainer: NSPersistentContainer = {<br>    <strong>let</strong> container = NSPersistentContainer(name: &quot;CoreDataRelationship&quot;)<br>    container.loadPersistentStores(completionHandler: { (storeDescription, error) <strong>in<br>      if</strong> <strong>let</strong> error = error <strong>as</strong> NSError? {<br>        fatalError(&quot;Unresolved error \(error), \(error.userInfo)&quot;)<br>      }<br>    })<br>    <strong>return</strong> container<br>  }()</pre><pre>  //<strong>Core Data Saving support<br>  func</strong> save () {<br>    <strong>let</strong> context = persistentContainer.viewContext</pre><pre><strong>    if</strong> context.hasChanges {<br>      <strong>do</strong> {<br>          <strong>try</strong> context.save()<br>      } <strong>catch</strong> {<br>          <strong>let</strong> nserror = error <strong>as</strong> NSError<br>          fatalError(&quot;Unresolved error \(nserror), \(nserror.userInfo)&quot;)<br>      }<br>    }<br>  }<br>}</pre><p>Methods responsible for <strong>Singer</strong> object and <strong>Song</strong> object creation implemented simply below. <strong>addToSongs(_value: Song)</strong> method relates the song to its Singer object as a parameter of the <strong>song</strong> method.</p><pre><strong>func</strong> singer(name: String) -&gt; Singer {<br>  <strong>let</strong> singer = Singer(context: persistentContainer.viewContext)<br>  singer.name = name<br>  <strong>return</strong> singer<br>}</pre><pre><strong>func</strong> song(title: String, releaseDate: String, singer: Singer) -&gt; Song {<br>  <strong>let</strong> song = Song(context: persistentContainer.viewContext)<br>  song.title = title<br>  song.releaseDate = releaseDate<br>  singer.addToSongs(song)<br>  <strong>return</strong> song<br>}</pre><p>Now we are going to implement methods responsible for fetching data of each type. To fetch the songs we must add a predicate to the NSFetchRequest so Core Data will ensure that only the matching objects, songs that belong to the <strong>Singer</strong> object, will get returned.</p><pre><strong>func</strong> singers() -&gt; [Singer] {<br>  <strong>let</strong> request: NSFetchRequest&lt;Singer&gt; = Singer.fetchRequest()<br>  <strong>var</strong> fetchedSingers: [Singer] = []</pre><pre><strong>  do</strong> {<br>     fetchedSingers = <strong>try </strong>persistentContainer.viewContext.fetch(request)<br>  } <strong>catch</strong> <strong>let</strong> error {<br>     print(&quot;Error fetching singers \(error)&quot;)<br>  }</pre><pre>  <strong>return</strong> fetchedSingers<br>}</pre><pre><strong>func</strong> songs(singer: Singer) -&gt; [Song] {<br>  <strong>let</strong> request: NSFetchRequest&lt;Song&gt; = Song.fetchRequest()<br>  request.predicate = NSPredicate(format: &quot;singer = %@&quot;, singer)<br>  request.sortDescriptors = [NSSortDescriptor(key: &quot;releaseDate&quot;, ascending: <strong>false</strong>)]<br>  <strong>var</strong> fetchedSongs: [Song] = []</pre><pre><strong>  do</strong> {<br>    fetchedSongs = <strong>try</strong> persistentContainer.viewContext.fetch(request)<br>  } <strong>catch</strong> <strong>let</strong> error {<br>    print(&quot;Error fetching songs \(error)&quot;)<br>  }</pre><pre><strong>  return</strong> fetchedSongs<br>}</pre><p>Methods responsible for deletion of each data type. As simple as that.</p><pre><strong>func</strong> deleteSong(song: Song) {<br>  <strong>let</strong> context = persistentContainer.viewContext<br>  context.delete(song)<br>  save()<br>}</pre><pre><strong>func</strong> deleteSinger(singer: Singer) {<br>  <strong>let</strong> context = persistentContainer.viewContext<br>  context.delete(singer)<br>  save()<br>}</pre><p>The data will be displayed in two UITableViews in different UIViewControllers. After creating <strong>SingerVC</strong> that is going to display the list of singers and <strong>SongVC</strong> that is going to display the list of songs I have set up a simple user interface as below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tVPPKX1ML2BnjfTRRuVBZw.jpeg" /></figure><p>The logic is simple: if we select a cell of <strong>SingerVC</strong> tableView the list of songs of this singer is going to display. Now straight to implementing the logic.</p><p>Let’s declare an array of Singer type globally because we are going to use it in different classes.</p><pre><strong>var</strong> singers = [Singer]()</pre><p>Let’s start with <strong>SingerVC</strong> and create a new <strong>Singer. </strong>We can create a new Singer by tapping the navigationItem.rightBarButtonItem this way:</p><ul><li>Create the object using singer method of DataManager class</li><li>Appending the object to singers array</li><li>Save the context and reload tableView</li></ul><pre><strong>let</strong> singer = DataManager.shared.singer(name: textfield.text ?? &quot;&quot;)<br>singers.append(singer)<br>DataManager.shared.save()<br><strong>self</strong>.tableView.reloadData()</pre><p>Fetching singers in <strong>viewDidLoad()</strong></p><pre><strong>override</strong> <strong>func</strong> viewDidLoad() {<br>  <strong>super</strong>.viewDidLoad()<br>  setupUI()<br>  singers = DataManager.shared.singers()<br>}</pre><p>Displaying singers in tableView. The number of rows is going to be equal to the number of singers and each row is going to display the <strong>Singer</strong> object’s name at given index in <strong>singers</strong> array.</p><pre><strong>extension</strong> SingerVC: UITableViewDelegate, UITableViewDataSource {</pre><pre><strong>  func</strong> numberOfSections(in tableView: UITableView) -&gt; Int {<br>    1<br>  }</pre><pre><strong>  func</strong> tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {<br>    <strong>return</strong> singers.count<br>  }</pre><pre><strong>  func</strong> tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {<br>    <strong>let</strong> singer = singers[indexPath.row]<br>    <strong>let</strong> cell = tableView.dequeueReusableCell(withIdentifier: &quot;singerCell&quot;, for: indexPath) <strong>as</strong> UITableViewCell<br>    cell.textLabel?.text = singer.name<br>    cell.accessoryType = .disclosureIndicator<br>    tableView.deselectRow(at: indexPath, animated: <strong>true</strong>)<br>  <br>    <strong>return</strong> cell<br>  }<br>}</pre><p>I have implemented the editing and deletion of the Singer objects as actions in the <strong>leadingSwipeActionsConfigurationForRowAt</strong> method.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*geOJ7eSk8DAj9KP6OKHrAg.png" /></figure><p>The editing is done via <strong>setValue(_ value: Any?, forKey key: String)</strong> method that sets the specified property of the managed object to the specified value by using its key.</p><pre><strong>private</strong> <strong>func</strong> editSingerAction(indexPath: IndexPath) {<br>  <strong>let</strong> singer = singers[indexPath.row]<br>  <strong>var</strong> nameTextField = UITextField()</pre><pre><strong>  let</strong> alert = UIAlertController(title: &quot;Edit singer&quot;, message: &quot;&quot;, preferredStyle: .alert)<br>  <strong>let</strong> editAction = UIAlertAction(title: &quot;Edit&quot;, style: .default) { (action) <strong>in<br>    </strong>singer.setValue(nameTextField.text ?? &quot;&quot;, forKey: &quot;name&quot;)<br>    DataManager.shared.save()<br>    <strong>self</strong>.tableView.reloadData()<br>  }</pre><pre>  alert.addTextField { (alertTextField) <strong>in<br>    </strong>alertTextField.placeholder = &quot;Ex: Beyonce&quot;<br>    alertTextField.text = singer.name<br>    nameTextField = alertTextField<br>  }</pre><pre><strong>  let</strong> cancelAction = UIAlertAction(title: &quot;Cancel&quot;, style: .destructive) { (action) <strong>in<br>    self</strong>.dismiss(animated: <strong>true</strong>, completion: <strong>nil</strong>)<br>  }</pre><pre>  alert.addAction(editAction)<br>  alert.addAction(cancelAction)<br>  present(alert, animated: <strong>true</strong>, completion: <strong>nil</strong>)<br>}</pre><p>For the deletion we are going to use the <strong>deleteSinger(singer: Singer)</strong> method that we created previously in the <strong>DataManager</strong> class and then delete the row of the tableView.</p><pre><strong>private</strong> <strong>func</strong> deleteSingerAction(indexPath: IndexPath) {<br>  <strong>let</strong> singer = singers[indexPath.row]<br>  <strong>let</strong> areYouSureAlert = UIAlertController(title: &quot;Are you sure you want to delete this singer?&quot;, message: &quot;&quot;, preferredStyle: .alert)<br>  <strong>let</strong> yesDeleteAction = UIAlertAction(title: &quot;Yes&quot;, style: .destructive) { [<strong>self</strong>] (action) <strong>in<br>    </strong>DataManager.shared.deleteSinger(singer: singer)<br>    singers.remove(at: indexPath.row)<br>    tableView.deleteRows(at: [indexPath], with: .fade)<br>    tableView.reloadData()<br>  }</pre><pre><strong>  let</strong> noDeleteAction = UIAlertAction(title: &quot;No&quot;, style: .default) { (action) <strong>in<br>    </strong>//do nothing<br>  }</pre><pre>  areYouSureAlert.addAction(noDeleteAction)<br>  areYouSureAlert.addAction(yesDeleteAction)<br>  <strong>self</strong>.present(areYouSureAlert, animated: <strong>true</strong>, completion: <strong>nil</strong>)<br>}</pre><p>Initialization of <strong>SongsVC</strong> for each singer in <strong>didSelectRowAt</strong>. The index parameter when initializing SongVC is going to define the singer in the <strong>singers</strong> array these songs belong to.</p><pre><strong>func</strong> tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {<br>  <strong>let</strong> songVC = SongsVC(index: indexPath.row)<br>  navigationController?.pushViewController(songVC, animated: <strong>true</strong>)<br>}</pre><p>Now let’s jump to <strong>SongVC</strong>. The implementation is almost the same except the fact that in this class we are going to define the fundamental part of the relationship. Let’s declare a <strong>Singer</strong> object and attach the element of the given index in the <strong>singers</strong> array.</p><pre><strong>var</strong> singer: Singer?<br>   ...</pre><pre><strong>init</strong>(index: Int) {<br>  <strong>self</strong>.index = index<br>  singer = singers[index]<br>  <strong>super</strong>.<strong>init</strong>(nibName: <strong>nil</strong>, bundle: <strong>nil</strong>)<br>}</pre><p>Now that we know the singer these songs belong to, we can create them the same way we created <strong>Singer</strong> objects.</p><pre><strong>let</strong> song = DataManager.shared.song(title: titleTextField.text ?? &quot;&quot;, releaseDate: releaseDateTextField.text ?? &quot;&quot;, singer: singer!)<br>songs.append(song)<br>tableView.reloadData()<br>DataManager.shared.save()</pre><p>We must use optional binding for the <strong>Singer</strong> object to make sure if it contains a value or not. If yes, the NSFetchRequest will be executed to fetch the songs in <strong>viewDidLoad() </strong>on<strong> SongVC</strong>.</p><pre><strong>override</strong> <strong>func</strong> viewDidLoad() {<br>  <strong>super</strong>.viewDidLoad()<br>  setupUI()</pre><pre><strong>  if</strong> <strong>let</strong> singer = singer {<br>    songs = DataManager.shared.songs(singer: singer)<br>  }<br>  tableView.reloadData()<br>}</pre><p>Songs will be displayed in tableView the same way singers are by adding the proper code to UITableViewDelegate, UITableViewDataSource extension.</p><p>Try to implement it by yourself. Do not worry if you can not because you can find it in the source code linked at the end of this article 😄. The implementation of editing and deletion is also the same so give it a try!</p><p>And that is it. I hope you find it helpful and enjoy implementing it. You can reuse it in different relationships as you please.</p><p>Click here for the source code:</p><p><a href="https://github.com/meggsila/CoreDataRelationship">GitHub - meggsila/CoreDataRelationship</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f51e19b28326" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>