I am reading JSON from a URL and that has been working correctly. This is my code:
#IBOutlet weak var ProfilesCell: UITableView!
let cellspacing: CGFloat = 50
var names = [String]()
var posts = [String]()
var locations = [String]()
var votes = [String]()
var comments = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ProfilesCell.dataSource = self
let url:URL = URL(string: "http://"+Connection_String+":8000/profile_view")!
URLSession.shared.dataTask(with:url, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Profile = parsedData["Profile"] as! [AnyObject]?
{
for Stream in Profile {
if let fullname = Stream["fullname"] as? String {
self.names.append(fullname)
}
if let post = Stream["post"] as? String {
self.posts.append(post)
}
if let location = Stream["location"] as? String {
self.locations.append(location)
}
if let vote = Stream["votes"] as? String {
self.votes.append(vote.appending(" Votes"))
}
if let comment = Stream["comments"] as? String {
self.comments.append(comment.appending(" Comments"))
}
DispatchQueue.main.async {
self.ProfilesCell.reloadData()
}
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
That code above correctly parses the JSON and the data is returned to the TableView. I now want to do an HTTP Post before reading that JSON and the parameter name is profile_id and I know that is something wrong in my code because if I do an HTML form with the parameter, things work correctly.
This is the new code that I now have:
#IBOutlet weak var ProfilesCell: UITableView!
let cellspacing: CGFloat = 50
var names = [String]()
var posts = [String]()
var locations = [String]()
var votes = [String]()
var comments = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ProfilesCell.dataSource = self
let url:URL = URL(string: "http://"+Connection_String+":8000/profile_view")!
let ss = "32"
var request = URLRequest(url:url)
let paramString = "profile_id=\(ss)"
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.httpBody = paramString.data(using: .utf8)
URLSession.shared.dataTask(with:url, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Profile = parsedData["Profile"] as! [AnyObject]?
{
for Stream in Profile {
if let fullname = Stream["fullname"] as? String {
self.names.append(fullname)
}
if let post = Stream["post"] as? String {
self.posts.append(post)
}
if let location = Stream["location"] as? String {
self.locations.append(location)
}
if let vote = Stream["votes"] as? String {
self.votes.append(vote.appending(" Votes"))
}
if let comment = Stream["comments"] as? String {
self.comments.append(comment.appending(" Comments"))
}
DispatchQueue.main.async {
self.ProfilesCell.reloadData()
}
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
Now with this extra code the URL is still being hit but profile_id is showing null even though I have hardcoded the number 32. I also get this message displayed:
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}
Related
I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked
I have these JSON data.
(
{
email = "b#p.com.my";
login = ID001;
pw = 1234;
},
{
email = "p#d.com.my";
login = ID002;
pw = 12345;
}
)
Right now, I can only print myJSON value in x code output.
My question is, how to display each JSON into UILabel _email, _login, _pw?
Someone said that I need to store as variable and set into UILabel but i don't know how to do it. Appreciate if someone can help me on this matters.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var _email: UILabel!
#IBOutlet var _login: UILabel!
#IBOutlet var _pw: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost/get.php")
let task = URLSession.shared.dataTask(with: url!) {
(data, response, error) in
if error != nil {
print("Error")
return
}
else {
if let content = data {
do {
let myJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(myJSON)
}
catch {}
}
}
}
task.resume()
}
}
Thanks.
EDIT:
After the block where you print your myJSON, try this:
for eachItem in myJSON {
if let emailParsed = eachItem["email"] as? String {
print(emailParsed)
_email.text = emailParsed
}
}
This loop runs between all the dictionaries and
This solution could be helpful for you. Please go through it!
Hi Please try this one
for i..0 < myJson.count {
let object = myJson[i] as AnyObject
if let emailParsed = myJSON["email"] as? String {
_email.text = emailParsed
}
}
EDITED
for i..0 < myJson.count {
let object = myJson[i] as [String : AnyObject]
if let emailParsed = myJSON["email"] as? String {
_email.text = emailParsed
}
}
It's simple because your json object gives an array of dictionary so first you have to take first object from array and after that you can take the string passing the key
override func viewDidLoad()
{
super.viewDidLoad()
let url = URL(string: "http://localhost/get.php")
let task = URLSession.shared.dataTask(with: url!)
{
(data, response, error) in
if error != nil
{
print("Error")
return
}
else
{
if let content = data
{
do {
let myJSON = try JSONSerialization.jsonObject(with: content, options:JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
// print(myJSON)
for data in myJSON
{
let email = data.1["email"] as? String
_email.text = email
}
}
catch {}
}
}
}
task.resume()
}
}
For batter option you can use some predefined libraries like swiftyJSON. To get best result.
Trouble with JSON parsing in swift 1.2
I have an issue with the JSON parsing, i am not getting the correct data response for my parameters. i have been stuck on the issue for quite some time now. i don’t know how to fix it. i am new to iOS development. also i haven’t used any third party library.[Don’t know how to use them]
I am getting the right response for my very first iteration but for the next iteration results of previous iteration is added along with the new results. Like that each loop result is ended up with the results of previous iteration
My JSON SAMPLE.[can provide link, please let me know]
struct AssetItems {
var Name:String
var Desc:String
var Image:String
var entityId:String
var aFileType:String
var aFileSize:String
var aFileCreatedDate:String
var aFileRevisionDate:String
var aFileModifiedDate:String
var aProductName:String
var aVersion:String
var aAssetType:String
var aIsFolder:String
init(assetName:String,assetDescription:String,assetImage:String,entityId:String,FileType:String,FileSize:String,FileCretedDate:String,FileReveisionDate:String,FileModifiedDate:String,ProductName:String,Version:String,AssetType:String,IsFolder:String) {
self.Name = assetName
self.Desc = assetDescription
self.Image = assetImage
self.entityId = entityId
self.aFileType = FileType
self.aFileSize = FileSize
self.aFileCreatedDate = FileCretedDate
self.aFileRevisionDate = FileReveisionDate
self.aFileModifiedDate = FileModifiedDate
self.aProductName = ProductName
self.aVersion = Version
self.aAssetType = AssetType
self.aIsFolder = IsFolder
}
}
struct AssetModel {
var aName:String
var aDesc:String
var aCount:String
var aEntityId:String
var items:[AssetItems]
init(rackName:String,rackDescription:String,totalNoAssets:String,EntityId:String,rackItems:[AssetItems]) {
self.aName = rackName
self.aDesc = rackDescription
self.aCount = totalNoAssets
self.aEntityId = EntityId
self.items = rackItems
}
}
var assetData:Array<AssetModel> = Array<AssetModel>()
var assetContents:Array<AssetItems> = Array<AssetItems>()
THIS IS HOW I AM PARSING JSON
func get_data_from_Url(url:String) {
let url = NSURL(string: url)
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content_Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var responseError : NSError?
var response : NSURLResponse?
var urlData: NSData? = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &responseError)
if(urlData != nil) {
var responseData:NSString = NSString(data: urlData!, encoding: NSUTF8StringEncoding)!
let res = response as! NSHTTPURLResponse
var err:NSError?
if (res.statusCode == 200)
{
var parseError: NSError?
let json:AnyObject = NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers , error: &err) as AnyObject!
if (parseError == nil) // no error while parsing
{
if let Asset_list = json as? NSArray
{
for (var i = 0; i < Asset_list.count ; i++ )
{
if let Asset_obj = Asset_list[i] as? NSDictionary
{
if let AssetGroupName = Asset_obj["Name"] as? String
{
if let AssetGroupDescription = Asset_obj["Description"] as? String
{
if let entityId = Asset_obj["EntityId"] as? String
{
if let totalAssets = Asset_obj["_NoOfAssets"] as? String
{
if let items = Asset_obj["Items"] as? NSArray
{
for (var j=0 ; j < items.count; j++)
{
if let asset_items = items[j] as? NSDictionary
{
if let AbsolutePath = asset_items["AbsolutePath"] as? String
{
if let Description = asset_items["_Description"] as? String
{
if let Name = asset_items["_Name"] as? String {
if let entityId = asset_items["EntityId"] as? String
{
if let FileType = asset_items["_FileType"] as? String
{
if let FileSize = asset_items["_FileSize"] as? String
{
if let FileCreatedDate = asset_items["CreatedDate"] as? String
{
if let FileModifiedDate = asset_items["ModifiedDate"] as? String
{
if let RevisionDate = asset_items["RevisionDate"] as? String
{
if let productName = asset_items["ProductName"] as? String
{
if let version = asset_items["Version"] as? String
{
if let AssetType = asset_items["_AssetType"] as? String
{
if let isFolder = asset_items["IsFolder"] as? String
{
var add = AssetItems(assetName: Name, assetDescription: Description, assetImage: AbsolutePath, entityId: entityId, FileType: FileType, FileSize: FileSize, FileCretedDate: FileCreatedDate, FileReveisionDate: RevisionDate, FileModifiedDate: FileModifiedDate, ProductName: productName, Version: version, AssetType: AssetType, IsFolder: isFolder)
assetContents.append(add)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
var add_it = AssetModel(rackName: AssetGroupName, rackDescription: AssetGroupDescription, totalNoAssets: totalAssets, EntityId: entityId, rackItems: assetContents)
assetData.append(add_it)
}
}
}
}
}
}
}
}
}
}
}
do_table_refresh()
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(),
{
self.tableView.reloadData()
})
return
}
My Output
Please some one help me fix this, Any suggestion will also do.
THANKS
Special thanks to #SMi, With his GuidLines i was able to solve my issue.
I knew that i had to clear the inner array at the end of the loop[But,didn't knew how to clear that.]
var add_it = AssetModel(rackName: AssetGroupName, rackDescription: AssetGroupDescription, totalNoAssets: totalAssets, EntityId: entityId, rackItems: assetContents)
assetData.append(add_it)
assetContents.removeAll()
All i had to do is use the .removeAll() method to clear the assetContents Array.
I am using following Class to receive data from an external database:
import Foundation
protocol HomeModelProtocal: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, NSURLSessionDataDelegate {
//properties
weak var delegate: HomeModelProtocal!
var data : NSMutableData = NSMutableData()
var mi_movil: String = ""
let misDatos:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var urlPath: String = "http:...hidden here.."
let parametros = "?id="
func downloadItems() {
mi_movil = misDatos.stringForKey("ID_IPHONE")!
print ("mi_movil en HOMEMODEL:",mi_movil)
urlPath = urlPath + parametros + mi_movil
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
print ("LA URL ES: ",url)
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(var i = 0; i < jsonResult.count; i++)
{
jsonElement = jsonResult[i] as! NSDictionary
print (jsonElement)
let location = MiAutoModel()
//the following insures none of the JsonElement values are nil through optional binding
if let id_mis_autos = jsonElement["id_mis_autos"] as? String,
let modelo = jsonElement["modelo"] as? String,
let ano = jsonElement["ano"] as? String,
let id_movil = jsonElement["id_movil"] as? String
{
location.id_mis_autos = id_mis_autos
location.modelo = modelo
location.ano = ano
location.id_movil = id_movil
}
locations.addObject(location)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.itemsDownloaded(locations)
})
}
}
If there are received data, it works fine but if there are no data an exception is thrown:
Could not cast value of type '__NSArray0' (0x1a0dd2978) to 'NSMutableArray' (0x1a0dd3490)
What should I change to detect if there are no data to avoid the exception?
Since you don't seem to be modifying jsonResult anywhere, the obvious choice is to make it an NSArray instead of an NSMutableArray, and change the downcasting to match that.
I'm not sure why you're using NSDictionary and NSMutableArray but this is how I would do it:
for result in jsonResult {
guard let jsonElement = result as? [String:AnyObject] else { return }
let locations: [MiAutoModel] = []
let location = MiAutoModel()
//the following insures none of the JsonElement values are nil through optional binding
let id_mis_autos = jsonElement["id_mis_autos"] as? String ?? ""
let modelo = jsonElement["modelo"] as? String ?? ""
let ano = jsonElement["ano"] as? String ?? ""
let id_movil = jsonElement["id_movil"] as? String ?? ""
location.id_mis_autos = id_mis_autos
location.modelo = modelo
location.ano = ano
location.id_movil = id_movil
locations.append(location)
}
You might have to change some of the code depending on your situation.
My problem arises when I want to populate data from my mysql database into a class object. I am trying to return an array of objects and it returns nil and then it fills itself somehow. How can I make it fill before returning the blank array?
Here is my code and a screenshot of code output
import Foundation
class Research
{
var mainResearchImageURL:String = ""
var userProfileImageURL:String = ""
var caption:String = ""
var shortDescription:String = ""
init(mainResearchImageURL :String, userProfileImageURL:String, caption:String, shortDescription:String)
{
self.mainResearchImageURL = mainResearchImageURL
self.userProfileImageURL = userProfileImageURL
self.caption = caption
self.shortDescription = shortDescription
}
class func downloadAllResearches()->[Research]
{
var researches = [Research]()
let urlString = "http://localhost/test/index.php"
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
let postString = "action=listresearches"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error in
if (error == nil) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
//let dictionary = json!.firstObject as? NSDictionary
var counter:Int = 0;
for line in json!{
let researchData = line as! NSDictionary
let researchLineFromData = Research(mainResearchImageURL: researchData["research_mainImageURL"] as! String, userProfileImageURL: researchData["research_creatorProfileImageURL"] as! String, caption: researchData["research_caption"] as! String, shortDescription: researchData["research_shortDescription"] as! String)
researches.append(researchLineFromData) //researches bir dizi ve elemanları Research türünde bir sınıftan oluşuyor.
counter += 1
print ("counter value \(counter)")
print("array count in loop is = \(researches.count)")
}
}catch let error as NSError{
print(error)
}
} else {
print(error)
}})
task.resume()
print("array count in return is = \(researches.count)")
return researches
}
}
And this is the output:
add this on you completionHandler ( it works if you update a view)
dispatch_async(dispatch_get_main_queue(), {
if (error == nil) { ...... }
})
Advice 1:
return the task and use a completion param in your method,
you can cancel the task if it's too slow.
Advice 2 :
Use alamofire and swiftyJson framework
What happen here is that you are returning the value before finish (remember that the call is Asynchronous), you can make something like this:
class func downloadAllResearches(success:([Research])->Void,failure:(String)->Void)
{
var researches = [Research]()
let urlString = "http://localhost/test/index.php"
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
let postString = "action=listresearches"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error in
if (error == nil) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
//let dictionary = json!.firstObject as? NSDictionary
var counter:Int = 0;
for line in json!{
let researchData = line as! NSDictionary
let researchLineFromData = Research(mainResearchImageURL: researchData["research_mainImageURL"] as! String, userProfileImageURL: researchData["research_creatorProfileImageURL"] as! String, caption: researchData["research_caption"] as! String, shortDescription: researchData["research_shortDescription"] as! String)
researches.append(researchLineFromData) //researches bir dizi ve elemanları Research türünde bir sınıftan oluşuyor.
counter += 1
print ("counter value \(counter)")
print("array count in loop is = \(researches.count)")
}
success(researches)
}catch let error as NSError{
print(error)
failure("Can be extract from NSERROR")
}
} else {
print(error)
failure("Error - Can be extract for NSERROR")
}})
task.resume()
}
And for call this Fuction use something like this:
Research.downloadAllResearches({ (objects:[Research]) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Do whatever you like with the content
})
}) { (failureLiteral:String) -> Void in
}