{"id":894,"date":"2014-11-20T23:24:44","date_gmt":"2014-11-21T05:24:44","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=894"},"modified":"2015-06-06T16:32:05","modified_gmt":"2015-06-06T22:32:05","slug":"corelocation-on-ios-8-with-swift","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/corelocation-on-ios-8-with-swift\/","title":{"rendered":"CoreLocation on iOS 8 with Swift"},"content":{"rendered":"<p><i>Editor&#8217;s Note:  This is part two in a three part series.  The goal in this series is to develop a fully functional (and useful) iOS application that is capable of pushing weather alerts based on your location to your iOS device.  The series is broken down as follows:<\/p>\n<ul>\n<li>Part One &#8211; Developing a Push Notification-capable application with Parse and Swift\n<li><b>Part Two &#8211; Using CoreLocation with Swift<\/b>\n<li>Part Three &#8211; Integrating in a Weather Service API\n<\/ul>\n<p><\/i><\/p>\n<p>In this second part of our three part series we&#8217;ll be expanding on our weather alerts application started in <a href=\"http:\/\/dev.iachieved.it\/iachievedit\/?p=823\">Part One<\/a>.  A warning!  The code examples that follow build upon the code in Part 1.<\/p>\n<h2>CoreLocation<\/h2>\n<p><a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/CoreLocation\/Reference\/CoreLocation_Framework\/\">CoreLocation<\/a> is the iOS framework which provides <i>location-based information<\/i> to your application.  By <i>location<\/i> we mean either the phone&#8217;s location in a GPS-coordinate space, or we can mean in reference to <a href=\"http:\/\/en.wikipedia.org\/wiki\/IBeacon\">beacon devices<\/a>.  In this tutorial we are interested in the GPS location of the device.<\/p>\n<p>Before we get started using CoreLocation, it is important to note that Apple has raised the bar in terms of how resource-intensive your application is with regards to using location-based services.  Our application, <i><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/?p=451\">Lewis and Clark<\/a><\/i>, made use of CoreLocation to send frequent and accurate GPS locations to determine whether a user was in a new county or state.  Apple had approved the application through several iterations and then rejected the application on the grounds it was requesting &#8220;navigation-level&#8221; GPS coordinates and was not a navigation application.  When writing an application that makes use of location updating strive to only request the bare minimum accuracy necessary for your application to work correctly.<\/p>\n<p>Okay, let&#8217;s get started.  In your weatheralerts Xcode project, create a new Swift file called <code>CoreLocationController.swift<\/code>.  If you don&#8217;t have the project you can get the version from <a href=\"https:\/\/bitbucket.org\/joeiachievedit\/weatheralerts\/get\/part1.zip\">here<\/a>.  Make sure and read the Part One post to obtain API keys for Parse and add them to your project.<\/p>\n<p>CoreLocation uses the <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/General\/Conceptual\/DevPedia-CocoaCore\/Delegation.html\">delegation pattern<\/a>, so our class needs to declare that is implements the <code>CLLocationManagerDelegate<\/code> protocol.  If you are unfamiliar with using the delegation pattern in iOS, see the following excellent tutorials:<\/p>\n<ul>\n<li><a href=\"http:\/\/chrisrisner.com\/31-Days-of-iOS--Day-6\u2013The-Delegate-Pattern\">The iOS Delegate Pattern<\/a>\n<li><a href=\"http:\/\/www.raywenderlich.com\/46988\/ios-design-patterns\">iOS Design Patterns<\/a>\n<\/ul>\n<p>Back to the code!  Here is our initial content for <code>CoreLocationController.swift<\/code>:<\/p>\n<pre>\r\nimport Foundation\r\nimport CoreLocation\r\n\r\nclass CoreLocationController : NSObject, CLLocationManagerDelegate {\r\n}\r\n<\/pre>\n<p>Next let&#8217;s declare a member variable <code>locationManager<\/code> and configure it in the <code>init()<\/code> method as follows (this code goes in your <code>CoreLocationManager<\/code> class!):<\/p>\n<pre>\r\n    var locationManager:CLLocationManager = CLLocationManager()\r\n\r\n    override init() {\r\n      super.init()\r\n      self.locationManager.delegate = self\r\n      self.locationManager.requestAlwaysAuthorization()\r\n    }\r\n<\/pre>\n<p><b>Note:<\/b>  To declare Swift classes as delegates you must first inherit from <code>NSObject<\/code>.  See <a href=\"http:\/\/stackoverflow.com\/questions\/24650325\/why-in-swift-we-cannot-adopt-a-protocol-without-inheritance-a-class-from-nsobjec\">Stackoverflow<\/a> for a good answer as to why.<\/p>\n<p>Before we forget, go to the <code>AppDelegate<\/code> and add:<\/p>\n<pre>\r\n  var coreLocationController:CoreLocationController?\r\n<\/pre>\n<p>as a member variable after the <code>pushNotificationController<\/code> declaration, and in the <code>didFinishLaunchingWithOptions<\/code> function add:<\/p>\n<pre>\r\n    self.coreLocationController     = CoreLocationController()\r\n<\/pre>\n<p>after the <code>self.pushNotificationController = PushNotificationController()<\/code> line.<\/p>\n<h2>Delegate Methods<\/h2>\n<p>There are a number of CoreLocation delegate methods we need to implement in our <code>CoreLocationController<\/code> class, but the first we will implement is <code>locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus)<\/code>.  iOS 8 introduced <a href=\"http:\/\/nevan.net\/2014\/09\/core-location-manager-changes-in-ios-8\/\">changes<\/a> to how apps are authorized to use CoreLocation, and we want to capture when our app moves from a given authorization state to the <code>Authorized<\/code> state.  To capture that transition we implement the delegate method:<\/p>\n<pre>\r\n  func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {\r\n    println(\"didChangeAuthorizationStatus\")\r\n    \r\n    switch status {\r\n    case .NotDetermined:\r\n      println(\".NotDetermined\")\r\n      break\r\n      \r\n    case .Authorized:\r\n      println(\".Authorized\")\r\n      self.locationManager.startUpdating()\r\n      break\r\n      \r\n    case .Denied:\r\n      println(\".Denied\")\r\n      break\r\n      \r\n    default:\r\n      println(\"Unhandled authorization status\")\r\n      break\r\n    \r\n    }\r\n  }\r\n<\/pre>\n<p>If we tried to compile and run this code as is you would notice that no authorization request is made.  iOS 8 requires you to not only request authorization status (with the code <code>requestAlwaysAuthorization<\/code> function), but also provide a description as to <i>why<\/i> you are requesting a given authorization.  The text description is given by the key <code>NSLocationAlwaysUsageDescription<\/code> (when requesting <i>always authorization<\/i>, which is used to receive location updates while the app is in the background) in your <code>Info.plist<\/code>.  To add it, edit your <code>Info.plist<\/code> in Xcode.  I prefer to right-click in the <code>Info.plist<\/code> editor window and <b>Show Raw Keys\/Values<\/b> before inserting a new key.  <\/p>\n<p>In this example we&#8217;ve added our key <code>NSLocationAlwaysUsageDescription<\/code> and the reason we give is &#8220;Your location is used to send you timely weather alerts in your immediate area.&#8221;<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_locationalways_usage.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_locationalways_usage.png\" alt=\"tut_locationalways_usage\" width=\"883\" height=\"313\" class=\"alignnone size-full wp-image-897\" \/><\/a><br \/>\n<b>Show Raw Keys\/Values<\/b><\/p>\n<p>Run the application again and you should see:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_access_location.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_access_location.png\" alt=\"tut_access_location\" width=\"320\" height=\"568\" class=\"alignnone size-full wp-image-908\" \/><\/a><\/p>\n<p>Press <b>Allow<\/b> and your application should switch to an <code>.Authorized<\/code> status.  If you have the same <code>println<\/code> statements in your code as above you will see in the console log:<\/p>\n<pre>\r\ndidChangeAuthorizationStatus\r\n.Authorized\r\n<\/pre>\n<p>Notice that when our app changed to <code>.Authorized<\/code> we call <code>self.locationManager.startUpdating()<\/code>.  <code>startUpdating()<\/code> is a method on <code>CLLocationManager<\/code> which will be responsible for obtaining GPS coordinates and delivering them to us.  And since this is a delegate pattern, we need to implement the function that will be called when <code>CLLocationManager<\/code> has GPS coordinates:  <code>locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)<\/code>.<\/p>\n<p>Here&#8217;s our initial implementation of this delegate function:<\/p>\n<pre>\r\n  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {\r\n    \r\n    let location = locations.last as CLLocation\r\n    \r\n    println(\"didUpdateLocations:  \\(location.coordinate.latitude), \\(location.coordinate.longitude)\")\r\n    \r\n  }\r\n<\/pre>\n<p>Run the application again and you should begin receiving a stream of locations.  Too many locations as a matter of fact, and they are all likely right on the same location.  We want to fine tune how many location updates we receive, as well as our relax our accuracy requirements.  <\/p>\n<p>Consider our use-case, receiving weather alerts relative to our location.  Does it matter if our GPS location is accurate down to a meter?  No.  Chances are a weather alert in your area is going to be the same as in your next-door neighbor&#8217;s area, i.e., you are in the same &#8220;area&#8221;.  We can greatly reduce the demands of our application without sacrificing accuracy by relaxing our GPS accuracy to a kilometer.  Moreover, we don&#8217;t need any additional updates to our location unless we move significantly, say 3 kilometers.  This can be achieved by adding the following code after <code>self.locationManager.delegate = self<\/code> in our <code>init()<\/code> routine.<\/p>\n<pre>\r\n    self.locationManager.distanceFilter  = 3000 \/\/ Must move at least 3km\r\n    self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer \/\/ Accurate within a kilometer\r\n<\/pre>\n<p>An illustration:<br \/>\n<a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_accuracy_distance.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_accuracy_distance.png\" alt=\"tut_accuracy_distance\" width=\"382\" height=\"327\" class=\"alignnone size-full wp-image-910\" \/><\/a><\/p>\n<p>The location reported by our phone is the green dot.  Because our accuracy is set to one kilometer we could <i>actually<\/i> be anywhere in the blue circle.  Again, we don&#8217;t really care.  On the edge of the blue circle, right in the center, etc., its all the same for a weather alert.  The red circle represents our <i>distance filter<\/i>, that is, the distance we have to <i>move<\/i> before we will receive another update.  As long as our green dot stays within the red circle, we <i>won&#8217;t<\/i> receive an update from CoreLocation, which again is fine for our purposes.<\/p>\n<p><b>Note:<\/b>  In our application we might even be better served by <a href=\"http:\/\/www.developer-tech.com\/news\/2014\/may\/19\/ios-significant-location-change-ios-70-and-71\/\">significant location change<\/a> updates from iOS, but for our purposes we&#8217;ll use standard location updating.<\/p>\n<h2>Reverse Geocoding<\/h2>\n<p>We now have the basics for receiving location updates whenever our phone moves approximately 3 kilometers from its previous location, and our location is accurate within a kilometer.  Our weather API will require us to pass city, state, and country information to provide weather alerts.  We can obtain this information by using the <i>reverse geocoding<\/i> feature of the <code><a href=\"https:\/\/developer.apple.com\/library\/IOs\/documentation\/CoreLocation\/Reference\/CLGeocoder_class\/index.html\">CLGeocoder<\/a><\/code> class.  Reverse geocoding relies on databases to take GPS coordinates and return the &#8220;address&#8221; of that location.  iOS encapsulates the returned data in a <code><a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/CoreLocation\/Reference\/CLPlacemark_class\/\">CLPlacemark<\/a><\/code> object which has various attributes we can extract as follows (this code goes in our <code>didUpdateLocations<\/code> delegate function):<\/p>\n<pre>\r\n    let geocoder = CLGeocoder()\r\n    geocoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, e) -> Void in\r\n      if let error = e {\r\n        println(\"Error:  \\(e.localizedDescription)\")\r\n      } else {\r\n        let placemark = placemarks.last as CLPlacemark\r\n        \r\n        let userInfo = [\r\n          \"city\":     placemark.locality,\r\n          \"state\":    placemark.administrativeArea,\r\n          \"country\":  placemark.country\r\n        ]\r\n        \r\n        println(\"Location:  \\(userInfo)\")\r\n\r\n      }\r\n    })\r\n<\/pre>\n<p>We&#8217;ll note that the call <code>reverseGeocodeLocation<\/code> returns immediately, and it is only after the reverse geocoding results are available (a network request is made to Apple servers to look up the data) is the <code>completionHandler<\/code> block called.  Heed the warning in Apple&#8217;s documentation when using reverse geocoding!  <i>Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail. When the maximum rate is exceeded, the geocoder passes an error object with the value kCLErrorNetwork to your completion handler.<\/i><\/p>\n<h2>Simulating Movement<\/h2>\n<p>At some point we&#8217;re going to need to test our application and ensure that current weather alerts are recognized and displayed to the user.  I live in North Texas, and while we do get severe weather from time to time (tornados anyone?), we don&#8217;t have severe weather <i>every day<\/i>.  So how are we going to test our application?  Simple.  We will take a look at Wunderground&#8217;s severe weather <a href=\"http:\/\/www.wunderground.com\/severe.asp\">alert map<\/a>, pick a city within an alert area, look up its GPS coordinates, and give that information to Xcode to simulate for us.<\/p>\n<p>Before we go into the details of getting the GPS address of a city of interest, let&#8217;s start with creating a GPX file in Xcode for GPS-location simulation.  To create a GPX file use Xcode <b>File &#8211; New &#8211; File<\/b> and select <b>iOS Resource &#8211; GPX File<\/b><\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_create_gpx_file.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_create_gpx_file.png\" alt=\"tut_create_gpx_file\" width=\"732\" height=\"431\" class=\"alignnone size-full wp-image-902\" \/><\/a><\/p>\n<p>Name the file <code>Waypoints<\/code>.  Edit the file in Xcode and delete the default waypoint (the lone &lt;wpt\/&gt; element) and then add:<\/p>\n<pre>\r\n  &lt;wpt lat=\"42.904722\" lon=\"-78.849444\"&gt;&lt;\/wpt&gt;\r\n<\/pre>\n<p>This coordinate is in Buffalo, New York, which, as of this writing, has a winter weather alert.  Run the application and then use Xcode to simulate the location based upon the content of the <code>Waypoints<\/code> GPX file.  This is done within Xcode, on the bar above the <b>Debug Area<\/b> notice the location arrow:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_simulate_waypoints.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_simulate_waypoints.png\" alt=\"tut_simulate_waypoints\" width=\"189\" height=\"283\" class=\"alignnone size-full wp-image-903\" \/><\/a><\/p>\n<p>You should see after triggering the location simulator to use <code>Waypoints<\/code>:<\/p>\n<pre>\r\ndidUpdateLocations:  42.904722, -78.849444\r\nLocation:  [country: United States, city: Buffalo, state: NY]\r\n<\/pre>\n<h2>Posting Our Location<\/h2>\n<p>I am a big fan of the <i>notification model<\/i> in iOS application development.  Rather than add hooks across classes with references to objects, or create a new delegate protocol, simply post a notification to the notification center and let anyone who is interested in the data subscribe to it (sometimes referred to as <a href=\"http:\/\/en.wikipedia.org\/wiki\/Publish\u2013subscribe_pattern\">pub-sub<\/a>).<\/p>\n<p>We will eventually write a class that handles taking our location and uses a weather service API to look up whether there are any active alerts.  That class is going to listen for an event that the <code>CoreLocationController<\/code> will publish.  For simplicity we&#8217;ll call it the <code>LOCATION_AVAILABLE<\/code> event.  Here&#8217;s how we use the notification center to post it.  After extracting the <code>CLPlacemark<\/code> data into a <code>userInfo<\/code> dictionary, add the following line:<\/p>\n<pre>\r\n        NSNotificationCenter.defaultCenter().postNotificationName(\"LOCATION_AVAILABLE\", object: nil, userInfo: userInfo)\r\n<\/pre>\n<p>Let&#8217;s go ahead and create the class that will receive and handle the notification.  Create a new file called <code>WeatherServiceController<\/code> and implement the following:<\/p>\n<pre>\r\nclass WeatherServiceController : NSObject {\r\n  \r\n  override init() {\r\n    super.init()\r\n    NSNotificationCenter.defaultCenter().addObserver(self, selector: \"locationAvailable:\", name: \"LOCATION_AVAILABLE\", object: nil)\r\n  }\r\n  \r\n  func locationAvailable(notification:NSNotification) -> Void {\r\n    let userInfo = notification.userInfo as Dictionary<String,String>\r\n    \r\n    println(\"WeatherService:  Location available \\(userInfo)\")\r\n\r\n  }\r\n  \r\n}\r\n<\/pre>\n<p><b>Note:<\/b>  <code>WeatherServiceController<\/code> <i>must<\/i> inherit from <code>NSObject<\/code> to make use of the notification center.  Failure to derive the class from <code>NSObject<\/code> will result in the following type of trap when the notification is posted by the <code>CoreLocationController<\/code>:<\/p>\n<pre>\r\n2014-11-19 21:54:26.405 weatheralerts[914:177667] *** NSForwarding: warning: object 0x1563db00 of class 'weatheralerts.WeatherServiceController' does not implement methodSignatureForSelector: -- trouble ahead\r\nUnrecognized selector -[weatheralerts.WeatherServiceController locationAvailable:]\r\n<\/pre>\n<p>Before trying to run the again, remember, we have to create an instance of our <code>WeatherServiceController<\/code>.  Head back to the <code>AppDelegate<\/code> and add:<\/p>\n<pre>\r\n  var weatherServiceController:WeatherServiceController?\r\n<\/pre>\n<p>to your member variable declarations and:<\/p>\n<pre>\r\n    self.weatherServiceController   = WeatherServiceController()\r\n<\/pre>\n<p>in your <code>didFinishLaunchingWithOptions<\/code> function.  At this point your <code>AppDelegate<\/code> should look a bit something like this:<\/p>\n<pre>\r\n...\r\nclass AppDelegate: UIResponder, UIApplicationDelegate {\r\n\r\n  var window: UIWindow?\r\n  \r\n  var pushNotificationController:PushNotificationController?\r\n  var coreLocationController:CoreLocationController?\r\n  var weatherServiceController:WeatherServiceController?\r\n\r\n  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {\r\n\r\n    self.pushNotificationController = PushNotificationController()\r\n    self.coreLocationController     = CoreLocationController()\r\n    self.weatherServiceController   = WeatherServiceController()\r\n...\r\n<\/pre>\n<p>Running the code again and simulating to Buffalo, New York:<\/p>\n<pre>\r\ndidUpdateLocations:  42.904722, -78.849444\r\nLocation:  [country: United States, city: Buffalo, state: NY]\r\nWeatherService:  Location available [country: United States, city: Buffalo, state: NY]\r\n<\/pre>\n<h2>Receiving Events in the Background<\/h2>\n<p>If you&#8217;ve been working along and put the application in the background, you&#8217;ll notice no updates are received!  That sort of defeats the purpose, so we have to explicitly add the capability to receive location updates in the background.  Go to your application target and navigate to the <b>Capabilities<\/b> page and scroll down to <b>Background Modes<\/b>.  Turn background modes on and select <b>Location updates<\/b><\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_background_location_updates.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2014\/11\/tut_background_location_updates.png\" alt=\"tut_background_location_updates\" width=\"668\" height=\"286\" class=\"alignnone size-full wp-image-911\" \/><\/a><\/p>\n<h2>Bitbucket Xcode Project<\/h2>\n<p>The work done thus far is available on <a href=\"https:\/\/bitbucket.org\/joeiachievedit\/weatheralerts\/\">Bitbucket<\/a> on a branch named <b>part2<\/b>.  To use the project you can directly download the <a href=\"https:\/\/bitbucket.org\/joeiachievedit\/weatheralerts\/get\/part2.zip\">zip file<\/a> and open the enclosed Xcode project.  There are a few steps you&#8217;ll need to complete to use the project:<\/p>\n<ul>\n<li>Sign up for Parse and obtain your own API keys\n<li>Change the <b>Bundle Identifier<\/b> to reflect your organization (that is, change <code>it.iachieved.<\/code> to <code>com.yourcompany<\/code>)\n<li>Create and add your own <code>ApiKeys.plist<\/code> file and use your Parse API keys\n<li>Create and configure your own provisioning profile\n<\/ul>\n<h3>Part 3<\/h3>\n<p>In part three we&#8217;ll be adding the final touches to the application which will be passing the reverse geocoded location to our weather service API.  This API will in turn provide us with information about weather alerts for the area, and if any are available we will provide that information to Parse to receive a push notification.  <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Editor&#8217;s Note: This is part two in a three part series. The goal in this series is to develop a fully functional (and useful) iOS application that is capable of pushing weather alerts based on your location to your iOS device. The series is broken down as follows: Part One &#8211; Developing a Push Notification-capable [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,5],"tags":[],"class_list":["post-894","post","type-post","status-publish","format-standard","hentry","category-apple","category-swift"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/894"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=894"}],"version-history":[{"count":35,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/894\/revisions"}],"predecessor-version":[{"id":1510,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/894\/revisions\/1510"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=894"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=894"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=894"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}