EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) with dataTaskWithUrl - exception

I'm using the google places api to search for nearby places. However, I only want places of specific types. The code (seen below) works when I specify just one type, but when I add a second my code runs and promptly give me a EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) error on this line:
session.dataTaskWithURL(url!, completionHandler: { (data : NSData!, response : NSURLResponse!, error : NSError!) -> Void in
I know the url is valid. I can plug it into the browser and see the json, so I don't understand what the problem is.
func search(location : CLLocationCoordinate2D, radius : Int, callback : (items : [Attraction]?, errorDescription : String?) -> Void) {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=37.7873589,-122.408227&radius=4000&types=aquarium|art_gallery&key=YOURKEY"
var url = NSURL(string: urlString)
var session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
session.dataTaskWithURL(url!, completionHandler: { (data : NSData!, response : NSURLResponse!, error : NSError!) -> Void in
if error != nil {
callback(items: nil, errorDescription: error.localizedDescription)
}
if let statusCode = response as? NSHTTPURLResponse {
if statusCode.statusCode != 200 {
callback(items: nil, errorDescription: "Could not continue. HTTP Status Code was \(statusCode)")
}
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
callback(items: GooglePlaces.parseFromData(data), errorDescription: nil)
})
}).resume()
}
class func parseFromData(data : NSData) -> [Attraction] {
var attractions = [Attraction]()
var json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var results = json["results"] as? [NSDictionary]
for result in results! {
var placeId = result["place_id"] as String
var image = result["icon"] as String
var name = result["name"] as String
var ratingString = ""
var types = result["types"] as [String]
println(types)
if result["rating"] != nil {
var rating = result["rating"] as Double
ratingString = "\(rating)"
}
var coordinate : CLLocationCoordinate2D!
if let geometry = result["geometry"] as? NSDictionary {
if let location = geometry["location"] as? NSDictionary {
var lat = location["lat"] as CLLocationDegrees
var long = location["lng"] as CLLocationDegrees
coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
var placemark = MKPlacemark(coordinate: coordinate, addressDictionary: nil)
var attraction = Attraction(id: placeId, imageUrl: "image url", locationName: name, ratingAvg: "\(ratingString)", types: types, placemarker: placemark)
attractions.append(attraction)
}
}
}
return attractions
}

I know the url is valid
The URL is not valid. You do not know what you think you know. Listen to the runtime. It knows more than you do.
Just try this code alone (in a playground, for instance):
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=37.7873589,-122.408227&radius=4000&types=aquarium|art_gallery&key=YOURKEY"
let url = NSURL(string:urlString)
url is nil. And that's your problem. You cannot force-unwrap nil; you will crash if you do.
Once you acknowledge this, you can start to think about why the URL is not valid. (It's pretty obvious why that might be.) Learning to believe the compiler and the runtime is key to successful programming.
HINT: Form your URL like this and all is well:
let url2 = NSURL(scheme: "https", host: "maps.googleapis.com", path: "/maps/api/place/nearbysearch/json?location=37.7873589,-122.408227&radius=4000&types=aquarium|art_gallery&key=YOURKEY")
Why do you suppose that is? Look at the docs and see what this initializer does for you...

I would use .stringByAddingPercentEscapesUsingEncoding()
var urlString = "http://example.com/?foo=bar|baz"
if var escapedURLString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
NSURL(string: escapedURLString)
}
Returns: http://example.com/?foo=bar%7Cbaz

Related

Unable to add parameter to API in swift

I am getting parameter value from other viewcontroller, and i am getting parameter valueperfectly but which i am unable to add to API
here if i hardcode eventStatus then its working
and eventStatus value from otherviewcontroller also coming perfectly which i am unable to add to API
if i hard code like this its working
var eventType = "Draft"
let string = Constants.GLOBAL_URL + "/get/allevents/?eventstatus=\(self.eventType)"
Code: here i am getting correct eventStatus value but while breakpoint its control goes to else, why?
class EventsViewController: UIViewController {
var eventsListArray = [AnyObject]()
// var eventType = "Draft"
var eventType: String!
var eventList : EventsModel? = nil
#IBOutlet weak var eventsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getAllEventsList()
}
func getAllEventsList() {
DispatchQueue.main.async {
let headers = ["deviceid": deviceId,"userType": "personal","key": personalId]
DispatchQueue.main.async {
//let string = Constants.GLOBAL_URL + "/get/allevents/?eventstatus=\(self.eventType)"
let string = Constants.GLOBAL_URL + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType
print("event status value in API call \(eventStatus)")
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
var request = URLRequest(url: urlStr!, cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers as! [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
DispatchQueue.main.async {
if error == nil {
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!) as! [String :Any]
print("publish event \(jsonObject)")
self.eventList = EventsModel.init(fromDictionary: jsonObject)
DispatchQueue.main.async {
if self.eventList?.events.count != 0 {
DispatchQueue.main.async {
self.eventsTableView.reloadData()
}
}
else {
DispatchQueue.main.async {
Constants.showAlertView(alertViewTitle: "", Message: "No Events \(self.eventType)", on: self)
self.eventList?.events.removeAll()
self.eventsTableView.reloadData()
}
}
}
} catch { print(error.localizedDescription) }
} else {
Constants.showAlertView(alertViewTitle: "", Message: "Something went wrong, Please try again", on: self)
}
}
}
})
dataTask.resume()
}
}
}
}
where i am wrong, why eventStatus value not adding to API.. please suggest me
it looks like you have a POST request and you need to use request data instead URL parameters.
HTTP Request in Swift with POST method here you can see:
let parameters: [String: Any] = [
"eventstatus": yourValue
]
request.httpBody = parameters.percentEncoded()
You also need to create parameters with eventStatus. And put it in
request.httpBody = parameters.percentEncoded()
If this endpoint on your server waits on this data in request body than you could not add it like a URL parameter.
Also, don't forget these 2 extensions from the example
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
The solution must be obvious from the discussion in the comments. Although you seem a little puzzled so, I'm adding this to help you out. The value of eventType is never getting allocated to a value, it is remaining as nil unless you set it's value at a point in your code before the API call. So here is a way for you to figure this out:
let string = "https://www.google.com" + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType ?? "Published" // here value is defaulted to "Published"
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
print(urlStr?.absoluteString)
Here, we're setting a default value for eventType for the scenario where eventType is nil.

optional type String? not unwrapped

I have a PHP page that is on my webserver that interacts with a mysql database called grabmapinfo.php
The output of the page is [{"companyname":"Brunos Burgers","companyphone":"7745632382","companytown":"858 Western Ave, Lynn, MA 01905"}]
Now I have this Swift code, which I want to get the info from the database, geocode the address to latitude and longitude, plot the annotation on the map, change the annotation image and title, and make a circle with a radius of 5 with the pin being in the center.
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.buyerMapView1.showsUserLocation = true
let url = NSURL(string: "https://alanr917.000webhostapp.com/grabmapinfo.php")
var request = URLRequest(url:url! as URL)
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
if error != nil {
// Display an alert message
print(error)
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String:AnyObject]] {
for item in json {
// Get company info from DB
let companyname = item["companyname"] as? String
let companyphone = item["companyphone"] as? String
let companytown = item["companytown"] as? String
print("Company : \(companyname)")
print("Phone : \(companyphone)")
print("Address : \(companytown)")
let address = companytown
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {
(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
let pa = MKPointAnnotation()
pa.coordinate = placemark.location.coordinate
pa.title = companyname
pa.imageName = #imageLiteral(resourceName: "growerAnnotation")
self.buyerMapView1.addAnnotation(pa)
let center = annotation.coordinate
let circle = MKCircle(center: center, radius: 5) // change the 5 later to however many miles the grower purchased
self.buyerMapView1.add(circle)
}
})
}
}
} catch {
print(error)
}
})
}
But i get an error that says the optional type String? is not unwrapped and it errors out and wont build.
Does anyone see where I'm going wrong? Thanks!
companyTown is declared as an optional string and the geocodeAddressString method takes a string. You need to unwrap the option before calling it.
if let addressUnwrapped = address {
geocoder.geocodeAddressString(addressUnwrapped, completionHandler: {
(placemarks: [AnyObject]!, error: NSError!) -> Void in
...
})
}
Please check the comments through the code for more detailed explanation on the problems that I found in your code:
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var buyerMapView1: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
buyerMapView1.showsUserLocation = true
// first unwrap your url
guard let url = URL(string: "https://alanr917.000webhostapp.com/grabmapinfo.php") else { return }
print("url:",url)
// no need to create a request. just a url is fine and you don't need to specify the parameters type. Let the compiler infer it.
URLSession.shared.dataTask(with: url) { data, response, error in
// unwrap your data and make sure there is no error
guard let data = data, error == nil else {
print(error ?? "nil")
return
}
// you should update the UI from the main queue
DispatchQueue.main.async {
print("data:", data)
do {
if let array = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
for dict in array {
// make sure you unwrap your dictionary strings
let companyname = dict["companyname"] as? String ?? ""
let companyphone = dict["companyphone"] as? String ?? ""
let companytown = dict["companytown"] as? String ?? ""
print("Company:", companyname)
print("Phone:", companyphone)
print("Address:", companytown)
let address = companytown
let geocoder = CLGeocoder()
// again let the compiler infer the types vvv vvv
geocoder.geocodeAddressString(address) { placemarks, error in
if let placemark = placemarks?.first,
let coordinate = placemark.location?.coordinate {
let pa = MKPointAnnotation()
pa.coordinate = coordinate
pa.title = companyname
self.buyerMapView1.addAnnotation(pa)
let center = pa.coordinate // where does this coordinate come from??
let circle = MKCircle(center: center, radius: 5)
self.buyerMapView1.add(circle)
}
}
}
}
} catch {
print(error)
}
}
// you forgot to call resume to start your data task
}.resume()
}
}

Validating Information Before Moving to Next View in Swift

I have an application where I need to validate some information(zip code) from a database before I allow my iOS application to proceed to the next view. I used the zip code project to import a DB Table will all valid US Zip codes, and I want to have the zip code the inputed by the user validated before I allow them to proceed. If the zip code isn't valid, I hold them up at the current view and display an alert. I have a class to validate the zip code, but the zip code isn't being validated until after the next view is loaded. I've been leaning towards using a completion handler, but I'm not exactly sure if that's my best/only option. Thanks in advance.
EDIT:
The following is the whole class for retrieve the data
protocol ZipCodeLocationProtocol: class {
func zipCodeLocationDownloaded(zipLocation: Location)
}
class RetrieveZipCodeLocation: NSObject, NSURLSessionDataDelegate {
// MARK: Properties
weak var delegate: ZipCodeLocationProtocol!
var data: NSMutableData = NSMutableData()
let urlPath: String = "xxxx"
func downloadZipCodeLocation(zipcode: Int) {
let path = self.urlPath + "?zipcode=\(zipcode)"
let url: NSURL = NSURL(string: path)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
var location = Location(title: "TITLE", coordinate: CLLocationCoordinate2D(latitude: 0, longitude: 0))
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:[]) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
for(var i = 0; i < jsonResult.count; i++) {
jsonElement = jsonResult[i] as! NSDictionary
let point = CLLocationCoordinate2D(latitude: (jsonElement["LATITUDE"] as! NSString).doubleValue, longitude: (jsonElement["LONGITUDE"] as! NSString).doubleValue)
// Get Information
location = Location(title: "TITLE", coordinate: point)
self.delegate.zipCodeLocationDownloaded(location)
}
}
I'm going to assume that a button triggers the segue to the next view. I'm also going to assume that the button is hooked up to a function for target-action. I'm also going to assume that you have the code to get the zip codes, otherwise you'll have to ask a separate question for that.
Assumptions aside, you need to present a UIAlertController instead of going to the next view controller when tapping the button. In order to do that:
func buttonAction() {
if verifyZipCode() {
let alert = UIAlertController(title: "Hold Up", message: "That zip code is invalid.", preferredStyle: .Alert)
let fixIt = UIAlertAction(title: "Fix It!", style: .Default, handler: nil) // handler could also contain code to make text field red or something interesting
alert.addAction(fixIt)
presentViewController(alert, animated: true, completion: nil)
} else {
// existing segue code
}
}
func verifyZipCode() -> Bool {
// Take text field text and verify zip code
}

Swift - Crash when try to get JSON values for second time

I am trying to get an array of values from a JSON file using Swift. I found the way to get the values printed when a button is pressed using a IBAction method.
The Function is printing the array fine the first time the button is pressed however, if I press the button again the app crashes giving the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
The array that I get the first time from printing jsonResult looks Like:
1: {
Age = 30;
Email = "info#example.com";
Location = Rome;
MyText = "Design Agency";
Name = Admin;
Password = 123456;
REF = 002;
"Reg_Date" = "2015-07-28";
Sex = Male;
Surname = "Mobile App"; }
2: {
Age = 30;
Email = "example#gmail.com";
Location = London;
MyText = "aaaaaaaaa";
Name = Andrew;
Password = 123456;
REF = 001;
"Reg_Date" = "2015-07-28";
Sex = Male;
Surname = Nos; }
It seems that the second time I call the function, it cannot read the file anymore.
Whats wrong with my code? Do I need to close the connection?
#IBAction func login_button(sender: AnyObject) {
searchFunction()
}
func searchFunction() {
var tempUrlValue = loginJsonResults.stringForKey("loginJsonResults")
let urlPath: String = "https://example.com/iOS/users_result.json"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.data.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var err: NSError
var jsonResult: NSMutableArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSMutableArray
//Call jsonResult Values
var temporaryString: AnyObject = jsonResult[1]
for (index, jsonResult) in enumerate(jsonResult){
println("\(index + 1): \(jsonResult)")
countUsers++
}
println(countUsers)
}

Watchkit table flickers then disappears completely

I have a watchkit table that is populated by information from a JSON. When I run the program the table becomes populated, however it starts to flicker then disappears completely. This happens every time I run it, and sometimes the table just doesn't show up at all. Anyone know why this might be happening?
Edit: Sorry, I should have put my code when I posted this question.
#IBOutlet weak var earthTable: WKInterfaceTable!
//Create cells
private func loadTableData() {
// getEarthquakeInfo is the function that I use to parse the JSON and grab the infomation I need into info.
getEarthquakeInfo { (info) in
self.earthTable.setNumberOfRows(info.count, withRowType: "earthquake")
//Create cells
for index in 0..<self.earthTable.numberOfRows {
var currentRow = self.earthTable.rowControllerAtIndex(index) as earthquakeViewController
let time = info[index].time
let mag = info[index].mag
let title = info[index].title
currentRow.titleLabel.setText("\(title)")
currentRow.timeLabel.setText("\(time)")
currentRow.magLabel.setText("\(mag)")
}
}
}
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
loadTableData()
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
}
Here's my function, getEarthquakeInfo at the top of my interfaceController.swift:
class InterfaceController: WKInterfaceController {
var info = [AppModel]()
func getEarthquakeInfo(completion: (results : [AppModel]) ->Void ){
DataManager.getEarthquakeDataFromFileWithSuccess {
(data) -> Void in
let json = JSON(data: data)
if var JsonArray = json.array {
JsonArray.removeAtIndex(0)
for appDict in JsonArray {
// parsing
var ids: String? = appDict["id"].stringValue
var title: String? = appDict["title"].stringValue
var time: String? = appDict["time"].stringValue
var lattitude: String? = appDict["lat"].stringValue
var longitude: String? = appDict["lng"].stringValue
var north: String? = appDict["north"].stringValue
var west: String? = appDict["west"].stringValue
var mag: String? = appDict["mag"].stringValue
var depth: String? = appDict["depth"].stringValue
var timeStamp: String? = appDict["timestamp"].stringValue
// Splitting up title string into 2 parts
let newString = title!.stringByReplacingOccurrencesOfString(" ", withString: " - ", options: NSStringCompareOptions.LiteralSearch, range: nil)
var title2strings = newString.componentsSeparatedByString(" - ")
var scale = title2strings[0]
var location = title2strings[1]
// replacing M in scale string with Richter Scale
let scaleString = scale.stringByReplacingOccurrencesOfString("ML", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString2 = scaleString.stringByReplacingOccurrencesOfString("mb", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString3 = scaleString2.stringByReplacingOccurrencesOfString("Mw", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString4 = scaleString3.stringByReplacingOccurrencesOfString("MD", withString: "Magnitude", options: NSStringCompareOptions.LiteralSearch, range: nil)
let scaleString5 = scaleString4.stringByReplacingOccurrencesOfString("M ", withString: "Magnitude ", options: NSStringCompareOptions.LiteralSearch, range: nil)
//Formatting the date
var date = NSDate(dateString: time!).getDatePart()
// Collecting all the information
var information = AppModel(idEarth: ids, title: title, time: date, lat: lattitude, lng: longitude, north: north!, west: west, mag: mag, depth: depth, timeStamp: timeStamp, location: location, scale: scaleString5)
self.info.append(information)
//sorting array by highest magnitude
// self.info.sort({$0.mag > $1.mag})
// returning the completion handler
completion(results: self.info)
}
}
}
}
However, I think this is where the problem is at. This is the file where I call the web service. When I debugged it, the data is retrieved however the table crashes once it's populated.
My DataManager.swift file:
import Foundation
let earthquakeURL = "http://www.kuakes.com/json/"
class DataManager {
class func getEarthquakeDataFromFileWithSuccess(success: ((websiteData: NSData) -> Void)) {
//1
loadDataFromURL(NSURL(string: earthquakeURL)!, completion:{(data, error) -> Void in
//2
if let urlData = data {
//3
success(websiteData: urlData) // When I debug, this line is hit and the data is being received, the table populates, however like I said it just disappears completely.
}
else {
println("nothing")
}
})
}
class func loadDataFromURL(url: NSURL, completion:(data: NSData?, error: NSError?) -> Void) {
var session = NSURLSession.sharedSession()
// Use NSURLSession to get data from an NSURL
let loadDataTask = session.dataTaskWithURL(url, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if let responseError = error {
completion(data: nil, error: responseError)
} else if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode != 200 {
var statusError = NSError(domain:"com.kuakes", code:httpResponse.statusCode, userInfo:[NSLocalizedDescriptionKey : "HTTP status code has unexpected value."])
completion(data: nil, error: statusError)
} else {
completion(data: data, error: nil)
}
}
})
loadDataTask.resume()
}
}