I've encountered a problem when I was working on my private project. I just improved my environment by buying a Virtual Private Server (VPS). I installed Apache, MySQL and PHP to be able to save data in a database followed by parsing it to a PHP and the final stage displaying it on an iOS app I'm creating.
I looked through quite few tutorials on the internet on how to retrieve the data from a php file and show it in a tableView and this is where my first problem is encountered. I'm not going to use a tableView and I for some reason have been struggling with just getting the data into an dictionary/array without any issues.
Problem:
I've followed a tutorial and made it work for a tableView but when I was trying to customize the output I don't manage to get it right.
I get the information to get saved to a dictionary but when I try to use the dictionary in any kind of way i get a breakpoint and the simulation just stops.
Code:
Class - LocationModel.swift
import Foundation
class LocationModel: NSObject {
var id: String?
var name: String?
var address: String?
var city: String?
var country: String?
var typ: String?
var lastresult: String?
override init() {
}
init(id: String, name: String, address: String, city: String, country: String, typ: String, lastresult: String) {
self.id = id
self.name = name
self.address = address
self.city = city
self.country = country
self.typ = typ
self.lastresult = lastresult
}
override var description: String {
return "Name: \(name), Address: \(address)"
}
}
Class - HomeModel.swift
import Foundation
protocol HomeModelProtocal: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, NSURLSessionDataDelegate {
weak var delegate: HomeModelProtocal!
var data : NSMutableData = NSMutableData()
let urlPath: String = "http://server.truesight.se/risSwiftPHP/phptest.php"
func downloadItems() {
let url: NSURL = NSURL(string: urlPath)!
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()
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for item in jsonResult {
jsonElement = item as! NSDictionary
let location = LocationModel()
if let id = jsonElement["id"] as? String,
let name = jsonElement["name"] as? String,
let address = jsonElement["address"] as? String,
let city = jsonElement["city"] as? String,
let country = jsonElement["country"] as? String,
let typ = jsonElement["typ"] as? String,
let lastresult = jsonElement["lastresult"] as? String
{
print(id + name + address + country + city + typ + lastresult)
location.id = id
location.name = name
location.address = address
location.city = city
location.country = country
location.typ = typ
location.lastresult = lastresult
}
if let name = jsonElement["name"] as? String,
let address = jsonElement["address"] as? String
{
location.name = name
location.address = address
}
locations.addObject(location)
}
dispatch_async(dispatch_get_main_queue(), {() -> Void in
self.delegate.itemsDownloaded(locations)
})
}
}
It is un the method/function parseJSON in the class HomeModel where the breakpoint appears. It's the let location = LocationModel() that breaks the code. I've tried to search through the debug for more information and been using po $arg0 on the threads to try find more but I only get the error message Errored out in Execute, couldn't PrepareToExecuteJITExpression
I do get the jsonResult NSMutableArray filled with information and the jsonElement as well but after that it breaks.
I would really appreciate some help in the matter as I'm soon out of hair on my head because of this problem. If you find the solution bad or want any more information please just ask.
I feel like I've solved it but I'm not sure if it's the ultimate solution. I moved the following section from parserJSON to URLSession.
From parseJSON
var jsonResult: NSMutableArray = NSMutableArray()
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
To function URLSession
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?){
if error != nil {
print("Failed to download data")
} else {
print("Data downloaded")
var jsonResult: NSMutableArray = NSMutableArray()
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
// Array is jsonResult,
// I sent it to my ViewController to control the output.
print(jsonResult)
var VC = ViewController()
VC.SomeMethod(jsonResult)
}
}
Related
I have this issue when trying to read my data which is json encoded from the php page to the swift page.
this is the code I am using
import Foundation
protocol HomeModelProtocol: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, URLSessionDataDelegate {
//properties
weak var delegate: HomeModelProtocol!
var data = Data()
let urlPath: String = "http://localhost/service.php" //this will be changed to the path where service.php lives
func downloadItems() {
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") // this work fine
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
print(jsonResult) // this print empty parentheses
print(String(data: data, encoding: .utf8)) // this prints out the array
//the code below throughs an arror
do{
jsonResult = try JSONSerialization.jsonObject(with:data, options:JSONSerialization.ReadingOptions.allowFragments) as! [NSArray] as NSArray
print(jsonResult)
} 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 name = jsonElement["Name"] as? String,
let address = jsonElement["Address"] as? String,
let latitude = jsonElement["Latitude"] as? String,
let longitude = jsonElement["Longitude"] as? String
{
location.name = name
location.address = address
location.latitude = latitude
location.longitude = longitude
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
}
this is the output which I am receiving:
Data downloaded
(
)
Optional(" \nconnectedinside[{\"name\":\"One\",\"add\":\"One\",\"lat\":\"1\",\"long\":\"1\"},{\"name\":\"Two\",\"add\":\"Two\",\"lat\":\"2\",\"long\":\"2\"},{\"name\":\"One\",\"add\":\"One\",\"lat\":\"1\",\"long\":\"1\"},{\"name\":\"Two\",\"add\":\"Two\",\"lat\":\"2\",\"long\":\"2\"}]")
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around
character 2." UserInfo={NSDebugDescription=Invalid value around
character 2.}
You get this error, because the json response you receive is not an array but a dictionary.
EDIT: as pointed out in a comment, you first need to fix your json response in your php code. There is ":" missing after "connectedinside".
It should look like this:
{\"connectedinside\":[{\"name\":\"One\",\"add\":"One",...},...]}
My suggestion to fix this:
You should have two models:
struct HomeModelResponse: Codable {
let connectedinside: [LocationModel]
}
// your LocationModel should look like this:
struct LocationModel: Codable {
let name: String
let add: String
let lat: String
let long: String
}
And change your JSONDecoding code to:
do {
jsonResult = try? JSONDecoder().decode(HomeModelResponse.self, from: data)
print()
} catch let exception {
print("received exception while decoding: \(exception)"
}
Then you can access your LocationModels by jsonResult.connectedinside
The problem was on my php side and I fixed it.it is working now.
This is my code and I am unable to compile it .
I am trying to login to my server but it doesnt allow me so
Sorry I am new at programming and I've researched on stackoverflow, regarding this error , I can only parse Dictionary or Array. but I've googled by copying my JSON response but it doesnt work.
Any Suggestions would be helpful !!
import UIKit
class Login: UIViewController {
#IBOutlet var Username: UITextField!
#IBOutlet var Password: UITextField!
#IBAction func Login(sender: UIButton) {
let username=Username.text
let password=Password.text
let URL_LOGIN="http://172.22.95.116/SoilCondition/app/getLogin.php?USERNAME=" + username! + "&PASSWORD=" + password!;
let requestURL = NSURL(string: URL_LOGIN)
let request = NSMutableURLRequest(URL: requestURL!)
request.HTTPMethod = "POST"
let postParameters = "username="+username!+"&password="+password!;
request.HTTPBody = postParameters.dataUsingEncoding(NSUTF8StringEncoding)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
data, response, error in guard let data = data where error == nil
else {
print("error: \(error)")
return
}
do{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
if let parseJSON = myJSON{
var msg: String!
msg = parseJSON["message"] as! String?
print(msg)
}
/* if let parseJSON = myJSON {
var msg : String!
msg = parseJSON["message"] as! String?
print(msg)
}*/
/*if data != nil {
json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
println("json: \(json)")
if let dictionary = parseJSON(jsonString) {
println("dictionary: \(dictionary)")
}*/
} catch let parseError{
print(parseError)
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I found this question regarding error code 3840.
As it says there, the problem could be that your server doesn't return valid JSON to you.
Now, you say:
I can only parse Dictionary or Array
I don't know if that means that you are able to actually parse the response you receive from the server into valid JSON here:
let myJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
But if it doesn't then a good place to start could be to verify whether your server actually returns valid JSON to you.
To do so, you could try calling your server directly from cURL or postman and see what you get in return.
Hope that helps you.
I have a UIPickerview which represents countries and i am trying to get its data from MYSQL database but i couldnt handle the coming data from PHP file. I couldnt find any Swift solution so thats why i am here.
//its the default values of pickerview
var pickOption = ["one", "two", "three", "seven", "fifteen"]
func getCountries() {
let url:NSURL = NSURL(string: "http:/domain.com/getCountriesLong.php")!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
let task = session.downloadTaskWithRequest(request) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let urlContents = try! NSString(contentsOfURL: location!, encoding: NSUTF8StringEncoding)
guard let _:NSString = urlContents else {
print("error")
return
}
//all result is here
//print(urlContents)
//string to NSData conversion
let data = urlContents.dataUsingEncoding(NSUTF8StringEncoding)
do {
//parse NSData to JSON
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
//pickOption = json["countryName"]
//pickOption = json
} catch {
print("error serializing JSON: \(error)")
}
}
task.resume()
}
Here is the PHP
$menuData = $db->get_results("SELECT countryName FROM countries ORDER BY countryName ASC");
echo json_encode($menuData);
How can i use coming data as value of my UIPickerView ?
I found the solution. instead of using pickOption = json["countryName"] i have declared new array. Here it is;
var arr = [String]()
for name in json as! [AnyObject] {
if let country = name["countryName"] as? String {
arr.append(country)
}
}
self.pickOption = arr
I looked at the following questions and they did not address the issue I have:
Issue: Saving Json data to Core Data
JSON to core-data
The other suggested links were not even close to my issue. My issue is this - I am gathering data from my own domain, using JSON Decode, and then trying to insert the data into Core Data. Is is working 99% of the time but my app crashes occasionally and I get this error message:
2016-01-07 09:49:33.096 AppleLawnApp[564:400038] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2016-01-07 09:49:33.098 AppleLawnApp[564:400038] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
I have placed it inside of an action block of code for a button:
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let url = NSURL(string: "http://www.mydomain/file.php")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
if error == nil {
let urlContent = NSString(data: data!, encoding: NSUTF8StringEncoding) as NSString!
let data: NSData = urlContent.dataUsingEncoding(NSUTF8StringEncoding)!
do {
let jsonObject = try (NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as? NSArray)!
var index1 = 0
while index1 < jsonObject.count {
let newEntry = NSEntityDescription.insertNewObjectForEntityForName(MyTableData, inManagedObjectContext: context) as NSManagedObject
let maindata = (jsonObject[index1] as! NSDictionary)
let this1 = maindata["dBase1"] as! String
let this2 = maindata["dBase2"] as! String
let this3 = maindata["dBase3"] as! String
let this4 = maindata["dBase4"] as! String
let this5 = maindata["dBase5"] as! String
let this6 = maindata["dBase6"] as! String
newEntry.setValue(this1, forKey: "CD1")
newEntry.setValue(this2, forKey: "CD2")
newEntry.setValue(this3, forKey: "CD3")
newEntry.setValue(this4, forKey: "CD4")
newEntry.setValue(this5, forKey: "CD5")
newEntry.setValue(this6, forKey: "CD6")
do {
try context.save()
}
catch {
print(error)
}
let data1 = arrayOne(c1: this1 c2: this2, c3: this3, c4: this4, c5: this5, c6: this6)
self.myArray.append(data1)
index1++
}
}
catch {
print(error)
}
}
})
task.resume()
}
}
My first guess, based on the error message, is that you may be getting an unexpected null value back in the json where your model entity has a value marked as non-optional. Perhaps try to test the data before updating the NSManagedObject. Here's a couple of similar ways:
if let this1 = maindata["dBase1"] {
newEntry.setValue(this1, forKey: "CD1")
} else {
assertionFailure("missing dBase1")
}
OR, something like this:
guard let this2 = maindata["dBase2"]
, let this3 = maindata["dBase3"]
, let this4 = maindata["dBase4"] else {
assertionFailure("missing data")
return
}
newEntry.setValue(this2, forKey: "CD2")
newEntry.setValue(this3, forKey: "CD3")
newEntry.setValue(this4, forKey: "CD4")
I receive data from php file with this format:
{"object:value", "object2:value2"...}
{"object:value", "object2:value2"...}
I know how to parse this in Swift2 using next code repeatedly changing separatedBy string:
if let url = NSURL(string: strURL), let data = NSData(contentsOfURL: url) {
let strResult = NSString(data: data, encoding: NSUTF8StringEncoding)
}
tareas = strResult!.componentsSeparatedByString(",")
But I want parse this more easily. I have read others questions and answers but the format to parse was:
[
{"person": {"name":"Dani","age":"24"}},
{"person": {"name":"ray","age":"70"}}
]
And my data is different. How can I do this more easily?
Thanks!
You can read it as a Array like this
First of all you need to convert your data in valid json like this and then access it
var result: NSArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSArray
println("result: \(result)")
Ex.
var str = "[{\"person\": {\"name\":\"Dani\",\"age\":\"24\"}},{\"person\": {\"name\":\"ray\",\"age\":\"70\"}}]"
var data : NSData = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
var result: NSArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSArray
println("Response: \(result)")
println("Oth Object: \(result[0])")
Output
Response: (
{
person = {
age = 24;
name = Dani;
};
},
{
person = {
age = 70;
name = ray;
};
}
)
Oth Object: {
person = {
age = 24;
name = Dani;
};
}
I suggest you to use SwiftyJSON (6k+ stars) library that converts response to JSON object. Something like:
if let url = NSURL(string: strURL), let data = NSData(contentsOfURL: url) {
let json = JSON(data: data!)
}
After that you can easily extract any info from JSON:
for item:JSON in json.arrayValue{
let person = item["person"].dictionaryValue
//...
}
Playground printscreen