Group Group Group Group Group Group Group Group Group

raywenderlich.com Forums

MapKit Tutorial: Getting Started

Get started with MapKit, adding annotations, and passing the user over to the Maps app in this location-filled MapKit and Swift tutorial!


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/1924-mapkit-tutorial-getting-started

Hey Audrey? How do I get the JSON file of whichever location I want to? For example, I need the JSON file of a place where I reside. Help Please?

Try searching the database at https://opendata.socrata.com? that’s where I found the Honolulu public art data—I think all Socrata data sets provide JSON downloads.

Hey Audrey, my annotations are only showing the “title” and not the “location name” below it. Any idea why this may be happening?

hi Pam - do you have the subtitle property in Artwork.swift? MKAnnotation uses it when creating the annotation.

In Swift 2, it’s now an optional:

var subtitle: String? {
  return locationName
}

It looks like there have been significant changes to Swift syntax since this tutorial was posted. I am having particular trouble with the loadInitialData function that uses the NSJSONSerialization to parse JSON data into Artwork Objects. Can anyone tell me what changes have been made since this was posted that would prevent the function from working properly? Thanks

I updated my own project with the following:

func loadInitialData() {
  // 1
  let fileName = NSBundle.mainBundle().pathForResource("PublicArt", ofType: "json");
//    var readError : NSError?
  var data: NSData?
  do {
    data = try NSData(contentsOfFile: fileName!, options: NSDataReadingOptions(rawValue: 0))
  } catch _ {
    data = nil
  }
  
  // 2
//    var error: NSError?
  var jsonObject: AnyObject? = nil
  if let data = data {
    do {
     jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0))
    } catch _ {
      jsonObject = nil
    }
  }
  
  // 3
  if let jsonObject = jsonObject as? [String: AnyObject],
  // 4
  let jsonData = JSONValue.fromObject(jsonObject)?["data"]?.array {
    for artworkJSON in jsonData {
      if let artworkJSON = artworkJSON.array,
      // 5
      artwork = Artwork.fromJSON(artworkJSON) {
        artworks.append(artwork)
      }
    }
  }

}

Thank you Audrey! I really appreciate it!

Now I am getting the message “unable to read data” on the let filename = … line in the debugger. Is there something more I need to do to make the file JSON.swift discoverable within the scope of the program?

you did this step?

When you have the PublicArt.json file, drag it into your HonoluluArt\Supporting Files group, make sure Destination: Copy items if needed and Add to targets: HonoluluArt are selected

and/or check that HonoluluArt is checked in Target Membership:

Thanks again, Audrey! I simply forgot to call the method loadInitialData() and mapView.addAnnotations(artwork). Although I did confirm all the other suggestions before I figured out what I did wrong. Everything worked great!

I did try to parse a JSON file from the Seattle data portal at data.seattle.gov thinking that I could just change the name of the file and the coordinates of the initial location. When I tried to run it I got a giant list of fatal errors.

Here is a link to the file I downloaded:
https://data.seattle.gov/api/views/7ckr-2zz9/rows.json?accessType=DOWNLOAD

I am not sure if this didn’t work because the JSON file varied slightly from the Honolulu Art dataset, or if it’s because I didn’t make the proper changes to the Swift code.

I downloaded that file—it’s Socrata, but Socrata allows some flexibility in the ordering of the data items. My fromJSON method is tailored to the Honolulu public art JSON file. If you look at the start of the Seattle file, before the actual data, you’ll see "name" : "Common Name", is at "position" : 3, but you’ll need to add 7 to position to get the correct data array index—look at a data item, and count within the array. I notice the Seattle file puts longitude before latitude, the opposite to the Honolulu file, so take care of that when modifying fromJSON.

If you want the app to download the JSON file from the url, you’ll need to use NSData(contentsOfURL:options:)

hope this helps!

I was wondering how you would implement dropping a pin at the user’s current location? Let’s say they wanted to permanently save their current location. Is it possible to do this with a simple button? Thanks for all the help.

this tutorial doesn’t really use user location; have a look at https://www.raywenderlich.com/97944/make-app-like-runkeeper-swift-part-1 to see how you can use CLLocationManager to log user locations.

I would like to know how come this static func fromObject(object: AnyObject) -> JSONValue? {
switch object {
case let value as NSString:
return JSONValue.JSONString(value as String)
case let value as NSNumber:
return JSONValue.JSONNumber(value)
case let value as NSNull:
return JSONValue.JSONNull
case let value as NSDictionary:
var jsonObject: [String:JSONValue] = [:]
for (k: AnyObject, v: AnyObject) in value {
if let k = k as? NSString {
if let v = JSONValue.fromObject(v) {
jsonObject[k as String] = v
} else {
return nil
}
}
}
return JSONValue.JSONObject(jsonObject)
case let value as NSArray:
var jsonArray: [JSONValue] = []
for v in value {
if let v = JSONValue.fromObject(v) {
jsonArray.append(v)
} else {
return nil
}
}
return JSONValue.JSONArray(jsonArray)
default:
return nil
}
}

in JSON.swift is giving me an error Definition conflicts with previous value. Thank you;

sorry, I don’t get this error when I open the final version of this project. Do a search in Xcode on fromObject, to see if you have another definition somewhere?

It does not give me any. I am using xcode 7.3 and trying to deploy in ios 9.3. Would that make any difference?

ah, it’s the definition of NSDictionary that’s changed! I made this adjustment last month:

replace the last line of this:

case let value as NSDictionary:
var jsonObject: [String:JSONValue] = [:]
for (k: AnyObject, v: AnyObject) in value {

with this:

for (k, v) in value {

that is, remove the types of k and v

Thanks a lot. That fixed it. If its not a bother, would it be possible to ask some more regarding a different topic like how to locate beacons accurately in swift? Thanks a lot.

https://www.raywenderlich.com/101891/ibeacons-tutorial-ios-swift

:]