{"id":611,"date":"2014-06-03T08:41:48","date_gmt":"2014-06-03T14:41:48","guid":{"rendered":"http:\/\/jamesonquave.com\/blog\/?p=611"},"modified":"2015-09-02T20:41:41","modified_gmt":"2015-09-03T02:41:41","slug":"developing-ios-apps-using-swift-part-3-best-practices","status":"publish","type":"post","link":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/","title":{"rendered":"Developing iOS Apps Using Swift Part 3 &#8211; Best Practices"},"content":{"rendered":"<p><strong><i>This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015<\/i><\/strong><\/b><\/p>\n<p>In parts 1 &amp; 2 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven&#8217;t read that yet, check out <a title=\"Developing iOS Apps Using Swift Tutorial Part 1\" href=\"http:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-tutorial\/\">Part 1<\/a> and <a title=\"Developing iOS Apps Using Swift Tutorial Part 2\" href=\"http:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-tutorial-part-2\/\">Part 2<\/a>. If you enjoy these tutorials, I&#8217;m also working on a book full of quality content for Swift developers, and it&#8217;s available for <a href='http:\/\/jamesonquave.com\/swiftebook'>Pre-Order now<\/a>.<\/p>\n<p>In this section we&#8217;re going to take a stop and clean up some of the code we&#8217;ve created so far by removing our network logic from our view controller code, and fixing some other issues that will hurt our performance. This may not be the most glamorous part of writing a new app, but it&#8217;s important! Let&#8217;s do it&#8230;<\/p>\n<p>If you&#8217;re looking something that goes a little deeper than these tutorials, I also have written a book and created video tutorials which are available now. If that&#8217;s more your speed, check out my <a href=\"http:\/\/jamesonquave.com\/swiftebook\/\" alt=\"Swift Book\">Swift Book &#038; Video Packages &raquo;<\/a>.<\/p>\n<div style=\"border: 1px solid #eee; margin: 5px;\">\n<center><br \/>\n<a href=\"http:\/\/jamesonquave.com\/swiftebook\/\" alt=\"Get The Swift Book\"><img decoding=\"async\" src=\"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png\" height=\"150\" alt=\"Get The Swift Book\" \/><br \/>\nLearn About My Book &#038; Video Packages &raquo;<\/a><br \/>\n<\/center>\n<\/div>\n<p><strong>Split up the code<\/strong><\/p>\n<p>First things first, let&#8217;s rename our View Controller to be something more meaningful. Open up your ViewController.swift file and replace the class name &#8216;ViewController&#8217; with our new name, &#8216;SearchResultsViewController&#8217;. Rename the file as well to\u00a0SearchResultsViewController.swift.<\/p>\n<p>If you try to run the app from here you&#8217;ll get a crash. This is because our storyboard file hasn&#8217;t been updated to know about the new class! So open up the Main.storyboard, select your &#8216;View Controller&#8217; object in the scene (the left-hand side nav), and then select the Identity Inspector (right-hand side, third button.) <\/p>\n<p>From here let&#8217;s change the class from &#8216;ViewController&#8217; to &#8216;SearchResultsViewController&#8217;. Now we should be back on track. Check that the project still works, and let&#8217;s proceed.<\/p>\n<p><img src='http:\/\/jamesonquave.com\/tutImg\/ClassFixSB.png' \/><\/p>\n<p>Let&#8217;s now move the API code out to it&#8217;s own class. Right click in the xcode navigation pane and select &#8216;New File&#8230;&#8217;. This one is going to be a regular Swift file, under the iOS-&gt;Source navigation option.<\/p>\n<p><img src='http:\/\/jamesonquave.com\/tutImg\/SwiftFile.png' \/><\/p>\n<p>This one handles all API work, so I&#8217;m going to call it APIController.<\/p>\n<p>Now let&#8217;s grab our searchItunesFor() function, and cut and paste it out of the Search controller and in to the APIController, inside of a new class &#8216;APIController&#8217;.<\/p>\n<p>Your complete APIController.swift file should look something like this:<\/p>\n<pre class=\"brush: js;\">\r\nimport Foundation\r\n\r\nclass APIController {\r\n    \r\n    func searchItunesFor(searchTerm: String) {\r\n        \r\n        \/\/ The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs\r\n        let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(\" \", withString: \"+\", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)\r\n        \r\n        \/\/ Now escape anything else that isn't URL-friendly\r\n        if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {\r\n            let urlPath = \"http:\/\/itunes.apple.com\/search?term=\\(escapedSearchTerm)&media=software\"\r\n            let url = NSURL(string: urlPath)\r\n            let session = NSURLSession.sharedSession()\r\n            let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in\r\n                println(\"Task completed\")\r\n                if(error != nil) {\r\n                    \/\/ If there is an error in the web request, print it to the console\r\n                    println(error.localizedDescription)\r\n                }\r\n                var err: NSError?\r\n                \r\n                var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary\r\n                if(err != nil) {\r\n                    \/\/ If there is an error parsing JSON, print it to the console\r\n                    println(\"JSON Error \\(err!.localizedDescription)\")\r\n                }\r\n                let results: NSArray = jsonResult[\"results\"] as NSArray\r\n                dispatch_async(dispatch_get_main_queue(), {\r\n                    self.tableData = results\r\n                    self.appsTableView!.reloadData()\r\n                })\r\n            })\r\n            \r\n            task.resume()\r\n        }\r\n    }\r\n    \r\n}\r\n<\/pre>\n<p>\nIf you try to build this straight away you&#8217;ll see three errors:<\/p>\n<p>1) searchItunesFor() is now undefined in our SearchResultsViewController.<br \/>\n2) self.tableData\u00a0is now undefined in the APIController.<br \/>\n3) self.appsTableView is now undefined in the APIController.<\/p>\n<p>To deal with this we need to let these objects know about each other. So let&#8217;s make APIController a child object of our SearchResultsViewController. It&#8217;s pretty easy to do, just add the following line underneath your SearchResultsViewController class definition:<\/p>\n<pre class=\"brush: js;\">let api = APIController()<\/pre>\n<p>Now modify the line that calls searchItunesFor() to this:<\/p>\n<pre class=\"brush: js;\">api.searchItunesFor(\"Angry Birds\")<\/pre>\n<p>The only difference here is that we&#8217;re calling the method from an instance of an APIController, as opposed to doing this all from a method in the View Controller.<\/p>\n<p>That takes care of the first set of errors, but now we need to also get APIController&#8217;s results back to the SearchResultsViewController. We would like for our API controller to be able to respond to arbitrary API calls, so we should define a protocol that views can subscribe to in order to get results!<\/p>\n<p><strong>Defining an API protocol<\/strong><\/p>\n<p>There are two erroring lines in our APIController right now referencing the tableData results, let&#8217;s just remove these. We&#8217;re going to use something a little cleaner.<\/p>\n<pre class=\"brush: js;\">\r\ndispatch_async(dispatch_get_main_queue(), {  \/\/ DELETE ME\r\n    self.tableData = results                 \/\/ DELETE ME\r\n    self.appsTableView!.reloadData()         \/\/ DELETE ME\r\n})                                           \/\/ DELETE ME\r\n<\/pre>\n<p>\nAbove the class definition in our APIController, let&#8217;s add a protocol that declare the function didReceiveAPIResults() as a required implementation. Note that the protocol goes <strong>outside<\/strong> of the class definition, so make sure it&#8217;s outside of the APIController{} curly braces.<\/p>\n<p>&nbsp;<\/p>\n<pre class=\"brush: js;\">\r\nprotocol APIControllerProtocol {\r\n&nbsp;&nbsp;&nbsp;func didReceiveAPIResults(results: NSArray)\r\n}\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>This doesn&#8217;t do anything on it&#8217;s own, but now we can add the protocol to our SearchResultsViewController. Not adhering to the protocol will now cause an error, so we don&#8217;t make the silly mistake of not implementing didReceiveAPIResults!<\/p>\n<p><strong>Adhering to the protocol<\/strong><\/p>\n<p>Now modify your SearchResultsViewController to adhere to the protocol:<\/p>\n<pre class=\"brush: js;\">class SearchResultsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {<\/pre>\n<p>Building now should give an error that SearchResultsViewController doesn&#8217;t conform to the APIControllerProtocol, great! This is exactly what a protocol does. It causes the compiler to spit out errors if you intended to implement a protocol, but didn&#8217;t completely do it. In this case it&#8217;s barfing because we&#8217;re missing the didReceiveAPIResults() callback method. Let&#8217;s add it&#8230;<\/p>\n<p>We just need to add the function within our SearchResultsViewController class. It will look pretty much like the contents of our dataTaskWithURL closure from before.<\/p>\n<pre class=\"brush: js;\">\r\nfunc didReceiveAPIResults(results: NSArray) {\r\n    dispatch_async(dispatch_get_main_queue(), {\r\n        self.tableData = results\r\n        self.appsTableView!.reloadData()\r\n    })\r\n}<\/pre>\n<p>The one thing left to do is change our API Controller to include a delegate object, and to call this method when the connection finished loading some API data.<\/p>\n<p><strong>Using the Protocol<\/strong><\/p>\n<p>Back in our APIController.swift file, let&#8217;s add the delegate and an empty constructor, just under the class definition.<\/p>\n<pre class=\"brush: js;\">\r\nvar delegate: APIControllerProtocol?\r\n<\/pre>\n<p>The question mark at the end here indicates that delegate is an *optional* value. Without the question mark, we will get a compiler error for not using an initial value for this variable, but with it we&#8217;re okay. The delegate object can be of any class here, as long as it adheres to the APIControllerProtocol by defining the method didReceiveAPIResults(), as we have done in our SearchResultsViewController<\/p>\n<p>Now that we have the delegate variable added, let&#8217;s head back to our SearchResultsViewController, and in the method viewDidLoad, let&#8217;s set the delegate for our api controller to itself, so it will receive delegate function calls.<\/p>\n<pre class=\"brush: js;\">\r\napi.delegate = self\r\n<\/pre>\n<p>Finally, in the dataTaskWithURL closure inside of our searchItunesFor() method, let&#8217;s add our protocol method where our tableview reloading code used to be. This will pass the responsibility of reloading the UI to the delegate, which in this case is SearchResultsViewController.<\/p>\n<p>This goes in place of where we delete the lines refreshing the tableview earlier on.<\/p>\n<pre class=\"brush: js;\">\r\nif let results: NSArray = jsonResult[\"results\"] as? NSArray {\r\n    self.delegate?.didReceiveAPIResults(results)\r\n}\r\n<\/pre>\n<p>Here is the completed APIController.swift code up to this point:<\/p>\n<pre class=\"brush: js;\">\r\nimport Foundation\r\n\r\nprotocol APIControllerProtocol {\r\n    func didReceiveAPIResults(results: NSArray)\r\n}\r\n\r\nclass APIController {\r\n    var delegate: APIControllerProtocol?\r\n    \r\n    func searchItunesFor(searchTerm: String) {\r\n        \/\/ The iTunes API wants multiple terms separated by + symbols, so replace spaces with + signs\r\n        let itunesSearchTerm = searchTerm.stringByReplacingOccurrencesOfString(\" \", withString: \"+\", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)\r\n        \r\n        \/\/ Now escape anything else that isn't URL-friendly\r\n        if let escapedSearchTerm = itunesSearchTerm.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {\r\n            let urlPath = \"http:\/\/itunes.apple.com\/search?term=\\(escapedSearchTerm)&media=software\"\r\n            let url = NSURL(string: urlPath)\r\n            let session = NSURLSession.sharedSession()\r\n            let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in\r\n                println(\"Task completed\")\r\n                if(error != nil) {\r\n                    \/\/ If there is an error in the web request, print it to the console\r\n                    println(error.localizedDescription)\r\n                }\r\n                var err: NSError?\r\n                if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {\r\n                    if(err != nil) {\r\n                        \/\/ If there is an error parsing JSON, print it to the console\r\n                        println(\"JSON Error \\(err!.localizedDescription)\")\r\n                    }\r\n                    if let results: NSArray = jsonResult[\"results\"] as? NSArray {\r\n                        self.delegate?.didReceiveAPIResults(results)\r\n                    }\r\n                }\r\n            })\r\n            \r\n            \/\/ The task is just an object with all these properties set\r\n            \/\/ In order to actually make the web request, we need to \"resume\"\r\n            task.resume()\r\n        }\r\n    }\r\n    \r\n}\r\n<\/pre>\n<div style=\"background-color: #f9ffd6; border: 1px solid #yellow;padding: 5px;\">\n<h2>Common issue here<\/h2>\n<p> *** Terminating app due to uncaught exception &#8216;NSUnknownKeyException&#8217;, reason: &#8216;[<UIViewController 0x7d27ddd0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key appsTableView.&#8217;<\/p>\n<p>If you get this issue, it could be caused by a few bugs in Xcode surrounding the renaming of View Controller subclasses. The best way to fix this is to open up the storyboard, select the &#8216;Search Results View Controller&#8217; object. Then in the identity inspector change its class back to UIViewController, build the app, then change it back to &#8216;SearchResultsViewController&#8217;. This resets some things and fixes the issue.\n<\/p><\/div>\n<p><strong>PHEW!<\/strong><\/p>\n<p>Okay, I know that might have seemed like a lot of boilerplate, and now our app does the exact same thing as it did before, but now we have something much more flexible. We can now use APIController for any API call to the iTunes search API, and have a custom delegate get the response. I think we ran out of time on this one, so we&#8217;ll move on to the interaction in <a title=\"Developing iOS Apps Using Swift Part 4 \u2013 Adding Interactions\" href=\"http:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-4-adding-interactions\/\">Part 4<\/a>. If you want to follow along make sure to <a title=\"Sign Up\" href=\"http:\/\/jamesonquave.us6.list-manage.com\/subscribe\/post?u=1d2576bf288fe2fd7fa71bd20&amp;id=6c787ed58a\">sign up for them via e-mail<\/a>.<\/p>\n<p>I felt this step was more important than fixing the performance issues, but I promise we&#8217;ll speed things up before we&#8217;re done. There are just some things we need to get done first. Hold tight!<\/p>\n<p>Full code for this section visible <a href=\"https:\/\/github.com\/jquave\/Swift-Tutorial\/tree\/Part3\">here<\/a>.<\/p>\n<p><a title=\"Developing iOS Apps Using Swift Part 4 \u2013 Adding Interactions\" href=\"http:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-4-adding-interactions\/\">Go to Part 4 -&gt;<\/a><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 &amp; 2 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven&#8217;t read that&#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":[26,25,10,32],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Developing iOS Apps Using Swift Part 3 - Best Practices - 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\/developing-ios-apps-using-swift-part-3-best-practices\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Developing iOS Apps Using Swift Part 3 - Best Practices - Jameson Quave\" \/>\n<meta property=\"og:description\" content=\"This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 &amp; 2 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven&#8217;t read that...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/\" \/>\n<meta property=\"og:site_name\" content=\"Jameson Quave\" \/>\n<meta property=\"article:published_time\" content=\"2014-06-03T14:41:48+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-09-03T02:41:41+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png\" \/>\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=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/\",\"url\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/\",\"name\":\"Developing iOS Apps Using Swift Part 3 - Best Practices - Jameson Quave\",\"isPartOf\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\"},\"datePublished\":\"2014-06-03T14:41:48+00:00\",\"dateModified\":\"2015-09-03T02:41:41+00:00\",\"author\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc\"},\"breadcrumb\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/jamesonquave.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Developing iOS Apps Using Swift Part 3 &#8211; Best Practices\"}]},{\"@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":"Developing iOS Apps Using Swift Part 3 - Best Practices - 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\/developing-ios-apps-using-swift-part-3-best-practices\/","og_locale":"en_US","og_type":"article","og_title":"Developing iOS Apps Using Swift Part 3 - Best Practices - Jameson Quave","og_description":"This section completely updated to reflect changes in Xcode 6.3, as of April 16, 2015 In parts 1 &amp; 2 we went over some basics of Swift, and set up a simple example project that creates a Table View and a puts some API results from iTunes inside of them. If you haven&#8217;t read that...","og_url":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/","og_site_name":"Jameson Quave","article_published_time":"2014-06-03T14:41:48+00:00","article_modified_time":"2015-09-03T02:41:41+00:00","og_image":[{"url":"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png"}],"author":"Jameson Quave","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jameson Quave","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/","url":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/","name":"Developing iOS Apps Using Swift Part 3 - Best Practices - Jameson Quave","isPartOf":{"@id":"https:\/\/jamesonquave.com\/blog\/#website"},"datePublished":"2014-06-03T14:41:48+00:00","dateModified":"2015-09-03T02:41:41+00:00","author":{"@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/db6184f355c7f4e3b876d0f228c2fcfc"},"breadcrumb":{"@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/jamesonquave.com\/blog\/developing-ios-apps-using-swift-part-3-best-practices\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/jamesonquave.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Developing iOS Apps Using Swift Part 3 &#8211; Best Practices"}]},{"@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\/611"}],"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=611"}],"version-history":[{"count":31,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/611\/revisions"}],"predecessor-version":[{"id":1937,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/611\/revisions\/1937"}],"wp:attachment":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/media?parent=611"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/categories?post=611"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/tags?post=611"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}