{"id":1376,"date":"2014-10-30T19:48:21","date_gmt":"2014-10-31T01:48:21","guid":{"rendered":"http:\/\/jamesonquave.com\/blog\/?p=1376"},"modified":"2015-02-16T16:37:22","modified_gmt":"2015-02-16T22:37:22","slug":"core-data-in-swift-tutorial-part-3","status":"publish","type":"post","link":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/","title":{"rendered":"Core Data in Swift Tutorial (Part 3)"},"content":{"rendered":"<p><strong>This post compatible with Xcode 6.3 Beta, Updated on February 16, 2014<\/strong><\/p>\n<p>This is part three of a tutorial series covering the usage of Core Data in Swift to write iOS apps with persistence. If you haven&#8217;t read part one yet, <a href=\"http:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-1\/\">read that first<\/a>.<\/p>\n<p>If you really want to get your feet wet,  <a href=\"http:\/\/jamesonquave.com\/swiftebook\">my Swift book which is now available for pre-order<\/a> with early access.<\/p>\n<p><i><br \/>\nIn this (somewhat lengthy) section of the tutorial, we&#8217;ll implement deleting rows, adding rows, and persisting the data so it stays put even after app close. When we&#8217;re done we&#8217;ll have something that works like the video here:<br \/>\n<\/i><\/p>\n<p><iframe loading=\"lazy\" width=\"420\" height=\"315\" src=\"\/\/www.youtube.com\/embed\/UX4lRZwQlos\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n<p><strong>Implementing swipe-to-delete<\/strong><\/p>\n<p>Before we get started, let&#8217;s go ahead and wipe out the filtering we were toying around with in fetchLog(). We&#8217;ll just simplify fetchLog to look like this:<\/p>\n<pre class=\"brush: js;\">\r\nfunc fetchLog() {\r\n    let fetchRequest = NSFetchRequest(entityName: \"LogItem\")\r\n    \r\n    \/\/ Create a sort descriptor object that sorts on the \"title\"\r\n    \/\/ property of the Core Data object\r\n    let sortDescriptor = NSSortDescriptor(key: \"title\", ascending: true)\r\n    \r\n    \/\/ Set the list of sort descriptors in the fetch request,\r\n    \/\/ so it includes the sort descriptor\r\n    fetchRequest.sortDescriptors = [sortDescriptor]\r\n    \r\n    if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [LogItem] {\r\n        logItems = fetchResults\r\n    }\r\n}\r\n<\/pre>\n<p>First, we just need to implement the canEditRowAtIndexPath callback from the UITableViewDataSource protocol. In our case, we&#8217;ll be able to delete anything, so we can just return true all the time. But, if for example we had some entries locked, we could check the indexPath and return false instead.<\/p>\n<pre class=\"brush: js;\">\r\nfunc tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {\r\n    return true\r\n}\r\n<\/pre>\n<p>Now, this tells the UITableView that we can edit these rows (deleting a row is a form of editing it.) But iOS will look for one more callback, tableView:commitEditingStyle:forRowAtIndexPath.<\/p>\n<pre class=\"brush: js\">\r\nfunc tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {\r\n    if(editingStyle == .Delete ) {\r\n        \/\/ Find the LogItem object the user is trying to delete\r\n        let logItemToDelete = logItems[indexPath.row]\r\n        \r\n        \/\/ Delete it from the managedObjectContext\r\n        managedObjectContext?.deleteObject(logItemToDelete)\r\n        \r\n        \/\/ Refresh the table view to indicate that it's deleted\r\n        self.fetchLog()\r\n        \r\n        \/\/ Tell the table view to animate out that row\r\n        logTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)\r\n    }\r\n}\r\n<\/pre>\n<p>This method gets called on the UITableViewDataSource as well, and it&#8217;s called upon performing the swipe-to-delete action on iOS. Without this method implemented swipe-to-delete will simply be disabled.<\/p>\n<p>So, we add it in here and we take action based on the deleted row. When the editingStyle is .Delete, we know that the intention is the delete the row. We identify the row data from our logItems array by accessing the indexPath.row index (this is the integer row number of the deleted item in the table view)<\/p>\n<p>Next, we delete the object in Core Data using the deleteObject method on the managedObjectContext we computed earlier.<\/p>\n<p>Now, we can update the table view itself by calling self.fetchLog() again. We could stop here and called reloadData() on the tableview, but instead we can use the deleteRowsAtIndexPaths:withRowAnimation: method of the table view to get some free table view animations.<\/p>\n<p>Try running the app now, and you can swipe left on a table view row to delete it. You&#8217;ll get some free animations, and this data is updated in our Core Data object graph.<\/p>\n<p>Being able to delete objects is great, but we also need to be able to add items if this is going to be a real app. Let&#8217;s make an adjustment to our viewDidLoad() code.<\/p>\n<p>We have a line in our code right now setting the height of the tableview:<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ Reduce the total height by 20 points\r\nviewFrame.size.height -= 20\r\n<\/pre>\n<p>If we&#8217;re going to add a button we&#8217;ll just to adjust this even further, by the amount of the button height, and create the button. So we&#8217;ll set that up now:<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ Add in the \"+\" button at the bottom\r\nlet addButton = UIButton(frame: CGRectMake(0, UIScreen.mainScreen().bounds.size.height - 44, UIScreen.mainScreen().bounds.size.width, 44))\r\naddButton.setTitle(\"+\", forState: .Normal)\r\naddButton.backgroundColor = UIColor(red: 0.5, green: 0.9, blue: 0.5, alpha: 1.0)\r\naddButton.addTarget(self, action: \"addNewItem\", forControlEvents: .TouchUpInside)\r\nself.view.addSubview(addButton)\r\n\r\n\/\/ Reduce the total height by 20 points for the status bar, and 44 points for the bottom button\r\nviewFrame.size.height -= (20 + addButton.frame.size.height)\r\n<\/pre>\n<p>First, we create a UIButton instance, and set it&#8217;s size to be equal to 44&#215;44 points. The position will be at an x coordinate of 0, and a y coordinate of whatever the screen height is, minus the size of the button (44 points) This puts it at the bottom of the screen.<br \/>\nWe set the title to simply &#8220;+&#8221; to indicate adding a new item, set the background color, and add a target.<\/p>\n<p>The target is a selector that fires when the button is clicked. In our case, the selector is called &#8220;addNewItem&#8221;, which means we need to create this as a function&#8230;<\/p>\n<pre class=\"brush: js;\">\r\nlet addItemAlertViewTag = 0\r\nlet addItemTextAlertViewTag = 1\r\nfunc addNewItem() {\r\n    \r\n    var titlePrompt = UIAlertController(title: \"Enter Title\",\r\n        message: \"Enter Text\",\r\n        preferredStyle: .Alert)\r\n    \r\n    var titleTextField: UITextField?\r\n    titlePrompt.addTextFieldWithConfigurationHandler {\r\n        (textField) -> Void in\r\n        titleTextField = textField\r\n        textField.placeholder = \"Title\"\r\n    }\r\n    \r\n    titlePrompt.addAction(UIAlertAction(title: \"Ok\",\r\n        style: .Default,\r\n        handler: { (action) -> Void in\r\n        if let textField = titleTextField {\r\n            println(textField.text)\r\n        }\r\n    }))\r\n    \r\n    self.presentViewController(titlePrompt,\r\n        animated: true,\r\n        completion: nil)\r\n}\r\n<\/pre>\n<p>This function will create a UIAlertController object with a text field by using the addTextFieldWithConfigurationHandler method. I&#8217;ll probably go over this as a separate blog post at some point, but for now it&#8217;s enough to know that when the handler on the &#8220;Ok&#8221; action gets called, the textField&#8217;s text value is inside of textField.text. We can use this to take input from the user without adding too much more view work.<\/p>\n<p>Okay, so now that we can detect the text entered, we can call a function to save the new item. We&#8217;ll call it saveNewItem:title<\/p>\n<pre class=\"brush: js;\">\r\nfunc saveNewItem(title : String) {\r\n    \/\/ Create the new  log item\r\n    var newLogItem = LogItem.createInManagedObjectContext(self.managedObjectContext!, title: title, text: \"\")\r\n    \r\n    \/\/ Update the array containing the table view row data\r\n    self.fetchLog()\r\n    \r\n    \/\/ Animate in the new row\r\n    \/\/ Use Swift's find() function to figure out the index of the newLogItem\r\n    \/\/ after it's been added and sorted in our logItems array\r\n    if let newItemIndex = find(logItems, newLogItem) {\r\n        \/\/ Create an NSIndexPath from the newItemIndex\r\n        let newLogItemIndexPath = NSIndexPath(forRow: newItemIndex, inSection: 0)\r\n        \/\/ Animate in the insertion of this row\r\n        logTableView.insertRowsAtIndexPaths([ newLogItemIndexPath ], withRowAnimation: .Automatic)\r\n    }\r\n}\r\n<\/pre>\n<p>Our method will take in the title as the only argument, and use the createInManagedObjectContext function we created earlier to add a new record with the title of <b>title<\/b>. For now, we can leave the text blank.<\/p>\n<p>Similar to how we did before with the deletion, we need to call fetchLog() to update our table view&#8217;s backing array, <b>logItems<\/b>.<\/p>\n<p>Finally, we can create our NSIndexPath for the new item, and called insertRowsAtIndexPaths to animate in the new row.<\/p>\n<p>Now, we just need to call the new method from the handler closure in the titlePrompt&#8217;s &#8220;Ok&#8221; button:<\/p>\n<pre class=\"brush: js; highlight: [5];\">\r\ntitlePrompt.addAction(UIAlertAction(title: \"Ok\",\r\n    style: .Default,\r\n    handler: { (action) -> Void in\r\n    if let textField = titleTextField {\r\n        self.saveNewItem(textField.text)\r\n    }\r\n}))\r\n<\/pre>\n<p>Running the app you can now add and delete rows to our table, backed by Core Data. If you close the app and restart it, you may notice that the data is reset every time. This is happening because Core Data doesn&#8217;t persist automatically, you must explicitly call save for that behavior. So as a final step in this tutorial section, let&#8217;s create a save() method in our ViewController.swift file.<\/p>\n<pre class=\"brush: js;\">\r\nfunc save() {\r\n    var error : NSError?\r\n    if(managedObjectContext!.save(&error) ) {\r\n        println(error?.localizedDescription)\r\n    }\r\n}\r\n<\/pre>\n<p>The API is pretty simple just call save() on the managedObjectContext. Optionally you can include an NSError pointer to capture errors. The save() function returns true on success, and false on failure. If the save succeeds, your data will now persist.<\/p>\n<p>Let&#8217;s call our save() method after the user adds or removes an item.<\/p>\n<pre class=\"brush: js; highlight: [5];\">\r\nfunc tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {\r\n...\r\n    \/\/ Tell the table view to animate out that row\r\n     logTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)\r\n     save()\r\n...\r\n<\/pre>\n<pre class=\"brush: js; highlight: [8];\">\r\nfunc saveNewItem(title : String) {\r\n...\r\n    if let newItemIndex = find(logItems, newLogItem) {\r\n        \/\/ Create an NSIndexPath from the newItemIndex\r\n        let newLogItemIndexPath = NSIndexPath(forRow: newItemIndex, inSection: 0)\r\n        \/\/ Animate in the insertion of this row\r\n        logTableView.insertRowsAtIndexPaths([ newLogItemIndexPath ], withRowAnimation: .Automatic)\r\n        save()\r\n...\r\n}\r\n<\/pre>\n<p>Running the app we now have a super nifty app where we can add or remove items from a list with neat animations!<\/p>\n<p>This concludes part 3 of the Core Data tutorial for now. The full source code can be found <a href=\"https:\/\/github.com\/jquave\/Core-Data-In-Swift-Tutorial\/tree\/Part3\/MyLog\">here<\/a>.<\/p>\n<p>In the next section, we&#8217;ll set up migrations so you can make changes to the schema of your live apps. <a href=\"http:\/\/jamesonquave.com\/blog\/core-data-migrations-swift-tutorial\/\">Core Data Migrations Tutorial in Swift &raquo;<\/a>.<\/p>\n<p>As always, be sure to <a href=\"http:\/\/eepurl.com\/sDFL9\">subscribe to my newsletter<\/a> for tutorial updates and more, and take a look at my upcoming <a href=\"http:\/\/jamesonquave.com\/swiftebook\/\">book<\/a>, which is now in <a href=\"http:\/\/jamesonquave.com\/swiftebook\/\">early access<\/a>.<\/p>\n<div class=\"tut-toc\">\n<a href=\"http:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-1\/\"  class=\"tutorial-part-button\">Part 1<\/a><br \/>\n<a href=\"http:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-2\/\" class=\"tutorial-part-button\">Part 2<\/a><br \/>\n<a href=\"http:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\" class=\"tutorial-part-button current\">Part 3<\/a><br \/>\n<a href=\"http:\/\/jamesonquave.com\/blog\/core-data-migrations-swift-tutorial\/\" class=\"tutorial-part-button\">Part 4<\/a>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>This post compatible with Xcode 6.3 Beta, Updated on February 16, 2014 This is part three of a tutorial series covering the usage of Core Data in Swift to write iOS apps with persistence. If you haven&#8217;t read part one yet, read that first. If you really want to get your feet wet, my Swift&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_links_to":"","_links_to_target":""},"categories":[25,10,32],"tags":[61,15,33,36],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Core Data in Swift Tutorial (Part 3) - Jameson Quave<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Core Data in Swift Tutorial (Part 3) - Jameson Quave\" \/>\n<meta property=\"og:description\" content=\"This post compatible with Xcode 6.3 Beta, Updated on February 16, 2014 This is part three of a tutorial series covering the usage of Core Data in Swift to write iOS apps with persistence. If you haven&#8217;t read part one yet, read that first. If you really want to get your feet wet, my Swift...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\" \/>\n<meta property=\"og:site_name\" content=\"Jameson Quave\" \/>\n<meta property=\"article:published_time\" content=\"2014-10-31T01:48:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-02-16T22:37:22+00:00\" \/>\n<meta name=\"author\" content=\"Jameson Quave\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jameson Quave\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\",\"url\":\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\",\"name\":\"Core Data in Swift Tutorial (Part 3) - Jameson Quave\",\"isPartOf\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\"},\"datePublished\":\"2014-10-31T01:48:21+00:00\",\"dateModified\":\"2015-02-16T22:37:22+00:00\",\"author\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc\"},\"breadcrumb\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/jamesonquave.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Core Data in Swift Tutorial (Part 3)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\",\"url\":\"https:\/\/jamesonquave.com\/blog\/\",\"name\":\"Jameson Quave\",\"description\":\"Using computer technology to educate, and improve lives.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc\",\"name\":\"Jameson Quave\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg\",\"caption\":\"Jameson Quave\"},\"sameAs\":[\"http:\/\/jamesonquave.com\"],\"url\":\"https:\/\/jamesonquave.com\/blog\/author\/jquave\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Core Data in Swift Tutorial (Part 3) - Jameson Quave","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/","og_locale":"en_US","og_type":"article","og_title":"Core Data in Swift Tutorial (Part 3) - Jameson Quave","og_description":"This post compatible with Xcode 6.3 Beta, Updated on February 16, 2014 This is part three of a tutorial series covering the usage of Core Data in Swift to write iOS apps with persistence. If you haven&#8217;t read part one yet, read that first. If you really want to get your feet wet, my Swift...","og_url":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/","og_site_name":"Jameson Quave","article_published_time":"2014-10-31T01:48:21+00:00","article_modified_time":"2015-02-16T22:37:22+00:00","author":"Jameson Quave","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jameson Quave","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/","url":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/","name":"Core Data in Swift Tutorial (Part 3) - Jameson Quave","isPartOf":{"@id":"https:\/\/jamesonquave.com\/blog\/#website"},"datePublished":"2014-10-31T01:48:21+00:00","dateModified":"2015-02-16T22:37:22+00:00","author":{"@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc"},"breadcrumb":{"@id":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/jamesonquave.com\/blog\/core-data-in-swift-tutorial-part-3\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/jamesonquave.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Core Data in Swift Tutorial (Part 3)"}]},{"@type":"WebSite","@id":"https:\/\/jamesonquave.com\/blog\/#website","url":"https:\/\/jamesonquave.com\/blog\/","name":"Jameson Quave","description":"Using computer technology to educate, and improve lives.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc","name":"Jameson Quave","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d9786c83345117d560bbeab0e1f26814?s=96&d=retro&r=pg","caption":"Jameson Quave"},"sameAs":["http:\/\/jamesonquave.com"],"url":"https:\/\/jamesonquave.com\/blog\/author\/jquave\/"}]}},"_links":{"self":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1376"}],"collection":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/comments?post=1376"}],"version-history":[{"count":25,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1376\/revisions"}],"predecessor-version":[{"id":1654,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1376\/revisions\/1654"}],"wp:attachment":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/media?parent=1376"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/categories?post=1376"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/tags?post=1376"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}