I'm trying to get the JSON from a website and parse it before printing it.
I've got a class called "JSONImport", which should handle a JSON Import from a server and print it.
A second class should make the call to start the Import and print the content of the JSON.
The following code is what I have so far (I took it from another question Downloading and parsing json in swift)
So this is my "JSONImport.swift":
var data = NSMutableData();
func startConnection(){
let urlPath: String = "http://echo.jsontest.com/key/value";
let url: NSURL = NSURL(string: urlPath)!;
let request: NSURLRequest = NSURLRequest(URL: url);
let 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!) {
// throwing an error on the line below (can't figure out where the error message is)
do{
let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonResult);}
catch {
print("Something went wrong?");
}
}
Inside another class, I would like to print the JSON by:
JSONImport.startConnection();
Now I get an error, because swift wants me to add a parameter to the call, making it look like:
JSONImport.startConnection(<#T##JSONImport#>);
Does someone have an idea, what I should put in there as a parameter?
I am confused, as i didn't declare one.
Thank you all in advance!
Kind regards, Manuel
startConnection() is a instance method, so you need to instantiate an JSONImport to call it.
Only type methods can be used that way. So it's essentially asking for an instance of JSONImport.
Here is how you should do it
let importer = JSONImport()
importer.startConnection()
or by calling from the type method
let importer = JSONImport()
JSONImport.startConnection(importer)
You can read more about instance/type methods at this guide
Related
I have an issue understanding the process in consuming REST api services with SWIFT, seems like i'm missing something simple, but yet important here.
this is the singleton DataManager class, I'm using to consume API with loadNews() method, as you can see it's simple, request method, getter and initializer that will load the data.
for loadNews() I use Alamofire to handle request, and SwiftyJSON to parse the response.
class DataManager{
static let shared = DataManager()
private var data:JSON = JSON()
private init(){
print("testprint1 \(self.data.count)")
loadNews() { response in
self.data = response
print("initprint \(self.data.count)")
print(self.data["response"]["results"].count)
print(self.data["response"]["results"][0]["id"].stringValue)
}
print("testprint2 \(self.data.count)")
}
func getNews() -> JSON {
return data
}
func loadNews(completion: #escaping (JSON) -> ()){
Alamofire.request("...")
.responseJSON{ response in
guard response.result.isSuccess,
let value = response.result.value else {
print("Error: \(String(describing: response.result.error))")
completion([])
return
}
let json = JSON(value)
completion(json)
}
}
}
issue that i'm facing is when i try to call the DataManager() instance in my ViewController, I'm not able to read data in the controller for some reason, here is the controller code (relevant one):
class SecondViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let data1 = DataManager.shared.getNews()
print("qa \(data1.count)")
}
...
}
now what bothers me is - logic behind this should be simple, let data1 = DataManager.shared.getNews() - if i'm not wrong will (should) execute the following flow:
init()->loadNews()->getNews()
initialize method will call loadNews, loadNews will fetch data from API, fill the data array, and getNews is supposed to return the filled data array, but that flow doesn't seem correct then
console output
console output text
testprint1 0
testprint2 0
qa 0
initprint 1
50
commentisfree/2019/dec/07/lost-my-faith-in-tech-evangelism-john-naughton
so it seems like both prints within init() get executed before loadNews() method that is between them, as well as "qa0" print that is printing the size of the array in the ViewController.
now my question is, does anyone see a mistake here, is this happening because of long network query, or am I just missing something, because it seems to me that data is properly loaded and parsed, which is seen in last 2 lines of output, but i can't get it where i need it, like it dissapears. is my logic here wrong? if someone could help I would really appreciate it.
The Alamofire process works asynchronously, but you don't consider it, that's the mistake.
Change the code to
class DataManager{
static let shared = DataManager()
func loadNews(completion: #escaping (JSON) -> ()){
Alamofire.request("...")
.responseJSON{ response in
guard response.result.isSuccess,
let value = response.result.value else {
print("Error:", response.result.error)
completion([])
return
}
let json = JSON(value)
completion(json)
}
}
}
class SecondViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
DataManager.shared.loadNews() { response in
print("initprint \(response.count)")
print(response["response"]["results"].count)
print(response["response"]["results"][0]["id"].stringValue)
}
}
...
}
You have to get the data from the completion handler in SecondViewController
I'm working on Ios and Swift 3 and I want to parse HTML Code.
Until now Hpple did the trick for me using this code:
func parse_Html_Text(url:String)
{
let data = NSData(contentsOf: URL(string: url)!)
let doc = TFHpple(htmlData: data! as Data!)
}
But when I'm trying to get html code from a query url (like this one: link), then my app crashes and I get Thread1: EXC_BAD_INSTRUCTION error in this line:
let doc = TFHpple(htmlData: data! as Data!)
I also tried Alamofire's request method but I didnt manage to make it work
I'm stucked two days with this so any help will be appreciated.
Thank you in advance!
I noticed that you use Swift 3. Probably the library expects an NSData object but I would suggest to write your function like the following:
func parse_Html_Text(url: String) {
if let safeUrl = URL(string: url) {
let data = try? Data(contentsOf: safeUrl)
let doc = TFHpple(htmlData: data as? NSData)
}
}
In this case keep in mind that we are not handling possible error from the data setter.
I am trying to learn Swift. One of my projects is to try to retrieve JSON data from an internal web service (a group of Python CGI scripts) and convert it into a Swift object. I can do this easily in Python, but I am having trouble doing this in Swift. Here is my playground code:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let endpoint: String = "http://pathToCgiScript/cgiScript.py"
let url = NSURL(string: endpoint)
let urlrequest = NSMutableURLRequest(URL: url!)
let headers: NSDictionary = ["User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"]
urlrequest.allHTTPHeaderFields = headers as? [String : String]
urlrequest.HTTPMethod = "POST"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlrequest) {
(data, response, error) in
guard data != nil else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("Error calling script!")
print(error)
return
}
do {
guard let received = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as?
[String: AnyObject] else {
print("Could not get JSON from stream")
return
}
print(received)
} catch {
print("error parsing response from POST")
}
}
task.resume()
I know making a 'POST' to retrieve data may look odd, but that is how the system is set up. I keep on getting:
Could not get data from JSON
I checked the response, and the status is 200. I then checked the data's description with:
print(data?.description)
I got an unexpected result. Here is a snippet:
Optional("<0d0a5b7b 22535441 54555322 3a202244 6f6e6522 2c202242 55535922...
I used Mirror, and apparently the type is NSData. Not sure what to make of this. I have tried to encode the data with base64EncodedDataWithOptions. I have tried different NSJSONReadingOptions as well to no avail. Any ideas?
Update:
I used Wireshark to double check the code in the Playground. Not only was the call made correctly, but the data being sent back is correct as well. In fact, Wireshark sees the data as JSON. The issue is trying to turn the JSON data into a Swift object.
I figured out what was wrong. I was casting to the wrong type. This is the new code:
guard let received = try! NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as? [AnyObject]
The JSON was not returning an array of dictionaries but an array of objects.
I've searched for how to do this but something just isn't making sense for me and I can't do it. All I need to do is get data out of my json session (if thats what you call it). I started programming about 3 weeks ago so I need lay mans terms please. I realize this is probably going to get marked as a duplicate but most of the answers on this / related topics are for other languages and I barely understand swift so they don't help me much.
I've spent hours trying to find the answer and since I'm new I don't know if what I'm searching for is even the right thing to be searching for. I've also tried reading the iOS developer library but either I don't understand what it's telling me or I haven't found the right section because I still can't figure this out. Please try to explain this instead of sending me to read other resources.
here is my function
func parseData() {
let urlString = "http://heroesjson.com/heroes.json"
let session = NSURLSession.sharedSession()
let url = NSURL(string: urlString)!
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
guard let responseData = data else { return }
var json: [[String: AnyObject]]!
do {
json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! [[String: AnyObject]]
}
catch {
//handle error
}
var arrayToReturn = [Hero]()
for element in json {
let hero = Hero(fromDictionary: element as! [String: AnyObject])
arrayToReturn.append(hero)
}
}.resume()//Closes Session.dataTaskWithURL
} //Closes parseData()
the goal is to get the json variable in my do statement so I can parse it outside of the function or get my "arrayToReturn" so I can save it to a global variable that I use.
If I understand correctly I can't just assign the value (arrayToReturn) to my global variable (heroes) because this is an asynchronous request so it just returns nil because the command is called before the request is finished. I think I have to use a completion handler or callback function. I don't really understand the difference between them and don't understand how or where to implement them.
Also, I don't understand this code very much either, I just know it's necessary to get what I want.
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
"(data: NSData?, response:NSURLResponse?, error: NSError?)" looks like parameters but they don't seem to be attached to a function so that doesn't make sense to me
"-> Void" Doesn't make sense to me because -> means return whatever follows, but void indicates to me that its returning nothing, so why not just leave it out all together?
"-> Void in" What what is the significance of in here? what does it mean / signal?
Go and read about Swift Closures. To use a value outside of it, you'd need to pass it to another closure.
func parseData(callback: (heroes: [Hero]) -> Void) {
let urlString = "http://heroesjson.com/heroes.json"
let session = NSURLSession.sharedSession()
let url = NSURL(string: urlString)!
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
guard let responseData = data else { return }
var json: [[String: AnyObject]]!
do {
json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! [[String: AnyObject]]
}
catch {
//handle error
}
var arrayToReturn = [Hero]()
for element in json {
let hero = Hero(fromDictionary: element as! [String: AnyObject])
arrayToReturn.append(hero)
}
callback(heroes: arrayToReturn)
}.resume()//Closes Session.dataTaskWithURL
} //Closes parseData()
And you'd call it:
parseData { heroes in
// do something with the array
}
I'm trying to send POST request to REST webservice using alamofire
I'm passing json object as POST body, and i'm getting the response and everything works fine till now
Alamofire.request(.POST, path, parameters: createQueryParams(), encoding: .JSON)
.responseArray { (request, response, myWrapper, error) in
if let anError = error
{
completionHandler(nil, error)
println("Error in handling request or response!")
return
}
completionHandler(myWrapper, nil)
}
private class func createQueryParams() -> [String:AnyObject]{
var parameters:[String:AnyObject] = [String:AnyObject]()
parameters["lat"] = lLat!
parameters["lng"] = lLon!
if category != nil { // here is the problem
parameters["category"] = category!
}
return parameters
}
I have a category filter, if there is a value in category variable, i want to send it as QueryParam (should encoding be .URL? but how can i send json object ??)
this code does not work
if category != nil {
parameters["category"] = category!
}
How can i do this? Hope I can explain it clearly
Thanks in advance
You could solve it this way:
let mutableUrlRequest = NSMutableUrlRequest(URL: URL.URLByAppendingPathComponent(path)
mutableUrlRequest.HTTPMethod = .POST
let request = Alamofire.ParameterEncoding.URL.encode(mutableUrlRequest, parameters: createQueryParameters()).0
Alamofire.request(request)
However, I would advise you to look into the Router declaration of Alamofire and try this one. With it you can create dynamic requests and all of them are declared in a single file.
Edit:
Oh wait you can forget the previous edit the solution is quite simple and you also answered it by yourself. Yes you just have to change the encoding to .URL, you still are able to send json objects, because Alamofire itself decodes then the json object to a string for queryparams.
Alamofire.request(.POST, path, parameters:createQueryParams(), encoding: .URL).responseArray...
Edit 2:
Since the first edit did not work, try this:
let url = NSURL(string: path)!
let urlRequest = NSURLReqeust(URL: url)
request.HTTPMethod = "Post"
let encoding = Alamofire.ParameterEncoding.URL
let request = encoding.encode(urlRequest, createQueryParams())
Alamofire.request(request)