I'm putting JSON data into a table view, and I'm trying to parse through the data using a for loop. However, when the loop is done parsing through the JSON data and has placed the 20 items into the table view, it restarts the process, parses the JSON again, and the same data appears in the table view again. This process repeats for a long time as well.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else{ return }
var searchURL = NSString(format: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=%f,%f&radius=50000&types=night_club&key=MY_API_KEY", (location.coordinate.latitude),(location.coordinate.longitude)) as? String
var cityInfo = NSString(format: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=%f,%f&radius=50000&types=locality&key=MY_API_KEY", (location.coordinate.latitude),(location.coordinate.longitude)) as? String
manager.stopUpdatingLocation()
getCityInfo(url: cityInfo!)
callAlamo(url: searchURL!)
}
func getCityInfo(url:String){
Alamofire.request(url).responseJSON(completionHandler: { response in
self.parseJSON(JSONData: response.data!)
})
}
func parseJSON(JSONData:Data){
do{
var readableJSON = try JSONSerialization.jsonObject(with: JSONData) as! JSONStandard
// PARSING THROUGH JSON DATA TO GET CITY NAME
if let results = readableJSON["results"] as? [JSONStandard]{
for i in 0..<results.count{
let item = results[i]
let cityInfo = item["name"] as! String
cityName.append(cityInfo)
// GETTING PHOTO URL WITH photo_reference AND PUTTING THEM INTO imageURL ARRAY
if let photos = item["photos"] as? [JSONStandard]{
for j in 0..<photos.count{
let photo = photos[j] as JSONStandard
let photoRef = photo["photo_reference"] as! String
let photoURL = NSString(format: "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=%#&key=MY_API_KEY", photoRef) as? String
cityURL.append(photoURL!)
}
}
}
}
cityLabel.text = cityName[0]
cityImage.sd_setImage(with: URL(string:cityURL[0]), placeholderImage: #imageLiteral(resourceName: "cityOfCalgary"))
}
catch{
print(error)
}
}
func callAlamo(url:String){
Alamofire.request(url).responseJSON(completionHandler: { response in
self.parseData(JSONData: response.data!)
})
}
func parseData(JSONData:Data){
do{
var myReadableJSON = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! JSONStandard
// PARSING THROUGH JSON DATA TO GET NAMES AND PICTURES OF PLACES, THEN PUTTING
// THEM INTO AN ARRAY AND OUTPUTTING THEM ONTO TABLE VIEW CELL
if let results = myReadableJSON["results"] as? [JSONStandard]{
for i in 0..<results.count{ //results.count = 20
let item = results[i]
let names = item["name"] as! String
placeNames.append(names)
// GETTING PHOTO URL WITH photo_reference AND PUTTING THEM INTO imageURL ARRAY
if let photos = item["photos"] as? [JSONStandard]{
let photoRef = photos[0]
let photoReference = photoRef["photo_reference"] as! String
let photoURL = NSString(format: "https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=%#&key=MY_API_KEY", photoReference) as? String
imageURL.append(photoURL!)
}
if let geometry = item["geometry"] as? JSONStandard{
if let location = geometry["location"] as? [String : Any]{
let latitude = location["lat"] as? Double
let longitude = location["lng"] as? Double
}
}
}
}
// SHOULD BE PLACED AT THE END OF GATHERING DATA
locationManager.stopUpdatingLocation()
self.tableView.reloadData()
}
catch{
print(error)
}
}
UPDATE:
As vadian had mentioned in one of his first comments, parseData() was getting called multiple times. So I added
locationManager.delegate = nil
after I stop updating the location in the locationManager delegate function.
`
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else{ return }
searchURL = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(location.coordinate.latitude),\(location.coordinate.longitude)&radius=50000&types=night_club&key=MY_API_KEY"
cityInfo = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(location.coordinate.latitude),\(location.coordinate.longitude)&radius=50000&types=locality&key=MY_API_KEY"
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
getCityInfo(url: cityInfo)
callAlamo(url: searchURL)
}
`
Everything else remains the same after this.
As I suspected you are calling parseData multiple times. A solution is to stop monitoring the location right in the delegate method.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
let searchURL = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(location.coordinate.latitude),\(location.coordinate.longitude)&radius=50000&types=night_club&key=AIzaSyA2LQsGK_I1ETnKPGbjWgFW9onZlHog6dg"
// var cityInfo = NSString(format: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=%f,%f&radius=50000&types=locality&key=AIzaSyA2LQsGK_I1ETnKPGbjWgFW9onZlHog6dg", (location?.coordinate.latitude)!,(location?.coordinate.longitude)!) as? String
manager.stopUpdatingLocation()
callAlamo(url: searchURL)
}
I edited the body of the method a bit to avoid all question and exclamation marks.
Side-note: Basically do not annotate types the compiler can infer.
Related
keysInCircle is the name of array of strings that has the keys to users inside the query range.
postData is the array of users that is used by tableView to populate the view.
My app gives a thread! : SIGABRT error in my appdelegate file whenever I run the tab containing the geofire.
It is also gives an error:
libc++abi.dylib: terminating with uncaught exception of type NSException.
//Location Functions
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let _ : CLLocationCoordinate2D = manager.location?.coordinate else{
return
}
geoFire!.setLocation(locations.last!, forKey: Auth.auth().currentUser!.uid)
InRange(location: locations.last!)
for ids in keysInCircle{
fetchInformation(idlabel: ids)
}
}
func InRange(location:CLLocation){
var query = geoFire?.query(at: location, withRadius: 0.25)
query?.observe(.keyEntered, with: { (key, qloc) in
if(key != Auth.auth().currentUser?.uid && !self.keysInCircle.contains(key)){
self.keysInCircle.append(key)
}
})
query?.observeReady {
self.postData.removeAll()
}
}
func fetchInformation(idlabel:String){
let userDbRef:DatabaseReference = Database.database().reference().child("Users").child(idlabel)
userDbRef.observe(.value, with: { (snapshot) in
if let post:[String:Any] = snapshot.value as? Dictionary
{
let name = post["name"] as? String
var url = post["profileImageUrl"] as? String
if url == nil{
url = "default"
}
let id = snapshot.key
var college = post["college"] as? String
if college == nil{
college = "Other"
}
let tableUser = user(_objectId: id, _name: name!, _url: url!, _college: college!)
self.postData.append(tableUser)
self.tableView.reloadData()
}
})
}
I am trying to show multiple locations which saved in Mysql using the code below. The data is loading but I have no idea how to show multiple locations depending on latitude and longitude.
Mysql is connected to application via PHP file.
Here is my code, the part which I called from NSObject:
func downloadItems() {
// the download function
// return the nsuserdefaults which hold the lati and longi from the notification table
UserDefaults.standard.string(forKey: "test");
let myUrl = UserDefaults.standard.string(forKey: "test");
let urlPath: String = myUrl!
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let locations = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let location = LocationModel()
//the following insures none of the JsonElement values are nil through optional binding
if let evIdL = jsonElement["id"] as? String,
let evUserNameL = jsonElement["username"] as? String,
let evNotikindL = jsonElement["notikind"] as? String,
let evLatiL = jsonElement["lati"] as? String,
let evLongiL = jsonElement["longi"] as? String,
let evLocatL = jsonElement["locat"] as? String,
let evTimedateL = jsonElement["timedate"] as? String,
let evDistanceL = jsonElement["distance"] as? String
{
location.evId = evIdL
location.evUsername = evUserNameL
location.evNotikind = evNotikindL
location.evLati = evLatiL
location.evLongi = evLongiL
location.evLocat = evDistanceL
location.evTimedate = evTimedateL
location.evDisatnce = evDistanceL
location.evLocat = evLocatL
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
}
I have no idea how to show few location on map.
try this code....
var locations = NSMutableArray()
var mapView = GMSMapView()
for i in 0..< location.count{
let obj = location[i]
lat = obj["lati"] as? Double
lng = obj["longi"] as? Double
let markerPoint = GMSMarker()
markerPoint.position = CLLocationCoordinate2D(latitude: lat!, longitude: lng!)
markerPoint.iconView = self.avtarImage() // Your image name
markerPoint.map = mapView // your mapview object name
markerPoint.zIndex = Int32(i)
markerPoint.infoWindowAnchor = CGPoint(x: 0, y: 0)
markerPoint.accessibilityLabel = String(format: "%d", i)
}
This question already has an answer here:
Json Serialisation Swift 3 type error
(1 answer)
Closed 5 years ago.
So I've been getting an error for a thread 4: SIGABRT on Xcode when trying to parse data from the OpenWeatherApp API. The error that pulls up on the console is:
Could not cast value of type '__NSArrayM' (0x3419714) to 'NSDictionary' (0x3419958)
I looked at different things on this forum already and none of it seems to really work.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var typeCity: UITextField!
var weatherDescription : String = ""
override func viewDidLoad() {
super.viewDidLoad()
// let checkText = typeCity.text
/*
if typeCity?.text == nil{
typeCity.placeholder = "Type a city with no spaces"
let city = "Chicago"
}
*/
let city = "Chicago"
/*
if checkText != nil {
typeCity?.text = city
}
*/
print("City: \(city)")
// Do any additional setup after loading the view, typically from
a nib.
let url = URL(string:
"http://api.openweathermap.org/data/2.5/weather?q=\
(city)&appid=626a124ef0844d2e021329c38a5dfafd")
let task = URLSession.shared.dataTask(with: url!) { (data,
response, error) in
if error != nil{
print(error!)
} else {
if let urlContent = data {
do {
let jsonResult = try
JSONSerialization.jsonObject(with: urlContent, options:
JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(jsonResult)
//let lon = jsonResult["coord"]["lon"].double
//let lat = jsonResult["coord"]["lon"].double
//let temp = jsonResult?["main"]["double"].double
//print
print(jsonResult["name"]!!)
let coordinates = jsonResult["coord"] as! [String:Any]//the coordinates parsing
print("Coordinates: \(coordinates)")
let lon = coordinates["lon"] as! Double
let lat = coordinates["lat"] as! Double
print("Latitude: \(lat) Longitude: \(lon)")
let main = jsonResult["main"] as!
[String:Any]//for the temperature
let kelvin = main["temp"] as! Double
let degreesFahrenheit = 9/5 * (kelvin-273) + 32
print("Temperature: \(degreesFahrenheit)")
let humidity = main["humidity"] as! Double
let pressure = main["pressure"] as! Double
let temp_max = main["temp_max"] as! Double
let temp_min = main["temp_min"] as! Double
let description = jsonResult["weather"]
["description"]as! [String: Any]
print("description")
} catch{
print("Json Processing has failed or city name not recognized.")
}
// let json = jsonResult(data: data)
//print("Lat: \(String(describing: lat)) Lon: \
(String(describing: lon)) Temp: \(String(describing: temp))")
}
}
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}}
There seems to be an error with the following line:
let description = jsonResult["weather"]["description"]as! [String: Any]
Thank you in advance for all the help!
You are trying to implicitly convert an array to a dictionary. Try to safely check the type of it:
if let description = jsonResult["weather"]["description"] as? [String] {
[...]
}
import UIKit
import MapKit
import CoreLocation
import AddressBook
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var TheMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
zoomToRegion()
location()
}
func centerMapOnLocation(location: MKPointAnnotation, regionRadius: Double) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
TheMap.setRegion(coordinateRegion, animated: true)
}
//MARK:- MapViewDelegate methods
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
if overlay is MKPolyline {
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.lineWidth = 5
}
return polylineRenderer
}
//MARK:- Zoom to region
func zoomToRegion() {
let location = CLLocationCoordinate2D(latitude: 28.618945, longitude: 77.377347400000005)
let region = MKCoordinateRegionMakeWithDistance(location, 5000.0, 7000.0)
TheMap.setRegion(region, animated: true)
}
// API CALL FUNCTION
func location() {
let user = "userid"
let password = "password"
let postString = ["empid":user, "date1": password]
var request = URLRequest(url:URL(string: "http://mydomainhere.com/airtel_hrm/webapi/api/getpunchdeytails")!)
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(withJSONObject: postString, options:.prettyPrinted)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?)in
if error != nil
{
print("error=\(error)")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any],
let data = json["punchdetails"] as? [[String: Any]] {
//print(data)
for datas in data {
let lat = datas["punch_loc_lat"] as! String
let long = datas["punch_loc_long"] as! String
var annotations = [MKPointAnnotation]()
let latitude = CLLocationDegrees(lat)
let longitude = CLLocationDegrees(long)
let coordinate = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotations.append(annotation)
self.TheMap.addAnnotations(annotations)
self.TheMap.delegate = self
self.centerMapOnLocation(location: annotations[0], regionRadius: 2000.0)
// Connect all the mappoints using Poly line.
var points: [CLLocationCoordinate2D] = [coordinate] //[CLLocationCoordinate2D]()
for annotation in annotations {
points.append(annotation.coordinate)
}
print("this is points = \(points)")
let polyline = MKPolyline(coordinates: &points, count: points.count)
//self.TheMap.add(polyline)
} //for loop closed
}
} catch {
print(error)
}
}
task.resume()
}
}
I'm sure if you shared the results of the print statement, the problem would have been obvious. You're probably seeing lots of print statements. Bottom line, you should define your array outside of the for loop, only append values within the loop, and then add the polyline after the loop:
do {
if let json = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let data = json["punchdetails"] as? [[String: Any]] {
var annotations = [MKPointAnnotation]()
for datas in data {
let lat = datas["punch_loc_lat"] as! String
let long = datas["punch_loc_long"] as! String
let latitude = CLLocationDegrees(lat)
let longitude = CLLocationDegrees(long)
let coordinate = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotations.append(annotation)
}
self.TheMap.delegate = self // you really should do this in IB or, if you feel compelled to do it programmatically, in viewDidLoad
// Connect all the mappoints using Poly line.
let points = annotations.map { $0.coordinate }
print("this is points = \(points)")
let polyline = MKPolyline(coordinates: &points, count: points.count)
DispatchQueue.main.async {
self.centerMapOnLocation(location: annotations[0], regionRadius: 2000.0)
self.TheMap.addAnnotations(annotations)
self.TheMap.add(polyline)
}
}
} catch {
print(error)
}
Note, I'd also do all interaction with the map view from the main queue.
I am trying to fetch JSON data from OpenWeatherMap API depending on user location and display it in a tableview.
First, I just init my table view :
init(_ coder: NSCoder? = nil) {
self.tableView = UITableView()
self.locationManager = CLLocationManager()
}
Then in viewDidLoad I call a function launchLocationOperations() to get user's location :
func launchLocationOperations() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
In my delegate :
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.locationManager.stopUpdatingLocation()
let locationArray = locations as NSArray
let currentLocation = locationArray.lastObject as! CLLocation
if self.currentCoordinates == nil ||
(self.currentCoordinates?.latitude != currentLocation.coordinate.latitude
||
self.currentCoordinates?.longitude != currentLocation.coordinate.longitude) {
self.currentCoordinates = currentLocation.coordinate
self.hasLoadedCoordinates = true
self.fetchWeatherInformations()
}
}
Then calling fetchWeatherInformation() :
func fetchWeatherInformations() {
// I build my path
let urlPath = StringUtils.buildUrl(self.currentCoordinates)
guard let url = URL(string: urlPath) else { return }
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, _, error in
do {
let jsonResponse = try data?.toJSON() as? [String : Any]
self.openWeatherMapResponse = OpenWeatherMapResponse.convert(jsonResponse: jsonResponse)
self.displayUI()
} catch {
print("Error while fetching JSON response : \(error.localizedDescription)")
}
}.resume()
}
And in displayUI() :
func displayUI() {
tableView.delegate = self
tableView.dataSource = self
}
So I have two problems here :
First, didUpdateLocations is being called many times even if I ask to stopUpdatingLocation() when entering the function.
Second, cellForRowAtIndexPath is being called very, very late, like 5 seconds late. I don't know why it is happening because the other dataSource/delegate methods are called instantly after entering displayUI()...
Thanks for your help.