Ambiguous reference to member 'subscript' when using Google maps route - ios

The below code works fine in Xcode 7.1 but when I upgrade to Xcode 8 and swift 3 I get this error, I had searched on internet for same problem in stackoverflow but none of them answer my question.
func drawRoute() {
clearRoute()
// self.routePolyline = nil
// self.routePolyline.map = nil
let route = mapTk.overviewPolyline!["points"] as String //error here
//var overViewPolyLine = routes![0]["overview_polyline"]["points"].string
let path: GMSPath = GMSPath(fromEncodedPath: route)
routePolyline = GMSPolyline(path: path)
routePolyline.map = viewMap
}
here is the class mapTK for more info:
import Foundation
import UIKit
import CoreLocation
import MapKit
///This Class is designed for the detail methods that contact google maps server like GeoCode, GetDirection and Calculate distance and time
class MapTK: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
let baseURLDirections = "https://maps.googleapis.com/maps/api/directions/json?"
var selectedRoute: Dictionary<NSObject, AnyObject>!
var overviewPolyline: Dictionary<NSObject, AnyObject>!
var originCoordinate: CLLocationCoordinate2D!
var destinationCoordinate: CLLocationCoordinate2D!
var originAddress: String!
var destinationAddress: String!
var totalDistanceInMeters: UInt = 0
var totalDistance: String!
var totalDurationInSeconds: UInt = 0
var totalDuration: String!
/* private let errorDictionary = ["NOT_FOUND" : "At least one of the locations specified in the request's origin, destination, or waypoints could not be geocoded",
"ZERO_RESULTS":"No route could be found between the origin and destination",
"MAX_WAYPOINTS_EXCEEDED":"Too many waypointss were provided in the request The maximum allowed waypoints is 8, plus the origin, and destination",
"INVALID_REQUEST":"The provided request was invalid. Common causes of this status include an invalid parameter or parameter value",
"OVER_QUERY_LIMIT":"Service has received too many requests from your application within the allowed time period",
"REQUEST_DENIED":"Service denied use of the directions service by your application",
"UNKNOWN_ERROR":"Directions request could not be processed due to a server error. Please try again"]
*/
override init() {
super.init()
}
///the geocodeAddress is getting information from googlemaps with JSON it receive the geocode information
func geocodeAddress(address: String!, withCompletionHandler completionHandler: #escaping ((_: String, _: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
//geocodeURLString = geocodeURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
let geocodeURL = NSURL(string: geocodeURLString)
// dispatch_Dispatch.Queue.mainasync(dispatch_get_main_queue(), { () -> Void in
DispatchQueue.main.async {
let geocodingResultsData = NSData(contentsOf: geocodeURL! as URL)
// let error: NSError?
do
{
let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
// try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
// Get the response status.
let status = dictionary?["status"]as! String
// let status = dictionary["status"] as! [AnyObject]
if status == "OK" {
let allResults = dictionary?["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"]? as? String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("No valid address.", false)
}
}
///getDirection method is using JSON to receive waypoints for making route for drawing polyline, as its name suggests it get the direciton inforamtion, first it send lat and lng the receive a dictionary and that dictionary give all the infrmation need to draw route like waypoints
func getDirections(origin: String!, destination: String!, waypoints: Array<String>!, travelMode: TravelModes!, completionHandler: #escaping ((_ status: String, _ success: Bool) -> Void)) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = baseURLDirections + "origin=" + originLocation + "&destination=" + destinationLocation //+ "&key=AIzaSyDsDqj0EMYZ-C4lGF3tmbntZtzurLl6_J4"
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
if let _ = travelMode {
var travelModeString = ""
switch travelMode.rawValue {
case TravelModes.walking.rawValue:
travelModeString = "walking"
case TravelModes.bicycling.rawValue:
travelModeString = "bicycling"
default:
travelModeString = "driving"
}
directionsURLString += "&mode=" + travelModeString
}
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
/// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
directionsURLString = directionsURLString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
//let encodedHost = unencodedHost.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
//var url = NSURL(urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
//let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo-\(date).mov")
let directionsURL = NSURL(string: directionsURLString)
// DispatchQueue.main.asynchronously(execute: { () -> Void in
DispatchQueue.main.async {
let directionsData = NSData(contentsOf: directionsURL! as URL)
// var error: NSError?
do {
let dictionary = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
let status = dictionary?["status"] as! String
if status == "OK" {
self.selectedRoute = (dictionary?["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<NSObject, AnyObject>
self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"]as! Dictionary<NSObject, AnyObject>
self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
self.originAddress = legs[0]["start_address"] as! String
self.destinationAddress = legs[legs.count - 1]["end_address"] as! String
self.calculateTotalDistanceAndDuration()
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("Destination is nil.", false)
}
}
else {
completionHandler("Origin is nil", false)
}
}
/// this method below is for the calculation of Distance adn Duration of each location for travel
func calculateTotalDistanceAndDuration() {
let legs = self.selectedRoute["legs"] as! Array<NSDictionary>
totalDistanceInMeters = 0
totalDurationInSeconds = 0
for leg in legs {
totalDistanceInMeters += (leg["distance"] as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
totalDurationInSeconds += (leg["duration"]as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
}
let distanceInKilometers: Double = Double(totalDistanceInMeters / 1000)
totalDistance = "Total Distance: \(distanceInKilometers) Km"
let mins = totalDurationInSeconds / 60
let hours = mins / 60
let days = hours / 24
let remainingHours = hours % 24
let remainingMins = mins % 60
let remainingSecs = totalDurationInSeconds % 60
totalDuration = "Duration: \(days) d, \(remainingHours) h, \(remainingMins) mins, \(remainingSecs) secs"
}
}

Use
Dictionary<String, Any>
as JSON dictionary type.
JSON keys are required to be String anyway and
AnyObject has been changed to Any in Swift 3.
Edit :
I recommend to use a type alias for the JSON dictionary type
typealias JSONObject = [String:Any] // synonym of Dictionary<String, Any>
Then you can write
var lookupAddressResults: JSONObject!
...
var selectedRoute: JSONObject!
var overviewPolyline: JSONObject!
and the parsing code
...
if status == "OK" {
let allResults = dictionary?["results"] as! Array<JSONObject>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as? String
let geometry = self.lookupAddressResults["geometry"] as! JSONObject
self.fetchedAddressLongitude = ((geometry["location"] as! JSONObject)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! JSONObject)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
...
You can also replace
( ... as! NSNumber).doubleValue
with
... as! Double

Related

google maps route between 2 points with multi waypoints swift ios

I have this code but for some reason it's just drawing a rout between 2 points (first, and last points) ignoring all other points which is [index == 1 to index == n-1 ]
output : route between only 2 markers
expected output : route between all markers (5 markers)
Is any body knows what is the wrong with my code ?
func getDotsToDrawRoute(positions : [CLLocationCoordinate2D], completion: #escaping(_ path : GMSPath) -> Void) {
if positions.count > 1 {
let origin = positions.first
let destination = positions.last
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.characters.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)|\(point.latitude),\(point.longitude)"
}
print("this is fullPath :: \(wayPoints)")
let request = "https://maps.googleapis.com/maps/api/directions/json"
let parameters : [String : String] = ["origin" : "\(origin!.latitude),\(origin!.longitude)", "destination" : "\(destination!.latitude),\(destination!.longitude)", "wayPoints" : wayPoints, "stopover": "true", "key" : kyes.google_map]
Alamofire.request(request, method:.get, parameters : parameters).responseJSON(completionHandler: { response in
guard let dictionary = response.result.value as? [String : AnyObject]
else {
return
}
print ("route iss ::: \(dictionary["routes"])")
if let routes = dictionary["routes"] as? [[String : AnyObject]] {
if routes.count > 0 {
var first = routes.first
if let legs = first!["legs"] as? [[String : AnyObject]] {
let fullPath : GMSMutablePath = GMSMutablePath()
for leg in legs {
if let steps = leg["steps"] as? [[String : AnyObject]] {
for step in steps {
if let polyline = step["polyline"] as? [String : AnyObject] {
if let points = polyline["points"] as? String {
fullPath.appendPath(path: GMSMutablePath(fromEncodedPath: points))
}
}
}
let polyline = GMSPolyline.init(path: fullPath)
polyline.path = fullPath
polyline.strokeWidth = 4.0
polyline.map = self._map }
}
}
}
}
})
}
}
Check out this solution working fine with me
func drawpath(positions: [CLLocationCoordinate2D]) {
let origin = positions.first!
let destination = positions.last!
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.characters.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)%7C\(point.latitude),\(point.longitude)"
}
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin!.latitude),\(origin!.longitude)&destination=\(destination.latitude),\(destination.longitude)&mode=driving&waypoints=\(wayPoints)&key=KEY"
Alamofire.request(url).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // HTTP URL response
print(response.data as Any) // server data
print(response.result as Any) // result of response serialization
let json = try! JSON(data: response.data!)
let routes = json["routes"][0]["overview_polyline"]["points"].stringValue
let path = GMSPath.init(fromEncodedPath: routes)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.red
polyline.map = self._map
}
}
try with:
func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
self.mapView.clear()
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=true&mode=driving&key=YOUR_API_KEY")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print("Error" + error!.localizedDescription)
}else {
do {
if let json : [String:Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]{
guard let routes = json["routes"] as? NSArray else {
return
}
print(routes)
if (routes.count > 0) {
let overview_polyline = routes[0] as? NSDictionary
let dictPolyline = overview_polyline?["overview_polyline"] as? NSDictionary
let points = dictPolyline?.object(forKey: "points") as? String
self.showPath(polyStr: points!)
let distancia = overview_polyline?["legs"] as? NSArray
let prueba = distancia![0] as? NSDictionary
let distancia2 = prueba?["distance"] as? NSDictionary
let control = distancia2?.object(forKey: "text") as! String
DispatchQueue.main.async {
let bounds = GMSCoordinateBounds(coordinate: source, coordinate: destination)
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(170, 30, 30, 30))
self.mapView!.moveCamera(update)
}
}
}
}
catch {
print("error in JSONSerialization")
}
}
})
task.resume()
}
You call the method with
self.getPolylineRoute(from: origin, to: destination)
Remember in the google url to put your api key where it says YOUR_API_KEY
You can use this function for drawing the polyline which covers your waypoints.
Make sure that positions doesn't have same waypoint coordinates. If
so, it will just draw the polylines from source to destination
func drawPolyLine(for positions: [CLLocationCoordinate2D]) {
let origin = positions.first!
let destination = positions.last!
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)%7C\(point.latitude),\(point.longitude)"
}
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin.latitude),\(origin.longitude)&destination=\(destination.latitude),\(destination.longitude)&mode=driving&waypoints=\(wayPoints)&key=\(YOUR_GOOLE_MAP_API_KEY)"
Alamofire.request(url).responseJSON { response in
do {
let json = try JSONSerialization.jsonObject(with: response.data!, options: .allowFragments) as? [String: Any]
guard let routes = json?["routes"] as? NSArray else {
return
}
for route in routes {
let routeOverviewPolyline: NSDictionary = ((route as? NSDictionary)!.value(forKey: "overview_polyline") as? NSDictionary)!
let points = routeOverviewPolyline.object(forKey: "points")
let path = GMSPath.init(fromEncodedPath: (points! as? String)!)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor(red: 95/255.0, green: 233/255.0, blue: 188/255.0, alpha: 1.0)
polyline.map = self.mapView
}
} catch {
print("Unexpected Error")
}
}
}

Showing multiple location using 'MKMapView'

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)
}

Get JSON value to Array

I need to get the JSON value from MYSQL and display it in the table view. I have done PHP part to receive data but I have error with my XCODE. My data will store temperature, humidity and digital status (on off) of sensor. I have questions below:
How to get ON/OFF JSON value and store in Bool array?
How to get JSON value and store in Float array?
if JSON value is , how can I store value as 0 in XCODE?
class DataManager {
var nodenameArray: [String] = []
var nodeidArray: [String] = []
var tempArray: [Float] = []
var humArray: [Float] = []
var pirArray: [Bool] = []
var lightArray: [Bool] = []
var relayArray: [Bool] = []
var hallArray: [Bool] = []
var smokeArray: [Bool] = []
#objc func taskdo() {
self.nodenameArray = []
self.nodeidArray = []
self.tempArray = []
self.humArray = []
self.pirArray = []
self.lightArray = []
self.relayArray = []
self.hallArray = []
self.smokeArray = []
if userlogin == true {
let request = NSMutableURLRequest(url: NSURL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")! as URL)
request.httpMethod = "POST"
let postString = "username=\(username)&password=\(password)&authen=wdwfesf9329140dsvfxkciospdkm"
request.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(error!)")
return
} else {
do {
if let respondString = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(respondString)
if let nodedata = respondString.value(forKey: "nodedata") as? NSArray {
for node in nodedata{
if let nodeDict = node as? NSDictionary {
if let nodeid = nodeDict.value(forKey: "node_id") {
self.nodeidArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "node_name") {
self.nodenameArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "temp") {
self.tempArray.insert(Float(nodeid as! String)!, at: 0)
}
if let nodeid = nodeDict.value(forKey: "hum") {
print(nodeid)
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
}
}
}
}
}
print(self.nodenameArray)
print(self.nodeidArray)
print(self.tempArray)
print(self.humArray)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
}
catch let error as NSError {
print(error.debugDescription)
}
}
}
task.resume()
}
}
}
below is the respond:
{
nodedata = (
{
"hall_status" = "<null>";
hum = 111;
"light_status" = "<null>";
"node_id" = y2cfwecrw3hqznuxmfvf;
"node_name" = SVIN03;
"pir_status" = OFF;
"relay_status" = "<null>";
"smoke_status" = "<null>";
temp = 2132;
},
{
"node_name" = SVIN04;
nodeid = aj2w1aljw8nd65ax79dm;
},
{
"hall_status" = "<null>";
hum = 100;
"light_status" = "<null>";
"node_id" = mwmfl2og2l8888fjpj2d;
"node_name" = SVIN05;
"pir_status" = ON;
"relay_status" = "<null>";
"smoke_status" = "<null>";
temp = 45;
}
);
numberofnodeid = 3;
}
111
100
Could not cast value of type '__NSCFNumber' (0x10db6f878) to 'NSString' (0x10ca71568).
2018-10-11 08:11:05.352491+0700 IOT 2[1506:101847] Could not cast value of type '__NSCFNumber' (0x10db6f878) to 'NSString' (0x10ca71568).
error is in this line :
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
First of all, you have to look at json_decode to read data from json in php
Example:
`$json = '{"foo-bar": 12345}';
$obj = json_decode($json);
print $obj->{'foo-bar'}; // this will print 12345`
How to get ON/OFF JSON value and store in Bool array ?
basically you have to add a new value to your onOff variable, something like
$onOff=array();
$val;
$obj = json_decode($json);
if (is_null($obj->{'name_of_the_alert'})) {
$val = 0
} else {
$val = $obj->{'name_of_the_alert'};
}
array_push($onOff,$obj->{'name_of_the_alert'});
How to get JSON value and store in Float array ?
Basically same thing, I haven't use PHP in a while, but just declare the array of the value type you need.
if JSON value is , how can i store value as 0 in XCODE ?
I guess you mean if it is null, in this case you have to do this
$onOff=array();
$val;
$obj = json_decode($json);
if (is_null($obj->{'name_of_the_alert'})) {
$val = 0
} else {
$val = $obj->{'name_of_the_alert'};
}
array_push($onOff,$obj->{'name_of_the_alert'});
Take into consideration that I may have some mistakes here because I am not familiar with newer PHP versions.
Good luck
From your question, it is clear that temp and hum are Number type(Int of Double). So use NSNumber rather than NSString as:
self.humArray.insert(Float(Int(nodeid as! NSNumber)!), at: 0)
As more swifty approach, you can update your code as:
if let nodeDict = node as? [String: Any] {
if let nodeid = nodeDict["node_id"] as? String {
self.nodeidArray.insert(nodeid, at: 0)
}
if let nodename = nodeDict["node_name"] as? String {
self.nodenameArray.insert(nodename, at: 0)
}
if let temp = nodeDict["temp"] as? Int {
self.tempArray.insert(Float(temp), at: 0)
}
if let hum = nodeDict["hum"] as? Int {
self.humArray.insert(Float(hum), at: 0)
}
}
There is plenty of issues with your code:
First, avoid using NSStuff in Swift3+ when there is the equivalent.
let request = NSMutableURLRequest(url: NSURL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")! as URL)
...
let task = URLSession.shared.dataTask(with: request as URLRequest) {
=>
let request = URLRequest(url: URL(string: http://talectric.com/wp-admin/a_p/iot/read_all.php")!)
...
let task = URLSession.shared.dataTask(with: request) {
See, you avoid already all the as URL, as URLRequest.
Avoid force unwrapping: Avoid using !.
Because if it's nil or with as! the cast fails (and then it's nil), you'll get a crash.
Use if let, guard let. Read this about Optional
As a sample, I would have wrote:
guard let url = URL(string:http...) else {
print("invalid URL")
return
}
let request = URLRequest(url: url)
Parsing part:
if let respondString = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
Get rid of NSDictionary, use [String: Any] instead, then get rid of .allowFragments, it's rarely useful (it makes sense on certains cases only) don't use the force unwrap on data!, and don't name your variable respondString
because that's clearly not a String and that's clearly misleading.
=>
guard let data = data else { return }
if let responseDict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
Now:
if let nodedata = respondString.value(forKey: "nodedata") as? NSArray {
for node in nodedata{
if let nodeDict = node as? NSDictionary {
if let nodeid = nodeDict.value(forKey: "node_id") {
self.nodeidArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "node_name") {
self.nodenameArray.insert(nodeid as! String, at: 0)
}
if let nodeid = nodeDict.value(forKey: "temp") {
self.tempArray.insert(Float(nodeid as! String)!, at: 0)
}
if let nodeid = nodeDict.value(forKey: "hum") {
print(nodeid)
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
}
}
}
}
No, no NSArray, no NSDictionary, avoid using value(forKey:) because it does what you think it does on a NSDictionary doing a object(forKey:), or simply subscript (using ["someKey"]) but on an Array you'll get an issue with an unexpected result I think.
Don't name all your var nodeid, it's harder to debug after ward, don't do a soft unwrap (if let) if just afterward you do a force unwrap on that value.
if let nodedata = responseDict["nodedata"] as? [[String: Any]] {
for aNode in nodedata {
if let id = aNode["node_id"] as? String {
self.nodeidArray.insert(nodeId, at: 0)
}
if let name = aNode["node_name"] as? String {
self.nodenameArray.insert(name, at: 0)
}
if let temp = aNode["temp"] as? String, let tempFloat = Float(temp) {
self.tempArray.insert(tempFloat, at: 0)
}
if let hum = aNode["hum"] as? String, let humFloat = Float(hum) {
self.humArray.insert(humFloat, at: 0)
}
}
}
That's much more readable.
Now instead of using insert(_:at:), it might be easier to use nodedata.inverted().
Now, let's talk about architecture:
What happens in your case, if you don't pass the if let hum = aNode["hum"] as? String, let humFloat = Float(hum) { on one iteration (let's say the second one which is exactly your case in the sample you gave there is no temp) but success on all the others?
Here what's going to happen:
self.nodeidArray[3] and self.humArray[3] won't be correct, they'll be desynchronized, right?
Because self.nodeidArray[2] should be with self.humArray[1].
Theses values are meant to be synce'd, so use a custom Struct/Class for it:
After all, Swift is a OOP (Object Oriented Programming) language, so use objects.
struct Node {
let id: String?
let name: String?
let humidity: Float?
let temperature: Float?
}
So instead of having:
var nodenameArray: [String] = []
var nodeidArray: [String] = []
var tempArray: [Float] = []
var humArray: [Float] = []
Just have:
var nodes: [Node] = []
And during the parsing, do
let node = Node.init(withDict: aNode)
nodes.append(node) //or insert it at 0
You can construct your own method init in Node, let's say:
func init(withDict dict: [String: Any]) {
if let id = dict["node_id"] as? String {
self.id = id
}
etc.
}
Having this Node class simplify. But using the JSONSerialization and init all the values manually could be avoided using Codable (available in Swift 4).
Side Note:
This code is not tested, there might be some glitch, typo etc. Let's focus on the explanation instead.
Lastly concerning the error:
Could not cast value of type '__NSCFNumber' (0x10db6f878) to
'NSString' (0x10ca71568). 2018-10-11 08:11:05.352491+0700 IOT
2[1506:101847] Could not cast value of type '__NSCFNumber'
(0x10db6f878) to 'NSString' (0x10ca71568).
error is in this line :
self.humArray.insert(Float(Int(nodeid as! String)!), at: 0)
The error means that at some point you tried to cas (using as) on a a (NS)String, but is was in fact NSNumber (in Swift, it's easily converted into an Int/Float, etc.), so it failed.
So the part that failed: nodeid as! String, because nodeid is not a (NS)String

'NSRangeException', reason: [__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray -> it is keep getting out of index bounds

[["The Cheesecake Factory", 3.8], ["Dave & Buster\'s", 3.8], ["Paul Martin\'s American Grill", 4.2], ["Yard House", 4.3], ["Javier\'s Restaurant - Irvine", 4], ["CUCINA enoteca Irvine", 4.4], ["SUBWAY®Restaurants", 3.2], ["SUBWAY®Restaurants", 4], ["Wendy\'s", 3.8], ["Izakaya Wasa", 3.7], ["Veggie Grill", 4.5], ["Bruegger\'s Bagels", 4.5], ["Capital Seafood Restaurant - Irvine Spectrum", 4], ["Burger King", 3.5], ["SUBWAY®Restaurants", 2.6], ["Corner Bakery Cafe", 3.9], ["Taiko Japanese Restaurant", 4.3], ["Red Robin Gourmet Burgers", 3.7], ["Johnny Rockets", 2.9], ["Chipotle Mexican Grill", 4]]
20
2017-11-15 13:26:36.367072-0800 E-Bike App[2584:1830704] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
*** First throw call stack:
(0x183ef5d04 0x183144528 0x183e510c4 0x102debe4c 0x102cafc84 0x184480f94 0x1844983b4 0x1848c1310 0x1848019e4 0x1847f1620 0x10532545c 0x105331b74 0x10532545c 0x105331b74 0x105331a34 0x1848c2fe8 0x10532545c 0x105332800 0x10533109c 0x105336b54 0x105336880 0x183b1f120 0x183b1ec20)
libc++abi.dylib: terminating with uncaught exception of type NSException'
Once I get the place name, rating and location (CLLocationCoordinate2D) above and save in the double array and parse index and location to drawPolylineAmongMultiplePoints function to find the distance and duration and append or push to the double array. NSRangeException is keep poping up..
Anyone has good idea to solve this?
func drawPolylineAmongMultiplePoints(coordinate: CLLocationCoordinate2D, pinPoint: Int) {
guard let lat = mapView.myLocation?.coordinate.latitude else {return}
guard let long = mapView.myLocation?.coordinate.longitude else {return}
let aPointCoordinate = "\(lat),\(long)"
let bPointCoordinate = "\(coordinate.latitude),\(coordinate.longitude)"
let url = "http://maps.googleapis.com/maps/api/directions/json?origin=\(aPointCoordinate)&destination=\(bPointCoordinate)&sensor=false&mode=\(DrivingMode.DRIVING)"
guard let urlString = URL(string: url) else {
print("Error: Cannot create URL")
return
}
let urlRequest = URLRequest(url: urlString)
// Set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// Make the request
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
do{
if error != nil{
print("Error: \(String(describing: error?.localizedDescription))")
} else {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}
let arrayRoutes = json["routes"] as! NSArray
let dicOfPoints = arrayRoutes[0] as! NSDictionary
let dic1 = dicOfPoints["overview_polyline"] as! NSDictionary
let points = dic1["points"] as! String
let arrayLegs = (arrayRoutes[0] as! NSDictionary).object(forKey: "legs") as! NSArray
let arraySteps = arrayLegs[0] as! NSDictionary
let dicDistance = arraySteps["distance"] as! NSDictionary
let totalDistance = dicDistance["text"] as! String
self.totalremainingDistance = (dicDistance["value"] as! Double)*(1/1000)*(1.61)
let dicDuration = arraySteps["duration"] as! NSDictionary
let totalDuration = dicDuration["text"] as! String
self.totalremainingDuration = dicDuration["value"] as! Double
let position = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
//self.eachCarouselDataDic[index] += [totalDistance, totalDuration]
//print(self.eachCarouselDataDic)
self.eachCarouselDataDic[pinPoint] += [totalDistance, totalDuration, points, position]
//print(self.eachCarouselDataDic)
}
}catch let error as JSONError {
print(error.rawValue)
}catch let error as NSError {
print(error.debugDescription)
}
})
task.resume()
}
#objc func POIForPlaces(sender: UIButton) {
print("I am here~~~")
//mapView.clear()
//For Carousel view to have access to the POI elements
isThisFirstTime = false
self.eachCarouselDataDic.removeAll()
var typeOfPlace = String()
var markerImage = UIImage()
switch sender.tag {
case 0:
typeOfPlace = "cafe"
markerImage = UIImage(named: "cafe")!
case 1:
typeOfPlace = "restaurant"
markerImage = UIImage(named: "restaurant")!
default:
break
}
let markerView = UIImageView(image: markerImage)
guard let lat = mapView.myLocation?.coordinate.latitude else {return}
guard let long = mapView.myLocation?.coordinate.longitude else {return}
var arrayOfLocations = [CLLocationCoordinate2D()]
arrayOfLocations.removeFirst()
var arrayOfNames = [String()]
arrayOfNames.removeFirst()
var arrayOfAddress = [String()]
arrayOfAddress.removeFirst()
var arrayOfRating = [Double()]
arrayOfRating.removeFirst()
var name = String()
var counter = 0
let jsonURLString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(lat),\(long)&maxprice=3&radius=3200&opennow&type=\(typeOfPlace)&key=\(Config.GOOGLE_API_KEY)"
guard let urlString = URL(string: jsonURLString) else {
print("Error: Cannot create URL")
return
}
//markerView.tintColor = UIColor.DTIBlue()
let urlRequest = URLRequest(url: urlString)
// Set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse else { return }
switch httpResponse.statusCode {
case 200:
do{
guard let data = data else { return }
guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary else { return }
//print(json)
DispatchQueue.global(qos: .background).async {
let arrayPlaces = json["results"] as! NSArray
if arrayPlaces.count > 0 {
for i in 0..<arrayPlaces.count {
print("-----------------------------------------------------------------------------------------------------")
print(arrayPlaces.count)
print("-----------------------------------------------------------------------------------------------------")
self.eachCarouselDataDic.append([])
let arrayForLocations = (((arrayPlaces[i] as! NSDictionary).object(forKey: "geometry") as! NSDictionary).object(forKey: "location") as! NSDictionary)
let lat = arrayForLocations.object(forKey: "lat")
let long = arrayForLocations.object(forKey: "lng")
let position = CLLocationCoordinate2D(latitude: lat as! CLLocationDegrees, longitude: long as! CLLocationDegrees)
let arrayForName = (arrayPlaces[i] as! NSDictionary).object(forKey: "name") as! String
let arrayForAddress = (arrayPlaces[i] as! NSDictionary).object(forKey: "vicinity") as! String
if let arrayForRating = (arrayPlaces[i] as! NSDictionary).object(forKey: "rating") as? NSNumber {
arrayOfRating.append(Double(truncating: arrayForRating).rounded(toPlaces: 1))
self.eachCarouselDataDic[i] += [arrayForName, arrayForRating]
print(self.eachCarouselDataDic)
} else {
arrayOfRating.append(0.0)
self.eachCarouselDataDic[i] += [arrayForName,0.0]
print(self.eachCarouselDataDic)
}
print("-----------------------------------------------------------------------------------------------------")
print(self.eachCarouselDataDic.count)
print("-----------------------------------------------------------------------------------------------------")
arrayOfNames.append(arrayForName)
arrayOfAddress.append(arrayForAddress)
arrayOfLocations.append(position)
//self.eachCarouselDataDic[i] += [arrayForName,arrayForRating]
//self.drawPolylineAmongMultiplePoints(coordinate: position, index: counter)
counter += 1
let nearbyMarker = GMSMarker()
//var position = CLLocationCoordinate2D()
nearbyMarker.iconView = markerView
name = arrayOfNames[i]
nearbyMarker.tracksViewChanges = true
nearbyMarker.title = name
nearbyMarker.position = position
nearbyMarker.snippet = "Rating = \(arrayOfRating[i]) \(self.ratingSystem(rating: arrayOfRating[i]))\n Address = \(arrayOfAddress[i])"
nearbyMarker.map = self.mapView
}
}
for i in 0..<counter {
self.drawPolylineAmongMultiplePoints(coordinate: arrayOfLocations[i], pinPoint: i)
}
}
}catch let error as NSError {
print(error.debugDescription)
}
default:
print("HTTP Reponse Code: \(httpResponse.statusCode)")
}
}
task.resume()
UIView.animate(withDuration: 3, animations: {
self.isCarouselActive = true
self.myLocationButton.setImage(UIImage(named: "carousel"), for: .normal)
})
}
The error tells you the problem:
index 0 beyond bounds for empty NSArray
So you have an empty array when you don't expect one.
Use the debugger and/or print() statements to examine the values of your variables. Work back from the point of failure to discover where it goes wrong.
You really need to do this yourself, you've provided a lot of code for people to work through. If you get stuck and cannot figure out the problem all a new question, include just sufficient code to show issue, explain what you've done and can't figure out, etc. Someone will undoubtedly help you at that point.

Placing marker of bus station on googlemap

I am having trouble with placing marker of bus station which was near me on my google map application.
Let say I live in New York City,I want all bus station with marker on my google map.But,i really don't know how to fetch all the bus station data of my current city(New York) and place a picker on it.I tried AppCoda tutorial and it gives me only marker of my custom search
Here : http://www.appcoda.com/google-maps-api-tutorial/
Is there any way that i can modified its "MapTask.Swift" code to get all bus station near me and place picker on each bus station?
Here is MapTask.Swift
import UIKit
import CoreLocation
import SwiftyJSON
class MapTask: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
override init() {
super.init()
}
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let dictionary: Dictionary<NSObject, AnyObject> = try!NSJSONSerialization.JSONObjectWithData(geocodingResultsData!, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<NSObject, AnyObject>
// Get the response status.
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
print("The result is : \(dictionary)")
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
})
}
else {
completionHandler(status: "No valid address.", success: false)
}
}
}
You'll need to call the Place Search with the type set to bus_station to limit the return to that specific type. The response will return results with their details that you can use to set the Markers.

Resources