I am trying to read an online JSON file using the sample found here. It seems quite straight forward except when I run it. It keeps failing at dataTaskWithURL call and the error.localizedDescription is "The operation couldn't be completed. (NSURLErrorDomain -1005.)" I have looked up the error code and it refers to NSURLErrorNetworkConnectionLost but I am connected to internet and when I try the same url in a browser, I receive the JSON result.
func searchItunes() {
let urlPath = "http://itunes.apple.com/search?term=JQ+Software&media=software"
let url: NSURL = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {
data, response, error in
if(error != nil) {
println(error.localizedDescription)
}
else {
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if(err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
else {
// process data here.
}
}
})
task.resume()
}
This should work, but note that your pasting above has an extra }) in the else block and an additional } below. With that I get the above to work successfully. If your brackets match up in your code then there may be some kind of intermediary proxy that is being used in your browser but not swift, or vice versa, that's causing the change to occur.
Note that the background thread will take a brief time to resolve, so you won't see anything if your program terminates before the thread finishes downloading in the background.
I was using the iOS Simulator and I realized that although my laptop is connected to the internet, the simulator is not because of the order of services in the System Preferences \ Network WiFi is not on the top. Apparently, the simulator does not try to find the first device which is actually connected! So I re-ordered the services and now the code works fine.
//this is swift program
struct Stack<Element>{
var brackets = [Element]()
mutating func push(bracket:Element){
brackets.append(bracket)
}
mutating func pop() -> Element{
return brackets.removeLast()
}
}
var stackOfCharacter = Stack<String>()
var arrayCharacter: [String] = ["[","(",")","]"]
for symbol in arrayCharacter{
if symbol == "{" {
stackOfCharacter.push(bracket: symbol)
}
else if symbol == "[" {
stackOfCharacter.push(bracket:symbol)
}
else if symbol == "(" {
stackOfCharacter.push(bracket:symbol)
}
else if symbol == "}"{
if stackOfCharacter.brackets.count != 0 {
var topItem = stackOfCharacter.brackets[stackOfCharacter.brackets.count - 1]
if topItem == "{" {
var element = stackOfCharacter.pop()
}
}
}
else if symbol == ")" {
if stackOfCharacter.brackets.count != 0 {
var topItem = stackOfCharacter.brackets[stackOfCharacter.brackets.count - 1]
if topItem == "(" {
var element = stackOfCharacter.pop()
}
}
}
else if symbol == "]" {
if stackOfCharacter.brackets.count != 0 {
var topItem = stackOfCharacter.brackets[stackOfCharacter.brackets.count - 1]
if topItem == "[" {
var element = stackOfCharacter.pop()
}
}
}
else {
stackOfCharacter.push(bracket:symbol)
}
}
var count = stackOfCharacter.brackets.isEmpty
if count {
print ("valid ")
}
else {
print ("Invalid")
}
Related
This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed last month.
I am retrieving data in JSON format from a URL using URLSession. After decoding the JSON data, I print it for debugging reasons and also populate an array of struct CVE. The array has data, while inside the function jsonDataRequest, but when I try to get its elements from the ViewController, the array is empty. I searched for quite sometime on how to resolve it, but I am a bit stuck. Below is my code:
Inside the class, I have the following code:
The structure
struct CVE : Decodable
{
var CVE : String
var severity : String
var cvss3_score : String? = nil
var public_date : String
var bugzilla_description : String
}
struct CVEdata : Decodable
{
var cves : [CVE]
}
The array I want to use from the ViewController
var arrCVE : [CVE] = []
The function I am calling from ViewController
func jsonDataRequest ()
{
let url = "https://access.redhat.com/hydra/rest/securitydata//cve.json?after=2022-12-26"
let urlObj = URL(string: url)
URLSession.shared.dataTask(with: urlObj!) { (data, response, error) in
do
{
// Json to Array
self.jsonCVE = try JSONDecoder().decode([CVE].self, from: data!)
var strCVE : String
var strSeverity : String
var strCvss3_score : String? = nil
var strPublic_date : String
var strBugzilla_description : String
print(self.jsonCVE)
for oCVE in self.jsonCVE
{
print(oCVE.CVE + " " + oCVE.severity + " " + oCVE.public_date + " " + oCVE.bugzilla_description)
// get each the CVE info
strCVE = oCVE.CVE
strSeverity = oCVE.severity
if (oCVE.cvss3_score != nil)
{
print(oCVE.cvss3_score!)
strCvss3_score = oCVE.cvss3_score
}
else
{
print("")
strCvss3_score = ""
}
strPublic_date = oCVE.public_date
strBugzilla_description = oCVE.bugzilla_description
// save it to the array
self.arrCVE.append(CVE(CVE: strCVE, severity: strSeverity, cvss3_score: strCvss3_score, public_date: strPublic_date, bugzilla_description: strBugzilla_description))
print(self.arrCVE)
}
// Logic after response has arrived
DispatchQueue.main.async
{
print("main.async")
}
} catch
{
print(error)
}
}.resume()
}
From the ViewController I instantiate an object from the class and access the array. I am displaying on a UITextView the array.count to see how many rows it contains
let oRedHatCVEs = RedHatCVEs()
oRedHatCVEs.jsonDataRequest()
txtvJSON.text = "Array elements: " + String(oRedHatCVEs.arrCVE.count)
and the result is Array elements: 0
Does it have to do with the asynchronous way the above code works? How can I finally get the array data back to my ViewController?
You are correct, it is to do with the asynchronous function jsonDataRequest.
There are many ways to deal (i.e wait) for asynchronous function to finish, before using the data.
This sample code shows one way using Swift async/await framework.
It also shortens the code to fetch the data.
class RedHatCVEs {
var arrCVE: [CVE] = []
func jsonDataRequest() async {
if let url = URL(string: "https://access.redhat.com/hydra/rest/securitydata/cve.json?after=2022-12-26") {
do {
let (data, _) = try await URLSession.shared.data(from: url)
arrCVE = try JSONDecoder().decode([CVE].self, from: data)
}
catch {
print(error)
}
}
}
}
Use it like this:
let oRedHatCVEs = RedHatCVEs()
Task {
await oRedHatCVEs.jsonDataRequest()
txtvJSON.text = "Array elements: " + String(oRedHatCVEs.arrCVE.count)
}
I am parsing a certain json url data to plot in a map and I need to detect that I have all the data to show a spinner while nothing is happening. I have created a variable that goes from false to true after I have all the data but that variable only exists as true inside the for loop
This is part of the code that gets the data
import SwiftUI
import MapKit
var locationsFillTest : Int = 0
var allLocations = [MKPointAnnotation]()
var doneGettingData : Bool = false
struct MapView: UIViewRepresentable {
var startdate : String
func makeUIView(context: Context) -> MKMapView{
MKMapView(frame: .zero)
}
func makeCoordinator() -> MapViewCoordinator{
MapViewCoordinator(self)
}
func updateUIView(_ uiView: MKMapView, context: Context){
uiView.removeAnnotations(allLocations)
allLocations = []
doneGettingData = false
print("Done = \(doneGettingData)")
let url = URL(string: "https://XXXXXX")!
URLSession.shared.dataTask(with: url) {(data,response,error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode(emsc.self, from: d)
DispatchQueue.main.async {
locationsFillTest = allLocations.count
doneGettingData = false
for locations in decodedLists.features {
let lat = Double(locations.properties.lat)
let long = Double(locations.properties.lon)
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: lat , longitude: long )
if locationsFillTest == 0 {
allLocations.append(annotation)}
}
uiView.addAnnotations(allLocations)
uiView.delegate = context.coordinator
uiView.showAnnotations(allLocations, animated: true)
doneGettingData = true
print("Done = \(doneGettingData)")
}
}else {
print("No Data")
}
} catch {
print("Error decoding JSON: ", error, response!)
}
}.resume()
}
}
The variable doneGettingData becomes false and true by watching the print but if I need to use it for example to create a spinner its false all the time since its only true inside.
How can I make it global ?
Thank you
Unless you have another declaration for doneGettingData inside the closure the instance level property is getting set to true. It may be getting set later than you expect though. Try the following to see when it changes (and to get you setup to react to those changes):
var doneGettingData : Bool = false {
didSet {
if doneGettingData {
print("The instance property doneGettingData is now true.")
} else {
print("The instance property doneGettingData is now false.")
}
}
}
You may want to make this into a custom enum though with cases along the lines of fetching, done, noData, and jsonError. Right now if there is no data you will never have a trigger to either retry, move on, notify the user, etc. The same applies when there is a decoding error. Or at the very least set the flag to true at the very end of the loop so something happens no matter what.
Something like:
enum DataCollectionState {
case fetching, done, noData, jsonError
var doneGettingData : DataCollectionState = fetching {
didSet {
switch doneGettingData {
case fetching:
// Show a spinner or something
case done:
// Hide the spinner
case noData:
// Tell the user there was no data? Try again?
case jsonError:
// Tell the user there was an error? Try again?
}
}
}
Note: I don't have Xcode open right now so syntax may not be exact.
My code checks if code: "this data" is not empty, how can I check also if code itself exists. Some responses might give me almost an empty JSON with just time stamp. So the var code won't be there.
Or is there a better way altogether to do this? as My JSON is
"Variable Based on text input" which leads to "code" which might not be there which will have "some info" or ""
Top of script:
struct Variables: Decodable {
var code: String
}
typealias DecoderX = [String: Variables]
Previous function sets inputs from user text which are cross checked with the database so GetInfo will only be called if UserDefault inputs are set.
func GetInfo() {
let Input1 = UserDefaults.standard.string(forKey: “UserInput1”) ?? “”
let Input2 = UserDefaults.standard.string(forKey: “UserInput2”) ?? “”
let Input3 = UserDefaults.standard.string(forKey: “UserInput3”) ?? “”
print(“Input Check 1: \(Input1) \(Input2) \(Input3)”)
// URL INFO With API key hidden
let jsonTask = URLSession.shared.dataTask(with: requestURL) { data, response, error in
if response == response {
if let data = data, let body = String(data: data, encoding: .utf8) {
do {
let json = try? decoder.decode(DeocderX.self, from: data);
DispatchQueue.main.async {
print(“Input Check 2: \(json![Input1]!.code) \(json![Input2]!.code) \(json![Input3]!.code)”)
if json?[Input1]?.code != nil {
print("Good Information 1")
} else {
print("Found Nothing 1")
}
if json?[Input2]?.code != nil {
print("Good Information 2")
} else {
print("Found Nothing 2")
}
if json?[Input3]?.code != nil {
print("Good Information 3")
} else {
print("Found Nothing 3")
}
}
// rest of code not applicable
You can use SwiftyJSON library. You can find it at this link
if let json = try? JSON(data: response.data!){
if json["yourKey"]["otherKey"].exists() {
print("exists")
}
}
Hope it helps...
I'm getting an odd error message saying "Extra argument 'endocing' in call", but it's in the method, so it's not an extra argument? Why is this happening and how can I resolve this? The error message appears when declaring the variable "parser" as you can see. Thanks!
if let checkedUrl = NSURL(string:"http://www.mobladet.se") {
if let htmlString = String(contentsOfURL: checkedUrl, encoding: NSUTF8StringEncoding, error: nil) {
// Parsing HTML
let opt = CInt(HTML_PARSE_NOERROR.value | HTML_PARSE_RECOVER.value)
var err : NSError?
var parser = HTMLParser(html: htmlString, encoding: NSUTF8StringEncoding, option: opt, error: &err)
var bodyNode = parser.body
// Create an array of the part of HTML you need
if let inputNodes = bodyNode?.findChildTags("h4") {
for node in inputNodes {
let result = html2String(node.rawContents)
println(result)
}
}
} else {
println("Could not load HTML Content")
}
}
html should be HTML code to be parsed not a NSURL. You need to use String( contentsOfURL:) to extract its contents and them parse it
if let checkedUrl = NSURL(string:"http://stackoverflow.com/questions/28751228/a-swift-wrapper-around-libxml-for-parsing-html"){
if let htmlString = String(contentsOfURL: checkedUrl, encoding: NSUTF8StringEncoding, error: nil) {
println(htmlString)
} else {
println("could not load html string from the url")
}
} else {
println("invalid url")
}
I am trying to parse JSON with the following code:
func ltchandler(response: NSURLResponse!, data : NSData!, error : NSError!) { //Is passed the results of a NSURLRequest
if ((error) != nil) {
//Error Handling Stuff
} else {
if (NSString(data:data, encoding:NSUTF8StringEncoding) == "") {
//Error Handling Stuff
} else {
var data = NSData(data: data);
// Define JSON string
var JSONString = "\(data)"
// Get NSData using string
if let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
// Parse JSONData into JSON object
var parsingError: NSError?
if let JSONObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parsingError) as? [String: AnyObject] {
// If the parsing was successful grab the rate object
var rateObject: Double! = JSONObject["price"]?.doubleValue
// Make sure the rate object is the expected type
if let rate = rateObject as? Double! { // THIS IS NOT WORKING!!!
//Do stuff with data
} else {
println("Parsing Issue")
}
}
}
}
}
}
The line marked THIS IS NOT WORKING!!! is not being called.
From what I can tell, it cannot cast rateObject as a double - why not? It is not showing any errors.
To clarify, the expected behavior is that a double is created from the JSON object.
To strictly answer your question have you tried printing the rateObject. Also why are you casting to Double! rather than just Double in the problematic line?
Personally I don't use ! in almost all cases. You are better off using either non-optionals or proper optionals.
In the relevent section I would write:
// Make sure the rate object is the expected type
if let rate = JSONObject["price"]?.doubleValue {
//Do stuff with rate
} else {
print("Parsing Issue")
}
Of course if the JSONObject["price"] is not something with a doubleValue method or the method returns nil you will end up with nil and the else case being taken.
the code worked for me, try this code:
// if the value equals nil or any String, the instruction abort the if
// SWIFT 2.0 in xcode beta 5
if let rate = Double((JSONObject["price"] as? String)!){
// insert you code here
} else {
print("error message")
}